From 4b5fc46498a192deca90d58d87b63feda5ad8611 Mon Sep 17 00:00:00 2001 From: actiontech-zihan Date: Tue, 21 Apr 2026 11:53:44 +0000 Subject: [PATCH 1/4] feat(masking): add API structs and controller handlers for custom masking rules #787 Add request/response structs for custom masking rules CRUD, sensitive types list, and masking effect preview. Add 6 controller handler methods following existing code style. --- api/dms/service/v1/masking.go | 291 +++++++++++++++++- .../apiserver/service/data_mask_controller.go | 254 +++++++++++++++ 2 files changed, 542 insertions(+), 3 deletions(-) diff --git a/api/dms/service/v1/masking.go b/api/dms/service/v1/masking.go index 2b93a4dda..6411dec50 100644 --- a/api/dms/service/v1/masking.go +++ b/api/dms/service/v1/masking.go @@ -10,9 +10,30 @@ type ListMaskingRulesReq struct { // swagger:model ListMaskingRulesData type ListMaskingRulesData struct { + // masking rule id + // Example: 1 + Id int `json:"id"` + // rule name + // Example: "手机号脱敏" + Name string `json:"name"` + // rule source: "builtin" or "custom" + // Example: "builtin" + Source string `json:"source"` // masking type // Example: "MASK_DIGIT" MaskingType string `json:"masking_type"` + // sensitive type display name + // Example: "手机号" + SensitiveTypeName string `json:"sensitive_type_name"` + // sensitive type identification info summary + // Example: "字段关键词: phone, mobile" + SensitiveTypeInfo string `json:"sensitive_type_info"` + // whether the sensitive type is user-created + // Example: false + IsCustomType bool `json:"is_custom_type"` + // masking algorithm type: CHAR, TAG, REPLACE, ALGO + // Example: "CHAR" + AlgorithmType string `json:"algorithm_type"` // description // Example: "mask digits" Description string `json:"description"` @@ -25,9 +46,6 @@ type ListMaskingRulesData struct { // effect example after masking // Example: "138******78" EffectExampleAfter string `json:"effect_example_after"` - // masking rule id - // Example: 1 - Id int `json:"id"` } // swagger:model ListMaskingRulesReply @@ -844,3 +862,270 @@ type ListCreatableDBServicesForMaskingTaskReply struct { base.GenericResp } + +// ===== 自定义脱敏规则 API 结构体 ===== + +// swagger:parameters ListMaskingRulesV2 +type ListMaskingRulesV2Req struct { + // project uid + // in: path + // Required: true + // Example: "project_uid" + ProjectUid string `param:"project_uid" json:"project_uid" validate:"required"` + // filter by source: "builtin" or "custom", empty returns all + // in: query + Source string `query:"source" json:"source"` + // fuzzy search by rule name + // in: query + Keywords string `query:"keywords" json:"keywords"` + // the maximum count of rules to be returned, default is 20 + // in: query + PageSize uint32 `query:"page_size" json:"page_size"` + // the offset of rules to be returned, default is 0 + // in: query + PageIndex uint32 `query:"page_index" json:"page_index"` +} + +// swagger:model ListMaskingRulesV2Reply +type ListMaskingRulesV2Reply struct { + // list masking rules reply + Data []ListMaskingRulesData `json:"data"` + // total count of masking rules + // Example: 100 + Total int64 `json:"total_nums"` + + base.GenericResp +} + +// swagger:model AddCustomMaskingRuleReq +type AddCustomMaskingRuleReq struct { + // project uid + // in: path + // swagger:ignore + // Required: true + // Example: "project_uid" + ProjectUid string `param:"project_uid" json:"project_uid" validate:"required"` + // custom masking rule + // Required: true + Rule *AddCustomMaskingRule `json:"rule" validate:"required"` +} + +// swagger:model AddCustomMaskingRule +type AddCustomMaskingRule struct { + // rule name + // Required: true + // Example: "自定义手机号脱敏" + Name string `json:"name" validate:"required"` + // rule description + // Example: "对手机号进行脱敏处理" + Description string `json:"description"` + // sensitive type configuration + // Required: true + SensitiveType *CustomRuleSensitiveType `json:"sensitive_type" validate:"required"` + // masking algorithm configuration + // Required: true + MaskingAlgorithm *CustomRuleMaskingAlgorithm `json:"masking_algorithm" validate:"required"` +} + +// swagger:model CustomRuleSensitiveType +type CustomRuleSensitiveType struct { + // sensitive type source: "builtin" or "custom" + // Required: true + // Example: "custom" + Source string `json:"source" validate:"required,oneof=builtin custom"` + // builtin sensitive type identifier, required when source is "builtin" + // Example: "PHONE" + BuiltinType string `json:"builtin_type"` + // existing custom type ID, used when source is "custom" and reusing existing type + // Example: 1 + CustomTypeID *uint `json:"custom_type_id"` + // new custom sensitive type definition, used when source is "custom" and creating new type + NewCustomType *NewCustomSensitiveType `json:"new_custom_type"` +} + +// swagger:model NewCustomSensitiveType +type NewCustomSensitiveType struct { + // chinese display name + // Required: true + // Example: "自定义手机号" + CnName string `json:"cn_name" validate:"required"` + // english identifier, only allows [a-z0-9_] + // Required: true + // Example: "custom_phone" + EnIdentifier string `json:"en_identifier" validate:"required"` + // field name keywords for identification + // Example: ["phone", "mobile"] + FieldKeywords []string `json:"field_keywords"` + // sample data regex pattern for identification + // Example: "^1[3-9]\\d{9}$" + SampleDataRegex string `json:"sample_data_regex"` +} + +// swagger:model CustomRuleMaskingAlgorithm +type CustomRuleMaskingAlgorithm struct { + // masking type: CHAR, TAG, REPLACE, ALGO + // Required: true + // Example: "CHAR" + MaskType string `json:"mask_type" validate:"required,oneof=CHAR TAG REPLACE ALGO"` + // masking value (replacement char for CHAR, tag text for TAG, replacement text for REPLACE, algorithm name for ALGO) + // Example: "*" + Value string `json:"value"` + // offset from the beginning for CHAR masking + // Example: 3 + Offset int32 `json:"offset"` + // padding from the end for CHAR masking + // Example: 4 + Padding int32 `json:"padding"` + // mask length for CHAR masking, 0 means mask all + // Example: 0 + Length int32 `json:"length"` + // whether to mask from the end for CHAR masking + // Example: false + Reverse bool `json:"reverse"` + // characters to ignore during CHAR masking + // Example: "-" + IgnoreCharSet string `json:"ignore_char_set"` +} + +// swagger:model AddCustomMaskingRuleReply +type AddCustomMaskingRuleReply struct { + // add custom masking rule reply + Data struct { + // new rule id + // Example: 10001 + RuleID uint `json:"rule_id"` + } `json:"data"` + + base.GenericResp +} + +// swagger:model UpdateCustomMaskingRuleReq +type UpdateCustomMaskingRuleReq struct { + // project uid + // in: path + // swagger:ignore + // Required: true + // Example: "project_uid" + ProjectUid string `param:"project_uid" json:"project_uid" validate:"required"` + // rule id + // in: path + // swagger:ignore + // Required: true + // Example: 10001 + RuleID uint `param:"rule_id" json:"rule_id" validate:"required"` + // custom masking rule update + // Required: true + Rule *UpdateCustomMaskingRule `json:"rule" validate:"required"` +} + +// swagger:model UpdateCustomMaskingRule +type UpdateCustomMaskingRule struct { + // rule name + // Required: true + // Example: "自定义手机号脱敏" + Name string `json:"name" validate:"required"` + // rule description + // Example: "对手机号进行脱敏处理" + Description string `json:"description"` + // masking algorithm configuration + // Required: true + MaskingAlgorithm *CustomRuleMaskingAlgorithm `json:"masking_algorithm" validate:"required"` + // custom sensitive type update (only allowed when the type is exclusively used by this rule) + CustomTypeUpdate *UpdateCustomSensitiveType `json:"custom_type_update"` +} + +// swagger:model UpdateCustomSensitiveType +type UpdateCustomSensitiveType struct { + // field name keywords for identification + // Example: ["phone", "mobile"] + FieldKeywords []string `json:"field_keywords"` + // sample data regex pattern for identification + // Example: "^1[3-9]\\d{9}$" + SampleDataRegex string `json:"sample_data_regex"` +} + +// swagger:model UpdateCustomMaskingRuleReply +type UpdateCustomMaskingRuleReply struct { + base.GenericResp +} + +// swagger:parameters DeleteCustomMaskingRule +type DeleteCustomMaskingRuleReq struct { + // project uid + // in: path + // Required: true + // Example: "project_uid" + ProjectUid string `param:"project_uid" json:"project_uid" validate:"required"` + // rule id + // in: path + // Required: true + // Example: 10001 + RuleID uint `param:"rule_id" json:"rule_id" validate:"required"` +} + +// swagger:model DeleteCustomMaskingRuleReply +type DeleteCustomMaskingRuleReply struct { + base.GenericResp +} + +// swagger:parameters ListSensitiveTypes +type ListSensitiveTypesReq struct { + // project uid + // in: path + // Required: true + // Example: "project_uid" + ProjectUid string `param:"project_uid" json:"project_uid" validate:"required"` +} + +// swagger:model SensitiveTypeData +type SensitiveTypeData struct { + // sensitive type source: "builtin" or "custom" + // Example: "builtin" + Source string `json:"source"` + // type identifier (builtin: enum value; custom: CUSTOM_ + en_identifier) + // Example: "PHONE" + TypeIdentifier string `json:"type_identifier"` + // chinese display name + // Example: "手机号" + CnName string `json:"cn_name"` + // custom type ID, only for custom types + // Example: 1 + CustomTypeID *uint `json:"custom_type_id"` + // field name keywords for identification + // Example: ["phone", "mobile"] + FieldKeywords []string `json:"field_keywords"` + // sample data regex pattern + // Example: "^1[3-9]\\d{9}$" + SampleDataRegex string `json:"sample_data_regex"` +} + +// swagger:model ListSensitiveTypesReply +type ListSensitiveTypesReply struct { + // sensitive types list + Data []SensitiveTypeData `json:"data"` + + base.GenericResp +} + +// swagger:model PreviewMaskingEffectReq +type PreviewMaskingEffectReq struct { + // sample input text for masking preview + // Required: true + // Example: "13812345678" + SampleInput string `json:"sample_input" validate:"required"` + // masking algorithm configuration + // Required: true + MaskingAlgorithm *CustomRuleMaskingAlgorithm `json:"masking_algorithm" validate:"required"` +} + +// swagger:model PreviewMaskingEffectReply +type PreviewMaskingEffectReply struct { + // masking effect preview reply + Data struct { + // masked output text + // Example: "138******78" + MaskedOutput string `json:"masked_output"` + } `json:"data"` + + base.GenericResp +} diff --git a/internal/apiserver/service/data_mask_controller.go b/internal/apiserver/service/data_mask_controller.go index 1a13b3987..417ec013e 100644 --- a/internal/apiserver/service/data_mask_controller.go +++ b/internal/apiserver/service/data_mask_controller.go @@ -737,3 +737,257 @@ func (ctl *DMSController) ProcessApprovalRequest(c echo.Context) error { } return NewOkRespWithReply(c, &aV1.ProcessApprovalRequestReply{}) } + +// swagger:operation GET /v1/dms/projects/{project_uid}/masking/rules Masking ListMaskingRulesV2 +// +// List masking rules (V2, includes custom rules). +// +// --- +// parameters: +// - name: project_uid +// description: project uid +// in: path +// required: true +// type: string +// - name: source +// description: "filter by source: builtin or custom, empty returns all" +// in: query +// required: false +// type: string +// - name: keywords +// description: fuzzy search by rule name +// in: query +// required: false +// type: string +// - name: page_size +// description: the maximum count of rules to be returned, default is 20 +// in: query +// required: false +// type: integer +// format: uint32 +// - name: page_index +// description: the offset of rules to be returned, default is 0 +// in: query +// required: false +// type: integer +// format: uint32 +// +// responses: +// +// '200': +// description: List masking rules successfully +// schema: +// "$ref": "#/definitions/ListMaskingRulesV2Reply" +// default: +// description: Generic error response +// schema: +// "$ref": "#/definitions/GenericResp" +func (ctl *DMSController) ListMaskingRulesV2(c echo.Context) error { + req := &aV1.ListMaskingRulesV2Req{} + if err := bindAndValidateReq(c, req); err != nil { + return NewErrResp(c, err, apiError.BadRequestErr) + } + + reply, err := ctl.DMS.ListMaskingRulesV2(c.Request().Context(), req) + if err != nil { + return NewErrResp(c, err, apiError.DMSServiceErr) + } + return NewOkRespWithReply(c, reply) +} + +// swagger:operation POST /v1/dms/projects/{project_uid}/masking/rules Masking AddCustomMaskingRule +// +// Add custom masking rule. +// +// --- +// parameters: +// - name: project_uid +// description: project uid +// in: path +// required: true +// type: string +// - name: rule +// description: custom masking rule info +// in: body +// required: true +// schema: +// "$ref": "#/definitions/AddCustomMaskingRuleReq" +// +// responses: +// +// '200': +// description: Add custom masking rule successfully +// schema: +// "$ref": "#/definitions/AddCustomMaskingRuleReply" +// default: +// description: Generic error response +// schema: +// "$ref": "#/definitions/GenericResp" +func (ctl *DMSController) AddCustomMaskingRule(c echo.Context) error { + req := &aV1.AddCustomMaskingRuleReq{} + if err := bindAndValidateReq(c, req); err != nil { + return NewErrResp(c, err, apiError.BadRequestErr) + } + + reply, err := ctl.DMS.AddCustomMaskingRule(c.Request().Context(), req) + if err != nil { + return NewErrResp(c, err, apiError.DMSServiceErr) + } + return NewOkRespWithReply(c, reply) +} + +// swagger:operation PUT /v1/dms/projects/{project_uid}/masking/rules/{rule_id} Masking UpdateCustomMaskingRule +// +// Update custom masking rule. +// +// --- +// parameters: +// - name: project_uid +// description: project uid +// in: path +// required: true +// type: string +// - name: rule_id +// description: custom masking rule id +// in: path +// required: true +// type: integer +// - name: rule +// description: custom masking rule update info +// in: body +// required: true +// schema: +// "$ref": "#/definitions/UpdateCustomMaskingRuleReq" +// +// responses: +// +// '200': +// description: Update custom masking rule successfully +// schema: +// "$ref": "#/definitions/UpdateCustomMaskingRuleReply" +// default: +// description: Generic error response +// schema: +// "$ref": "#/definitions/GenericResp" +func (ctl *DMSController) UpdateCustomMaskingRule(c echo.Context) error { + req := &aV1.UpdateCustomMaskingRuleReq{} + if err := bindAndValidateReq(c, req); err != nil { + return NewErrResp(c, err, apiError.BadRequestErr) + } + + reply, err := ctl.DMS.UpdateCustomMaskingRule(c.Request().Context(), req) + if err != nil { + return NewErrResp(c, err, apiError.DMSServiceErr) + } + return NewOkRespWithReply(c, reply) +} + +// swagger:operation DELETE /v1/dms/projects/{project_uid}/masking/rules/{rule_id} Masking DeleteCustomMaskingRule +// +// Delete custom masking rule. +// +// --- +// parameters: +// - name: project_uid +// description: project uid +// in: path +// required: true +// type: string +// - name: rule_id +// description: custom masking rule id +// in: path +// required: true +// type: integer +// +// responses: +// +// '200': +// description: Delete custom masking rule successfully +// schema: +// "$ref": "#/definitions/DeleteCustomMaskingRuleReply" +// default: +// description: Generic error response +// schema: +// "$ref": "#/definitions/GenericResp" +func (ctl *DMSController) DeleteCustomMaskingRule(c echo.Context) error { + req := &aV1.DeleteCustomMaskingRuleReq{} + if err := bindAndValidateReq(c, req); err != nil { + return NewErrResp(c, err, apiError.BadRequestErr) + } + + if err := ctl.DMS.DeleteCustomMaskingRule(c.Request().Context(), req); err != nil { + return NewErrResp(c, err, apiError.DMSServiceErr) + } + return NewOkRespWithReply(c, &aV1.DeleteCustomMaskingRuleReply{}) +} + +// swagger:operation GET /v1/dms/projects/{project_uid}/masking/sensitive-types Masking ListSensitiveTypes +// +// List sensitive types (builtin + custom). +// +// --- +// parameters: +// - name: project_uid +// description: project uid +// in: path +// required: true +// type: string +// +// responses: +// +// '200': +// description: List sensitive types successfully +// schema: +// "$ref": "#/definitions/ListSensitiveTypesReply" +// default: +// description: Generic error response +// schema: +// "$ref": "#/definitions/GenericResp" +func (ctl *DMSController) ListSensitiveTypes(c echo.Context) error { + req := &aV1.ListSensitiveTypesReq{} + if err := bindAndValidateReq(c, req); err != nil { + return NewErrResp(c, err, apiError.BadRequestErr) + } + + reply, err := ctl.DMS.ListSensitiveTypes(c.Request().Context(), req) + if err != nil { + return NewErrResp(c, err, apiError.DMSServiceErr) + } + return NewOkRespWithReply(c, reply) +} + +// swagger:operation POST /v1/dms/masking/preview Masking PreviewMaskingEffect +// +// Preview masking effect. +// +// --- +// parameters: +// - name: preview_req +// description: masking effect preview request +// in: body +// required: true +// schema: +// "$ref": "#/definitions/PreviewMaskingEffectReq" +// +// responses: +// +// '200': +// description: Preview masking effect successfully +// schema: +// "$ref": "#/definitions/PreviewMaskingEffectReply" +// default: +// description: Generic error response +// schema: +// "$ref": "#/definitions/GenericResp" +func (ctl *DMSController) PreviewMaskingEffect(c echo.Context) error { + req := &aV1.PreviewMaskingEffectReq{} + if err := bindAndValidateReq(c, req); err != nil { + return NewErrResp(c, err, apiError.BadRequestErr) + } + + reply, err := ctl.DMS.PreviewMaskingEffect(c.Request().Context(), req) + if err != nil { + return NewErrResp(c, err, apiError.DMSServiceErr) + } + return NewOkRespWithReply(c, reply) +} From a6a4e0f9575b2bc65fb024327a8767081d9c767b Mon Sep 17 00:00:00 2001 From: actiontech-zihan Date: Tue, 21 Apr 2026 13:05:31 +0000 Subject: [PATCH 2/4] fix(masking): add CE stubs for custom masking rule service methods The new V2 API controller calls ListMaskingRulesV2, AddCustomMaskingRule, UpdateCustomMaskingRule, DeleteCustomMaskingRule, ListSensitiveTypes, and PreviewMaskingEffect which only had EE implementations (dms build tag). Without CE stubs the non-dms build fails. Add stub methods returning errNotSupportDataMasking to data_masking_ce.go. --- internal/dms/service/data_masking_ce.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/internal/dms/service/data_masking_ce.go b/internal/dms/service/data_masking_ce.go index 04f176ea1..e66679788 100644 --- a/internal/dms/service/data_masking_ce.go +++ b/internal/dms/service/data_masking_ce.go @@ -73,6 +73,30 @@ func (d *DMSService) ListCreatableDBServicesForMaskingTask(ctx context.Context, return nil, errNotSupportDataMasking } +func (d *DMSService) ListMaskingRulesV2(ctx context.Context, req *dmsV1.ListMaskingRulesV2Req) (*dmsV1.ListMaskingRulesV2Reply, error) { + return nil, errNotSupportDataMasking +} + +func (d *DMSService) AddCustomMaskingRule(ctx context.Context, req *dmsV1.AddCustomMaskingRuleReq) (*dmsV1.AddCustomMaskingRuleReply, error) { + return nil, errNotSupportDataMasking +} + +func (d *DMSService) UpdateCustomMaskingRule(ctx context.Context, req *dmsV1.UpdateCustomMaskingRuleReq) (*dmsV1.UpdateCustomMaskingRuleReply, error) { + return nil, errNotSupportDataMasking +} + +func (d *DMSService) DeleteCustomMaskingRule(ctx context.Context, req *dmsV1.DeleteCustomMaskingRuleReq) error { + return errNotSupportDataMasking +} + +func (d *DMSService) ListSensitiveTypes(ctx context.Context, req *dmsV1.ListSensitiveTypesReq) (*dmsV1.ListSensitiveTypesReply, error) { + return nil, errNotSupportDataMasking +} + +func (d *DMSService) PreviewMaskingEffect(ctx context.Context, req *dmsV1.PreviewMaskingEffectReq) (*dmsV1.PreviewMaskingEffectReply, error) { + return nil, errNotSupportDataMasking +} + func initDataMaskingUsecase(_ utilLog.Logger, _ *storage.Storage, _ *biz.DBServiceUsecase, _ *biz.ClusterUsecase, _ biz.ProxyTargetRepo) (*dataMaskingUsecase, func(), error) { return nil, func() {}, nil } From 15555a6ab3db6ecd245374905a1361da8052eb23 Mon Sep 17 00:00:00 2001 From: actiontech-zihan Date: Wed, 22 Apr 2026 09:52:10 +0000 Subject: [PATCH 3/4] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E8=84=B1=E6=95=8F=E8=A7=84=E5=88=99=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E9=97=AE=E9=A2=98=20(API=E7=BB=93=E6=9E=84?= =?UTF-8?q?=E4=BD=93=E8=B0=83=E6=95=B4)=20#787?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/dms/service/v1/masking.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/api/dms/service/v1/masking.go b/api/dms/service/v1/masking.go index 6411dec50..3532c33f1 100644 --- a/api/dms/service/v1/masking.go +++ b/api/dms/service/v1/masking.go @@ -31,7 +31,13 @@ type ListMaskingRulesData struct { // whether the sensitive type is user-created // Example: false IsCustomType bool `json:"is_custom_type"` - // masking algorithm type: CHAR, TAG, REPLACE, ALGO + // builtin sensitive type identifier (for edit form pre-fill) + // Example: "phone" + BuiltinSensitiveType string `json:"builtin_sensitive_type,omitempty"` + // custom sensitive type ID (for edit form pre-fill) + // Example: 1 + CustomSensitiveTypeID *uint `json:"custom_sensitive_type_id,omitempty"` + // masking algorithm type: CHAR, TAG, REPLACE, HASH // Example: "CHAR" AlgorithmType string `json:"algorithm_type"` // description From eba3978d92c42ce578fa997cfd135d067815c99b Mon Sep 17 00:00:00 2001 From: actiontech-zihan Date: Wed, 22 Apr 2026 09:52:25 +0000 Subject: [PATCH 4/4] fix(masking): add rule_source field to API structs for builtin/custom distinction #787 --- api/dms/service/v1/masking.go | 42 ++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/api/dms/service/v1/masking.go b/api/dms/service/v1/masking.go index 3532c33f1..9ba79d2f7 100644 --- a/api/dms/service/v1/masking.go +++ b/api/dms/service/v1/masking.go @@ -116,17 +116,31 @@ type AddMaskingTemplateReq struct { MaskingTemplate *AddMaskingTemplate `json:"masking_template" validate:"required"` } +// swagger:model MaskingRuleRef +type MaskingRuleRef struct { + // rule id + // Required: true + // Example: 1 + RuleID int `json:"rule_id" validate:"required"` + // rule source: "builtin" or "custom" + // Required: true + // Example: "builtin" + RuleSource string `json:"rule_source" validate:"required,oneof=builtin custom"` +} + // swagger:model AddMaskingTemplate type AddMaskingTemplate struct { // masking template name // Required: true // Example: "New Template" Name string `json:"name" validate:"required"` - // masking rule id list - // Required: true - // MinLength: 1 + // masking rule references with source info (preferred over rule_ids) + // Example: [{"rule_id": 1, "rule_source": "builtin"}, {"rule_id": 2, "rule_source": "custom"}] + RuleRefs []MaskingRuleRef `json:"rule_refs"` + // masking rule id list (deprecated: use rule_refs instead; kept for backward compatibility) + // When rule_refs is provided, rule_ids is ignored. // Example: [1, 2, 3] - RuleIDs []int `json:"rule_ids" validate:"required,min=1"` + RuleIDs []int `json:"rule_ids"` } // swagger:model AddMaskingTemplateReply @@ -155,11 +169,13 @@ type UpdateMaskingTemplateReq struct { // swagger:model UpdateMaskingTemplate type UpdateMaskingTemplate struct { - // masking rule id list - // Required: true - // MinLength: 1 + // masking rule references with source info (preferred over rule_ids) + // Example: [{"rule_id": 1, "rule_source": "builtin"}, {"rule_id": 2, "rule_source": "custom"}] + RuleRefs []MaskingRuleRef `json:"rule_refs"` + // masking rule id list (deprecated: use rule_refs instead; kept for backward compatibility) + // When rule_refs is provided, rule_ids is ignored. // Example: [1, 2] - RuleIDs []int `json:"rule_ids" validate:"required,min=1"` + RuleIDs []int `json:"rule_ids"` } // swagger:model UpdateMaskingTemplateReply @@ -348,6 +364,9 @@ type SensitiveFieldScanResult struct { // recommended masking rule id // Example: 1 RecommendedMaskingRuleID int `json:"recommended_masking_rule_id"` + // recommended masking rule source: "builtin" or "custom" + // Example: "builtin" + RecommendedMaskingRuleSource string `json:"recommended_masking_rule_source,omitempty"` // recommended masking rule name // Example: "Email Masking" RecommendedMaskingRuleName string `json:"recommended_masking_rule_name"` @@ -551,6 +570,9 @@ type MaskingRuleConfig struct { // Required: true // Example: 1 MaskingRuleID int `json:"masking_rule_id" validate:"required"` + // masking rule source: "builtin" or "custom", default is "builtin" + // Example: "builtin" + MaskingRuleSource string `json:"masking_rule_source"` // whether to enable masking for this column // Required: true // Example: true @@ -668,6 +690,10 @@ type TableColumnMaskingDetail struct { // // Example: 1 MaskingRuleID *int `json:"masking_rule_id"` + // current masking rule source: "builtin" or "custom" + // + // Example: "builtin" + MaskingRuleSource string `json:"masking_rule_source,omitempty"` // current masking rule name, null if no masking rule is applied // // Example: "Email Masking"