diff --git a/cmd/data-service/service/global-config/global_config.go b/cmd/data-service/service/global-config/global_config.go new file mode 100644 index 0000000000..01e718b69c --- /dev/null +++ b/cmd/data-service/service/global-config/global_config.go @@ -0,0 +1,203 @@ +/* + * TencentBlueKing is pleased to support the open source community by making + * 蓝鲸智云 - 混合云管理平台 (BlueKing - Hybrid Cloud Management System) available. + * Copyright (C) 2022 THL A29 Limited, + * a Tencent company. All rights reserved. + * Licensed under the MIT License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://opensource.org/licenses/MIT + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * We undertake not to change the open source license (MIT license) applicable + * + * to the current version of the project delivered to anyone in the future. + */ + +// Package globalconfig global config service +package globalconfig + +import ( + "fmt" + + "hcm/pkg/api/core" + datagconf "hcm/pkg/api/data-service/global_config" + "hcm/pkg/criteria/errf" + "hcm/pkg/dal/dao/orm" + "hcm/pkg/dal/dao/tools" + "hcm/pkg/dal/dao/types" + tablegconf "hcm/pkg/dal/table/global-config" + dtypes "hcm/pkg/dal/table/types" + "hcm/pkg/logs" + "hcm/pkg/rest" + "hcm/pkg/tools/util" + + "github.com/jmoiron/sqlx" +) + +// BatchCreateGlobalConfigs creates the global config. +func (svc *service) BatchCreateGlobalConfigs(cts *rest.Contexts) (interface{}, error) { + req := new(datagconf.BatchCreateReq) + if err := cts.DecodeInto(req); err != nil { + logs.Errorf("create global config decode request failed, err: %v, rid: %s", err, cts.Kit.Rid) + return nil, errf.NewFromErr(errf.InvalidParameter, err) + } + + if err := req.Validate(); err != nil { + logs.Errorf("create global config validate request failed, err: %v, rid: %s", err, cts.Kit.Rid) + return nil, errf.NewFromErr(errf.InvalidParameter, err) + } + + // create + ids, err := svc.dao.Txn().AutoTxn(cts.Kit, func(txn *sqlx.Tx, opt *orm.TxnOption) (interface{}, error) { + globalConfigs := make([]tablegconf.GlobalConfigTable, len(req.Configs)) + for index, config := range req.Configs { + globalConfigs[index] = tablegconf.GlobalConfigTable{ + ConfigKey: config.ConfigKey, + ConfigValue: dtypes.JsonField(util.GetStrByInterface(config.ConfigValue)), + ConfigType: config.ConfigType, + Memo: config.Memo, + Creator: cts.Kit.User, + Reviser: cts.Kit.User, + } + } + + ids, err := svc.dao.GlobalConfig().CreateWithTx(cts.Kit, txn, globalConfigs) + if err != nil { + logs.Errorf("create global config failed, err: %v, rid: %s", err, cts.Kit.Rid) + return nil, err + } + + return ids, nil + }) + if err != nil { + logs.Errorf("create global config failed, err: %v, rid: %s", err, cts.Kit.Rid) + return nil, errf.NewFromErr(errf.Aborted, err) + } + + return ids, nil +} + +// ListGlobalConfigs ... +func (svc *service) ListGlobalConfigs(cts *rest.Contexts) (interface{}, error) { + req := new(datagconf.ListReq) + if err := cts.DecodeInto(req); err != nil { + logs.Errorf("list global config decode request failed, err: %v, rid: %s", err, cts.Kit.Rid) + return nil, errf.NewFromErr(errf.InvalidParameter, err) + } + + if err := req.Validate(); err != nil { + logs.Errorf("list global config validate request failed, err: %v, rid: %s", err, cts.Kit.Rid) + return nil, errf.NewFromErr(errf.InvalidParameter, err) + } + + listOpt := &types.ListOption{ + Fields: req.Fields, + Filter: req.Filter, + Page: req.Page, + } + result, err := svc.dao.GlobalConfig().List(cts.Kit, listOpt) + if err != nil { + logs.Errorf("list global config failed, err: %v, rid: %s", err, cts.Kit.Rid) + return nil, errf.NewFromErr(errf.Aborted, err) + } + + resp := &datagconf.ListResp{ + Count: result.Count, + Details: result.Details, + } + + return resp, nil +} + +// BatchUpdateGlobalConfigs ... +func (svc *service) BatchUpdateGlobalConfigs(cts *rest.Contexts) (interface{}, error) { + req := new(datagconf.BatchUpdateReq) + if err := cts.DecodeInto(req); err != nil { + logs.Errorf("batch update global config decode request failed, err: %v, rid: %s", err, cts.Kit.Rid) + return nil, errf.NewFromErr(errf.InvalidParameter, err) + } + + if err := req.Validate(); err != nil { + logs.Errorf("batch update global config validate request failed, err: %v, rid: %s", err, cts.Kit.Rid) + return nil, errf.NewFromErr(errf.InvalidParameter, err) + } + + _, err := svc.dao.Txn().AutoTxn(cts.Kit, func(txn *sqlx.Tx, opt *orm.TxnOption) (interface{}, error) { + for _, config := range req.Configs { + record := &tablegconf.GlobalConfigTable{ + Reviser: cts.Kit.User, + } + + if config.ConfigValue != nil { + record.ConfigValue = dtypes.JsonField(util.GetStrByInterface(config.ConfigValue)) + } + + if config.Memo != nil { + record.Memo = config.Memo + } + + if err := svc.dao.GlobalConfig().UpdateWithTx(cts.Kit, txn, + tools.EqualExpression("id", config.ID), record); err != nil { + return nil, err + } + } + return nil, nil + }) + if err != nil { + logs.Errorf("batch update global config failed, err: %v, rid: %s", err, cts.Kit.Rid) + return nil, errf.NewFromErr(errf.Aborted, err) + } + + return nil, nil +} + +// BatchDeleteGlobalConfigs ... +func (svc *service) BatchDeleteGlobalConfigs(cts *rest.Contexts) (interface{}, error) { + req := new(datagconf.BatchDeleteReq) + if err := cts.DecodeInto(req); err != nil { + logs.Errorf("batch delete global config decode request failed, err: %v, rid: %s", err, cts.Kit.Rid) + return nil, errf.NewFromErr(errf.InvalidParameter, err) + } + + if err := req.Validate(); err != nil { + logs.Errorf("batch delete global config validate request failed, err: %v, rid: %s", err, cts.Kit.Rid) + return nil, errf.NewFromErr(errf.InvalidParameter, err) + } + + opt := &types.ListOption{ + Filter: tools.ContainersExpression("id", req.IDs), + Page: core.NewDefaultBasePage(), + } + listResp, err := svc.dao.GlobalConfig().List(cts.Kit, opt) + if err != nil { + logs.Errorf("delete list global config failed, err: %v, rid: %s", err, cts.Kit.Rid) + return nil, fmt.Errorf("delete list global config failed, err: %v", err) + } + + if len(listResp.Details) == 0 { + return nil, nil + } + + delIDs := make([]string, len(listResp.Details)) + for index, one := range listResp.Details { + delIDs[index] = one.ID + } + + _, err = svc.dao.Txn().AutoTxn(cts.Kit, func(txn *sqlx.Tx, opt *orm.TxnOption) (interface{}, error) { + delFilter := tools.ContainersExpression("id", delIDs) + if err = svc.dao.GlobalConfig().DeleteWithTx(cts.Kit, txn, delFilter); err != nil { + return nil, err + } + return nil, nil + }) + if err != nil { + logs.Errorf("delete global config failed, err: %v, rid: %s", err, cts.Kit.Rid) + return nil, err + } + + return nil, nil +} diff --git a/cmd/data-service/service/global-config/service.go b/cmd/data-service/service/global-config/service.go new file mode 100644 index 0000000000..ae6a7934e9 --- /dev/null +++ b/cmd/data-service/service/global-config/service.go @@ -0,0 +1,50 @@ +/* + * TencentBlueKing is pleased to support the open source community by making + * 蓝鲸智云 - 混合云管理平台 (BlueKing - Hybrid Cloud Management System) available. + * Copyright (C) 2022 THL A29 Limited, + * a Tencent company. All rights reserved. + * Licensed under the MIT License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://opensource.org/licenses/MIT + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * We undertake not to change the open source license (MIT license) applicable + * + * to the current version of the project delivered to anyone in the future. + */ + +// Package globalconfig global config service +package globalconfig + +import ( + "net/http" + + "hcm/cmd/data-service/service/capability" + "hcm/pkg/dal/dao" + "hcm/pkg/rest" +) + +// InitService initial the service +func InitService(cap *capability.Capability) { + svc := &service{ + dao: cap.Dao, + } + + h := rest.NewHandler() + + // common api + h.Add("ListGlobalConfigs", http.MethodPost, "/global_configs/list", svc.ListGlobalConfigs) + h.Add("BatchCreateGlobalConfigs", http.MethodPost, "/global_configs/batch/create", svc.BatchCreateGlobalConfigs) + h.Add("BatchUpdateGlobalConfigs", http.MethodPatch, "/global_configs/batch", svc.BatchUpdateGlobalConfigs) + h.Add("BatchDeleteGlobalConfigs", http.MethodDelete, "/global_configs/batch", svc.BatchDeleteGlobalConfigs) + + h.Load(cap.WebService) +} + +type service struct { + dao dao.Set +} diff --git a/cmd/data-service/service/service.go b/cmd/data-service/service/service.go index ab536f4971..b5a70444cf 100644 --- a/cmd/data-service/service/service.go +++ b/cmd/data-service/service/service.go @@ -72,6 +72,7 @@ import ( sync "hcm/cmd/data-service/service/cloud/sync" "hcm/cmd/data-service/service/cloud/zone" "hcm/cmd/data-service/service/cos" + globalconfig "hcm/cmd/data-service/service/global-config" recyclerecord "hcm/cmd/data-service/service/recycle-record" "hcm/cmd/data-service/service/task" "hcm/cmd/data-service/service/user" @@ -270,6 +271,7 @@ func (s *Service) apiSet() *restful.Container { billexchangerate.InitService(capability) billsyncrecord.InitService(capability) + globalconfig.InitService(capability) task.InitService(capability) diff --git a/pkg/api/data-service/global_config/global_config.go b/pkg/api/data-service/global_config/global_config.go new file mode 100644 index 0000000000..95fcfd2d63 --- /dev/null +++ b/pkg/api/data-service/global_config/global_config.go @@ -0,0 +1,131 @@ +/* + * TencentBlueKing is pleased to support the open source community by making + * 蓝鲸智云 - 混合云管理平台 (BlueKing - Hybrid Cloud Management System) available. + * Copyright (C) 2022 THL A29 Limited, + * a Tencent company. All rights reserved. + * Licensed under the MIT License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://opensource.org/licenses/MIT + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * We undertake not to change the open source license (MIT license) applicable + * + * to the current version of the project delivered to anyone in the future. + */ + +// Package datagconf global config data service +package datagconf + +import ( + "fmt" + + "hcm/pkg/api/core" + "hcm/pkg/criteria/validator" + tablegconf "hcm/pkg/dal/table/global-config" +) + +// ListReq ... +type ListReq struct { + core.ListReq `json:",inline"` +} + +// Validate ListReq +func (req *ListReq) Validate() error { + if err := validator.Validate.Struct(req); err != nil { + return err + } + + if err := req.ListReq.Validate(); err != nil { + return err + } + + return nil +} + +// ListResp ... +type ListResp core.ListResultT[tablegconf.GlobalConfigTable] + +// BatchCreateReq ... +type BatchCreateReq struct { + Configs []GlobalConfig `json:"configs" validate:"required,min=1"` +} + +// Validate BatchCreateReq +func (req *BatchCreateReq) Validate() error { + if err := validator.Validate.Struct(req); err != nil { + return err + } + + return nil +} + +// BatchUpdateReq ... +type BatchUpdateReq struct { + Configs []GlobalConfig `json:"configs" validate:"required,min=1"` +} + +// Validate BatchUpdateReq +func (req *BatchUpdateReq) Validate() error { + if err := validator.Validate.Struct(req); err != nil { + return err + } + + for _, config := range req.Configs { + if len(config.ID) == 0 { + return fmt.Errorf("config id can not be empty") + } + } + + return nil +} + +// BatchDeleteReq ... +type BatchDeleteReq struct { + core.BatchDeleteReq `json:",inline"` +} + +// Validate BatchCreateReq +func (req *BatchDeleteReq) Validate() error { + if err := validator.Validate.Struct(req); err != nil { + return err + } + + if err := req.BatchDeleteReq.Validate(); err != nil { + return err + } + + return nil +} + +// FindReq ... +type FindReq struct { + Key string `json:"key" validate:"required"` + Type string `json:"type" validate:"required"` +} + +// Validate FindReq +func (req *FindReq) Validate() error { + if err := validator.Validate.Struct(req); err != nil { + return err + } + + return nil +} + +// GlobalConfig define cvm table. +type GlobalConfig struct { + // ID global config id + ID string `json:"id"` + // ConfigKey global config key, key+type is unique + ConfigKey string `json:"config_key"` + // ConfigValue global config value, json format + ConfigValue interface{} `json:"config_value"` + // ConfigType global config type + ConfigType string `json:"config_type"` + // Memo global config memo + Memo *string `json:"memo"` +} diff --git a/pkg/client/data-service/global/client.go b/pkg/client/data-service/global/client.go index 8855f3a37d..b63af4919a 100644 --- a/pkg/client/data-service/global/client.go +++ b/pkg/client/data-service/global/client.go @@ -64,6 +64,8 @@ type Client struct { TaskDetail *TaskDetailClient TaskManagement *TaskManagementClient + + GlobalConfig *GlobalConfigsClient } type restClient struct { @@ -110,5 +112,6 @@ func NewClient(client rest.ClientInterface) *Client { TaskDetail: NewTaskDetailClient(client), TaskManagement: NewTaskManagementClient(client), + GlobalConfig: NewGlobalConfigClient(client), } } diff --git a/pkg/client/data-service/global/global_config.go b/pkg/client/data-service/global/global_config.go new file mode 100644 index 0000000000..bde60dd141 --- /dev/null +++ b/pkg/client/data-service/global/global_config.go @@ -0,0 +1,68 @@ +/* + * TencentBlueKing is pleased to support the open source community by making + * 蓝鲸智云 - 混合云管理平台 (BlueKing - Hybrid Cloud Management System) available. + * Copyright (C) 2024 THL A29 Limited, + * a Tencent company. All rights reserved. + * Licensed under the MIT License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://opensource.org/licenses/MIT + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * We undertake not to change the open source license (MIT license) applicable + * + * to the current version of the project delivered to anyone in the future. + */ + +package global + +import ( + "hcm/pkg/api/core" + datagconf "hcm/pkg/api/data-service/global_config" + "hcm/pkg/client/common" + "hcm/pkg/kit" + "hcm/pkg/rest" +) + +// GlobalConfigsClient is data service global config api client. +type GlobalConfigsClient struct { + client rest.ClientInterface +} + +// NewGlobalConfigClient create a new global config api client. +func NewGlobalConfigClient(client rest.ClientInterface) *GlobalConfigsClient { + return &GlobalConfigsClient{ + client: client, + } +} + +// List ... +func (g *GlobalConfigsClient) List(kt *kit.Kit, req *datagconf.ListReq) ( + *datagconf.ListResp, error) { + + return common.Request[datagconf.ListReq, datagconf.ListResp]( + g.client, rest.POST, kt, req, "/global_configs/list") +} + +// BatchCreate ... +func (g *GlobalConfigsClient) BatchCreate(kt *kit.Kit, req *datagconf.BatchCreateReq) ( + *core.BatchCreateResult, error) { + + return common.Request[datagconf.BatchCreateReq, core.BatchCreateResult]( + g.client, rest.POST, kt, req, "/global_configs/batch/create") +} + +// BatchUpdate ... +func (g *GlobalConfigsClient) BatchUpdate(kt *kit.Kit, req *datagconf.BatchUpdateReq) error { + return common.RequestNoResp[datagconf.BatchUpdateReq]( + g.client, rest.PATCH, kt, req, "/global_configs/batch") +} + +// BatchDelete ... +func (g *GlobalConfigsClient) BatchDelete(kt *kit.Kit, req *datagconf.BatchDeleteReq) error { + return common.RequestNoResp[datagconf.BatchDeleteReq]( + g.client, rest.DELETE, kt, req, "/global_configs/batch") +} diff --git a/pkg/dal/dao/dao.go b/pkg/dal/dao/dao.go index 9139c92801..1826b52d3e 100644 --- a/pkg/dal/dao/dao.go +++ b/pkg/dal/dao/dao.go @@ -56,6 +56,7 @@ import ( daosubaccount "hcm/pkg/dal/dao/cloud/sub-account" daosync "hcm/pkg/dal/dao/cloud/sync" "hcm/pkg/dal/dao/cloud/zone" + globalconfig "hcm/pkg/dal/dao/global-config" idgenerator "hcm/pkg/dal/dao/id-generator" "hcm/pkg/dal/dao/orm" recyclerecord "hcm/pkg/dal/dao/recycle-record" @@ -139,6 +140,8 @@ type Set interface { RootAccount() accountset.RootAccount TaskDetail() task.Detail TaskManagement() task.Management + GlobalConfig() globalconfig.Interface + Txn() *Txn } @@ -760,3 +763,11 @@ func (s *set) TaskDetail() task.Detail { func (s *set) TaskManagement() task.Management { return task.NewManagementDao(s.orm, s.idGen, s.audit) } + +// GlobalConfig return dao. +func (s *set) GlobalConfig() globalconfig.Interface { + return &globalconfig.Dao{ + Orm: s.orm, + IDGen: s.idGen, + } +} diff --git a/pkg/dal/dao/global-config/global-config.go b/pkg/dal/dao/global-config/global-config.go new file mode 100644 index 0000000000..16e30c1b30 --- /dev/null +++ b/pkg/dal/dao/global-config/global-config.go @@ -0,0 +1,200 @@ +/* + * TencentBlueKing is pleased to support the open source community by making + * 蓝鲸智云 - 混合云管理平台 (BlueKing - Hybrid Cloud Management System) available. + * Copyright (C) 2022 THL A29 Limited, + * a Tencent company. All rights reserved. + * Licensed under the MIT License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://opensource.org/licenses/MIT + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * We undertake not to change the open source license (MIT license) applicable + * + * to the current version of the project delivered to anyone in the future. + */ + +// Package daogconf global config dao. +package daogconf + +import ( + "fmt" + + "hcm/pkg/api/core" + "hcm/pkg/criteria/errf" + idgenerator "hcm/pkg/dal/dao/id-generator" + "hcm/pkg/dal/dao/orm" + "hcm/pkg/dal/dao/tools" + "hcm/pkg/dal/dao/types" + "hcm/pkg/dal/table" + tablegconf "hcm/pkg/dal/table/global-config" + "hcm/pkg/dal/table/utils" + "hcm/pkg/kit" + "hcm/pkg/logs" + "hcm/pkg/runtime/filter" + + "github.com/jmoiron/sqlx" +) + +// Interface only used for global config. +type Interface interface { + List(kt *kit.Kit, opt *types.ListOption) (*types.ListResult[tablegconf.GlobalConfigTable], error) + CreateWithTx(kt *kit.Kit, tx *sqlx.Tx, models []tablegconf.GlobalConfigTable) ([]string, error) + UpdateWithTx(kt *kit.Kit, tx *sqlx.Tx, filterExpr *filter.Expression, model *tablegconf.GlobalConfigTable) error + DeleteWithTx(kt *kit.Kit, tx *sqlx.Tx, f *filter.Expression) error +} + +var _ Interface = new(Dao) + +// Dao global config dao. +type Dao struct { + Orm orm.Interface + IDGen idgenerator.IDGenInterface +} + +// CreateWithTx ... +func (d Dao) CreateWithTx(kt *kit.Kit, tx *sqlx.Tx, models []tablegconf.GlobalConfigTable) ( + []string, error) { + + if len(models) == 0 { + return nil, errf.New(errf.InvalidParameter, "models to create cannot be empty") + } + + for index := range models { + if err := models[index].InsertValidate(); err != nil { + return nil, err + } + } + + ids, err := d.IDGen.Batch(kt, models[0].TableName(), len(models)) + if err != nil { + return nil, err + } + + for index := range models { + models[index].ID = ids[index] + } + + sql := fmt.Sprintf(`INSERT INTO %s (%s) VALUES(%s)`, models[0].TableName(), + tablegconf.GlobalConfigTableColumns.ColumnExpr(), tablegconf.GlobalConfigTableColumns.ColonNameExpr()) + + if err = d.Orm.Txn(tx).BulkInsert(kt.Ctx, sql, models); err != nil { + logs.Errorf("insert %s failed, err: %v, rid: %s", models[0].TableName(), err, kt.Rid) + return nil, fmt.Errorf("insert %s failed, err: %v", models[0].TableName(), err) + } + + return ids, nil +} + +// UpdateWithTx ... +func (d Dao) UpdateWithTx(kt *kit.Kit, tx *sqlx.Tx, filterExpr *filter.Expression, + model *tablegconf.GlobalConfigTable) error { + + if filterExpr == nil { + return errf.New(errf.InvalidParameter, "filter expr is nil") + } + + if err := model.UpdateValidate(); err != nil { + return err + } + + whereExpr, whereValue, err := filterExpr.SQLWhereExpr(tools.DefaultSqlWhereOption) + if err != nil { + return err + } + + opts := utils.NewFieldOptions().AddIgnoredFields(types.DefaultIgnoredFields...).AddBlankedFields("memo") + // config_key and config_type is combined unique index, so we need to ignore them. + opts = opts.AddIgnoredFields("config_key", "config_type") + + setExpr, toUpdate, err := utils.RearrangeSQLDataWithOption(model, opts) + if err != nil { + return fmt.Errorf("prepare parsed sql set filter expr failed, err: %v", err) + } + + sql := fmt.Sprintf(`UPDATE %s %s %s`, model.TableName(), setExpr, whereExpr) + + effected, err := d.Orm.Txn(tx).Update(kt.Ctx, sql, tools.MapMerge(toUpdate, whereValue)) + if err != nil { + logs.ErrorJson("update global config failed, filter: %v, err: %v, rid: %v", + filterExpr, err, kt.Rid) + return err + } + + if effected == 0 { + logs.ErrorJson("update global config, but record not found, filter: %v, rid: %v", + filterExpr, kt.Rid) + } + + return nil +} + +// List ... +func (d Dao) List(kt *kit.Kit, opt *types.ListOption) (*types.ListResult[tablegconf.GlobalConfigTable], error) { + if opt == nil { + return nil, errf.New(errf.InvalidParameter, "list global config options is nil") + } + + if err := opt.ValidateExcludeFilter( + filter.NewExprOption(filter.RuleFields(tablegconf.GlobalConfigTableColumns.ColumnTypes())), + core.NewDefaultPageOption()); err != nil { + return nil, err + } + + whereExpr, whereValue, err := opt.Filter.SQLWhereExpr(tools.DefaultSqlWhereOption) + if err != nil { + return nil, err + } + + if opt.Page.Count { + // this is a count request, then do count operation only. + sql := fmt.Sprintf(`SELECT COUNT(*) FROM %s %s`, table.GlobalConfigTable, whereExpr) + + count, err := d.Orm.Do().Count(kt.Ctx, sql, whereValue) + if err != nil { + logs.ErrorJson("count global config failed, err: %v, filter: %v, rid: %s", err, opt.Filter, kt.Rid) + return nil, err + } + + return &types.ListResult[tablegconf.GlobalConfigTable]{Count: count}, nil + } + + pageExpr, err := types.PageSQLExpr(opt.Page, types.DefaultPageSQLOption) + if err != nil { + return nil, err + } + + sql := fmt.Sprintf(`SELECT %s FROM %s %s %s`, tablegconf.GlobalConfigTableColumns.FieldsNamedExpr(opt.Fields), + table.GlobalConfigTable, whereExpr, pageExpr) + + details := make([]tablegconf.GlobalConfigTable, 0) + if err = d.Orm.Do().Select(kt.Ctx, &details, sql, whereValue); err != nil { + return nil, err + } + + return &types.ListResult[tablegconf.GlobalConfigTable]{Count: 0, Details: details}, nil +} + +// DeleteWithTx delete global config with tx. +func (d Dao) DeleteWithTx(kt *kit.Kit, tx *sqlx.Tx, expr *filter.Expression) error { + if expr == nil { + return errf.New(errf.InvalidParameter, "filter expr is required") + } + + whereExpr, whereValue, err := expr.SQLWhereExpr(tools.DefaultSqlWhereOption) + if err != nil { + return err + } + + sql := fmt.Sprintf(`DELETE FROM %s %s`, table.GlobalConfigTable, whereExpr) + + if _, err = d.Orm.Txn(tx).Delete(kt.Ctx, sql, whereValue); err != nil { + logs.ErrorJson("delete global config failed, err: %v, filter: %v, rid: %s", err, expr, kt.Rid) + return err + } + + return nil +} diff --git a/pkg/dal/dao/types/types.go b/pkg/dal/dao/types/types.go index 16eea35461..c9e54c80a8 100644 --- a/pkg/dal/dao/types/types.go +++ b/pkg/dal/dao/types/types.go @@ -91,6 +91,32 @@ func (opt *CountOption) Validate(eo *filter.ExprOption) error { return nil } +// ValidateExcludeFilter validate list option, Filter is allowed to be empty. +func (opt ListOption) ValidateExcludeFilter(eo *filter.ExprOption, po *core.PageOption) error { + if opt.Filter != nil { + if eo == nil { + return errf.New(errf.InvalidParameter, "filter expr option is required") + } + if err := opt.Filter.Validate(eo); err != nil { + return err + } + } + + if opt.Page == nil { + return errf.New(errf.InvalidParameter, "page is required") + } + + if po == nil { + return errf.New(errf.InvalidParameter, "page option is required") + } + + if err := opt.Page.Validate(po); err != nil { + return err + } + + return nil +} + // CountResult defines count resources with group by options result. type CountResult struct { GroupField string `db:"group_field" json:"group_field"` diff --git a/pkg/dal/table/global-config/global_config.go b/pkg/dal/table/global-config/global_config.go new file mode 100644 index 0000000000..110be214b6 --- /dev/null +++ b/pkg/dal/table/global-config/global_config.go @@ -0,0 +1,157 @@ +/* + * TencentBlueKing is pleased to support the open source community by making + * 蓝鲸智云 - 混合云管理平台 (BlueKing - Hybrid Cloud Management System) available. + * Copyright (C) 2022 THL A29 Limited, + * a Tencent company. All rights reserved. + * Licensed under the MIT License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://opensource.org/licenses/MIT + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * We undertake not to change the open source license (MIT license) applicable + * + * to the current version of the project delivered to anyone in the future. + */ + +// Package tablegconf global config table +package tablegconf + +import ( + "errors" + "fmt" + + "hcm/pkg/criteria/enumor" + "hcm/pkg/criteria/validator" + "hcm/pkg/dal/table" + "hcm/pkg/dal/table/types" + "hcm/pkg/dal/table/utils" +) + +// GlobalConfigTableColumns defines all the cvm table's columns. +var GlobalConfigTableColumns = utils.MergeColumns(nil, GlobalConfigTableColumnDescriptors) + +// GlobalConfigTableColumnDescriptors is cvm table column descriptors. +var GlobalConfigTableColumnDescriptors = utils.ColumnDescriptors{ + {Column: "id", NamedC: "id", Type: enumor.String}, + {Column: "config_key", NamedC: "config_key", Type: enumor.String}, + {Column: "config_value", NamedC: "config_value", Type: enumor.Json}, + {Column: "config_type", NamedC: "config_type", Type: enumor.String}, + {Column: "memo", NamedC: "memo", Type: enumor.String}, + {Column: "creator", NamedC: "creator", Type: enumor.String}, + {Column: "reviser", NamedC: "reviser", Type: enumor.String}, + {Column: "created_at", NamedC: "created_at", Type: enumor.String}, + {Column: "updated_at", NamedC: "updated_at", Type: enumor.String}, +} + +// GlobalConfigTable define cvm table. +type GlobalConfigTable struct { + // ID global config id + ID string `db:"id" json:"id"` + // ConfigKey global config key, key+type is unique + ConfigKey string `db:"config_key" json:"config_key"` + // ConfigValue global config value, json format + ConfigValue types.JsonField `db:"config_value" json:"config_value"` + // ConfigType global config type, enum: string/int/float/bool/map/slice + ConfigType string `db:"config_type" json:"config_type"` + // Memo global config memo + Memo *string `db:"memo" json:"memo"` + // Creator 创建者 + Creator string `db:"creator" json:"creator"` + // Reviser 更新者 + Reviser string `db:"reviser" json:"reviser"` + // CreatedAt 创建时间 + CreatedAt types.Time `db:"created_at" json:"created_at"` + // UpdatedAt 更新时间 + UpdatedAt types.Time `db:"updated_at" json:"updated_at"` +} + +// UniqueKey returns the unique key of BizOrgRel table. +func (t GlobalConfigTable) UniqueKey() string { + return fmt.Sprintf("(%s,%s)", t.ConfigType, t.ConfigKey) +} + +// Columns return cvm table columns. +func (t GlobalConfigTable) Columns() *utils.Columns { + return GlobalConfigTableColumns +} + +// ColumnDescriptors define cvm table column descriptor. +func (t GlobalConfigTable) ColumnDescriptors() utils.ColumnDescriptors { + return GlobalConfigTableColumnDescriptors +} + +// TableName return cvm table name. +func (t GlobalConfigTable) TableName() table.Name { + return table.GlobalConfigTable +} + +// InsertValidate cvm table when insert. +func (t GlobalConfigTable) InsertValidate() error { + // length validate. + if err := validator.Validate.Struct(t); err != nil { + return err + } + + if len(t.ID) != 0 { + return errors.New("id can not set") + } + + if len(t.ConfigKey) == 0 { + return errors.New("key is required") + } + + if len(t.ConfigValue) == 0 { + return errors.New("value is required") + } + + if len(t.ConfigType) == 0 { + return errors.New("type is required") + } + + if err := validator.ValidateMemo(t.Memo, false); err != nil { + return err + } + + if len(t.Creator) == 0 { + return errors.New("creator is required") + } + + if len(t.Reviser) == 0 { + return errors.New("reviser is required") + } + + return nil +} + +// UpdateValidate cvm table when update. +func (t GlobalConfigTable) UpdateValidate() error { + if err := validator.Validate.Struct(t); err != nil { + return err + } + if len(t.ID) != 0 { + return errors.New("id can not be updated") + } + + // key+type 组成一个全局唯一键,所以不能更新 + if len(t.ConfigKey) != 0 { + return errors.New("key can not be updated") + } + + if len(t.ConfigType) != 0 { + return errors.New("type can not be updated") + } + + if len(t.Creator) != 0 { + return errors.New("creator can not be updated") + } + + if len(t.Reviser) == 0 { + return errors.New("reviser is required") + } + + return nil +} diff --git a/pkg/dal/table/table.go b/pkg/dal/table/table.go index 170c7c13d6..b8dd4ee07e 100644 --- a/pkg/dal/table/table.go +++ b/pkg/dal/table/table.go @@ -202,6 +202,8 @@ const ( TaskDetailTable = "task_detail" // TaskManagementTable 任务管理表 TaskManagementTable = "task_management" + // GlobalConfigTable 全局配置表 + GlobalConfigTable = "global_config" ) // Validate whether the table name is valid or not. @@ -304,6 +306,8 @@ var TableMap = map[Name]struct{}{ TaskManagementTable: {}, TaskDetailTable: {}, + + GlobalConfigTable: {}, } // Register 注册表名 diff --git a/pkg/tools/util/conv.go b/pkg/tools/util/conv.go new file mode 100644 index 0000000000..50bcb43977 --- /dev/null +++ b/pkg/tools/util/conv.go @@ -0,0 +1,26 @@ +/* + * Tencent is pleased to support the open source community by making 蓝鲸 available. + * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved. + * Licensed under the MIT License (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * http://opensource.org/licenses/MIT + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Package util ... +package util + +import ( + "fmt" +) + +// GetStrByInterface interface to string +func GetStrByInterface(a interface{}) string { + if nil == a { + return "" + } + return fmt.Sprintf("%v", a) +} diff --git a/scripts/sql/9999_20241114_1100_global_config.sql b/scripts/sql/9999_20241114_1100_global_config.sql new file mode 100644 index 0000000000..796e4d83fc --- /dev/null +++ b/scripts/sql/9999_20241114_1100_global_config.sql @@ -0,0 +1,54 @@ +/* + * TencentBlueKing is pleased to support the open source community by making + * 蓝鲸智云 - 混合云管理平台 (BlueKing - Hybrid Cloud Management System) available. + * Copyright (C) 2022 THL A29 Limited, + * a Tencent company. All rights reserved. + * Licensed under the MIT License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://opensource.org/licenses/MIT + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * We undertake not to change the open source license (MIT license) applicable + * + * to the current version of the project delivered to anyone in the future. + */ + +/* + SQLVER=9999,HCMVER=v9.9.9 + + Notes: + 1. 添加全局配置表 global_config +*/ + +START TRANSACTION; + +-- 1. 全局配置表 +create table if not exists `global_config` +( + `id` varchar(64) not null comment '主键', + `config_key` varchar(64) not null comment 'key', + `config_value` json not null comment 'value', + `config_type` varchar(64) not null comment '类型', + `memo` varchar(255) default '' comment '备注', + `creator` varchar(64) not null comment '创建者', + `reviser` varchar(64) not null comment '更新者', + `result` json default NULL comment '结果', + `created_at` timestamp not null default current_timestamp comment '创建时间', + `updated_at` timestamp not null default current_timestamp on update current_timestamp comment '更新时间', + primary key (`id`), + unique key `idx_global_config_id` (`config_type`, `config_key`) +) engine = innodb + default charset = utf8mb4 + collate utf8mb4_bin comment ='全局配置表'; + +insert into id_generator(`resource`, `max_id`) +values ('global_config', '0'); + +CREATE OR REPLACE VIEW `hcm_version`(`hcm_ver`, `sql_ver`) AS +SELECT 'v9.9.9' as `hcm_ver`, '9999' as `sql_ver`; + +COMMIT;