Skip to content

Commit f77a494

Browse files
committed
feat:新增数组字段 --story=128495448 字段类型: 数组
1 parent cc45f93 commit f77a494

15 files changed

+3423
-0
lines changed
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Tencent is pleased to support the open source community by making 蓝鲸 available.
3+
* Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.
4+
* Licensed under the MIT License (the "License"); you may not use this file except
5+
* in compliance with the License. You may obtain a copy of the License at
6+
* http://opensource.org/licenses/MIT
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
9+
* either express or implied. See the License for the specific language governing permissions and
10+
* limitations under the License.
11+
*/
12+
13+
package metadata
14+
15+
import (
16+
"configcenter/src/common/util"
17+
"encoding/json"
18+
"fmt"
19+
"math"
20+
21+
"github.com/tidwall/gjson"
22+
"go.mongodb.org/mongo-driver/bson"
23+
)
24+
25+
// ArrayOption len cap ,option is basic type 's option
26+
type ArrayOption[T any] struct {
27+
Len int `bson:"len" json:"len"`
28+
Cap int `bson:"cap" json:"cap"`
29+
Option T `bson:"option" json:"option"`
30+
}
31+
32+
// Valid ArrayOption
33+
func (a *ArrayOption[T]) Valid() error {
34+
if a.Len < 0 || a.Len > a.Cap {
35+
return fmt.Errorf("invalid array option,len:%d cap:%d", a.Len, a.Cap)
36+
}
37+
return nil
38+
}
39+
40+
// ParseArrayOption len cap ,option is basic type 's option
41+
func ParseArrayOption[T any](option any, handle func(v any) (T, error)) (ArrayOption[T], error) {
42+
if option == nil {
43+
return ArrayOption[T]{Len: math.MaxInt, Cap: math.MaxInt}, nil
44+
}
45+
46+
var result ArrayOption[T]
47+
48+
optMap := map[string]interface{}{
49+
"len": math.MaxInt,
50+
"cap": math.MaxInt,
51+
}
52+
switch value := option.(type) {
53+
case ArrayOption[T]:
54+
return value, nil
55+
case bson.M:
56+
optMap = value
57+
case map[string]interface{}:
58+
optMap = value
59+
default:
60+
marshal, err := json.Marshal(option)
61+
if err != nil {
62+
return result, fmt.Errorf("invalid array option,type:%v,value:%v,err:%w", option, option, err)
63+
}
64+
65+
lenItem := gjson.GetBytes(marshal, "len")
66+
capItem := gjson.GetBytes(marshal, "cap")
67+
if !lenItem.Exists() || !capItem.Exists() {
68+
return result, fmt.Errorf("invalid array option,type:%v,value:%v,err: not exist len or cap", option, option)
69+
}
70+
optMap["len"] = lenItem.Int()
71+
optMap["cap"] = capItem.Int()
72+
optMap["option"] = gjson.GetBytes(marshal, "option").Value()
73+
}
74+
75+
lenn, lenOk := optMap["len"]
76+
capp, capOk := optMap["cap"]
77+
if !lenOk || !capOk {
78+
return result, fmt.Errorf("invalid array option,type:%v,value:%v,err: not exist len or cap", option, option)
79+
}
80+
81+
lenOpt, err := util.GetIntByInterface(lenn)
82+
if err != nil {
83+
return result, err
84+
}
85+
result.Len = lenOpt
86+
capOpt, err := util.GetIntByInterface(capp)
87+
if err != nil {
88+
return result, err
89+
}
90+
result.Cap = capOpt
91+
92+
var defaultOption T
93+
result.Option = defaultOption
94+
if handle != nil {
95+
t, err := handle(optMap["option"])
96+
if err != nil {
97+
return ArrayOption[T]{}, err
98+
}
99+
result.Option = t
100+
}
101+
return result, result.Valid()
102+
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
/*
2+
* Tencent is pleased to support the open source community by making 蓝鲸 available.
3+
* Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.
4+
* Licensed under the MIT License (the "License"); you may not use this file except
5+
* in compliance with the License. You may obtain a copy of the License at
6+
* http://opensource.org/licenses/MIT
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
9+
* either express or implied. See the License for the specific language governing permissions and
10+
* limitations under the License.
11+
*/
12+
13+
package metadata
14+
15+
import (
16+
"context"
17+
"fmt"
18+
19+
"configcenter/src/common"
20+
"configcenter/src/common/blog"
21+
"configcenter/src/common/mapstr"
22+
"configcenter/src/common/util"
23+
"configcenter/src/common/valid/attribute/manager/register"
24+
)
25+
26+
func init() {
27+
// Register the arrayBool attribute type
28+
register.Register(arrayBool{})
29+
}
30+
31+
type arrayBool struct {
32+
}
33+
34+
// Name returns the name of the arrayBool attribute.
35+
func (a arrayBool) Name() string {
36+
return "array_bool"
37+
}
38+
39+
// DisplayName returns the display name for user.
40+
func (a arrayBool) DisplayName() string {
41+
return "布尔数组"
42+
}
43+
44+
// RealType returns the db type of the arrayBool attribute.
45+
// Flattened array uses LongChar as storage type
46+
func (a arrayBool) RealType() string {
47+
return common.FieldTypeLongChar
48+
}
49+
50+
// Info returns the tips for user.
51+
func (a arrayBool) Info() string {
52+
return "布尔值的扁平化数组字段,存储多个true/false值"
53+
}
54+
55+
// Validate validates the arrayBool attribute value
56+
func (a arrayBool) Validate(ctx context.Context, objID string, propertyType string, required bool,
57+
option, value interface{}) error {
58+
59+
rid := util.ExtractRequestIDFromContext(ctx)
60+
61+
if value == nil {
62+
if required {
63+
blog.Errorf("array_bool attribute %s.%s value is required but got nil, rid: %s",
64+
objID, propertyType, rid)
65+
return fmt.Errorf("array_bool attribute %s.%s value is required but got nil",
66+
objID, propertyType)
67+
}
68+
return nil
69+
}
70+
opts, err := ParseArrayOption[any](option, nil)
71+
if err != nil {
72+
blog.Errorf("array_bool parse option failed: %v, rid: %s", err, rid)
73+
return fmt.Errorf("array_bool invalid option: %v", err)
74+
}
75+
76+
// Validate that value is a slice of any
77+
boolArray, ok := util.ConvertAnyToSlice(value)
78+
if !ok {
79+
blog.Errorf("array_bool attribute %s.%s value must be []interface{}, got %T, rid: %s",
80+
objID, propertyType, value, rid)
81+
return fmt.Errorf("array_bool attribute %s.%s value must be []interface{}, got %T",
82+
objID, propertyType, value)
83+
}
84+
if opts.Cap < len(boolArray) {
85+
return fmt.Errorf("array_bool invalid cap %d, rid: %s", opts.Cap, rid)
86+
}
87+
// Validate each item in the array is a boolean
88+
for i, item := range boolArray {
89+
if _, ok := item.(bool); !ok {
90+
blog.Errorf("array_bool attribute %s.%s array item [%d] type %T is not bool, rid: %s",
91+
objID, propertyType, i, item, rid)
92+
return fmt.Errorf("array_bool attribute %s.%s array item [%v] type %T is not bool",
93+
objID, propertyType, item, item)
94+
}
95+
}
96+
97+
return nil
98+
}
99+
100+
// FillLostValue fills the lost value with default value
101+
func (a arrayBool) FillLostValue(ctx context.Context, valData mapstr.MapStr, propertyId string,
102+
defaultValue, option interface{}) error {
103+
104+
rid := util.ExtractRequestIDFromContext(ctx)
105+
106+
valData[propertyId] = nil
107+
if defaultValue == nil {
108+
return nil
109+
}
110+
111+
// Validate default value
112+
defaultArray, ok := util.ConvertAnyToSlice(defaultValue)
113+
if !ok {
114+
blog.Errorf("array_bool default value must be []interface{}, got %T, rid: %s", defaultValue, rid)
115+
return fmt.Errorf("array_bool default value must be []interface{}, got %T", defaultValue)
116+
}
117+
118+
// Validate each item in default array
119+
for i, item := range defaultArray {
120+
if _, ok := item.(bool); !ok {
121+
blog.Errorf("array_bool default value array item [%d] type %T is not bool, rid: %s", i, item, rid)
122+
return fmt.Errorf("array_bool default value array item [%d] type %T is not bool", i, item)
123+
}
124+
}
125+
126+
valData[propertyId] = defaultArray
127+
return nil
128+
}
129+
130+
// ValidateOption validates the option field
131+
func (a arrayBool) ValidateOption(ctx context.Context, option interface{}, defaultVal interface{}) error {
132+
133+
rid := util.ExtractRequestIDFromContext(ctx)
134+
135+
_, err := ParseArrayOption[any](option, nil)
136+
if err != nil {
137+
return err
138+
}
139+
if defaultVal == nil {
140+
return nil
141+
}
142+
143+
// Validate default value
144+
defaultArray, ok := util.ConvertAnyToSlice(defaultVal)
145+
if !ok {
146+
blog.Errorf("array_bool default value must be []interface{}, got %T, rid: %s", defaultVal, rid)
147+
return fmt.Errorf("array_bool default value must be []interface{}, got %T", defaultVal)
148+
}
149+
150+
// Validate each item in default array
151+
for i, item := range defaultArray {
152+
if _, ok := item.(bool); !ok {
153+
blog.Errorf("array_bool default value array item [%d] type %T is not bool, rid: %s", i, item, rid)
154+
return fmt.Errorf("array_bool default value array item [%d] type %T is not bool", i, item)
155+
}
156+
}
157+
158+
return nil
159+
}
160+
161+
var _ register.AttributeTypeI = &arrayBool{}

0 commit comments

Comments
 (0)