Skip to content

Commit db92aa4

Browse files
committed
relaxednames->namecharset
Signed-off-by: Doug Davis <duglin@gmail.com>
1 parent 8778fff commit db92aa4

File tree

4 files changed

+147
-97
lines changed

4 files changed

+147
-97
lines changed

registry/entity.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2240,12 +2240,12 @@ func (e *Entity) Validate() error {
22402240
e.Type, e.UID, ToJSON(e.NewObject))
22412241
log.VPrintf(0, "Attrs: %v", SortedKeys(attrs))
22422242
}
2243-
return e.ValidateObject(e.NewObject, false, attrs, NewPP())
2243+
return e.ValidateObject(e.NewObject, "strict", attrs, NewPP())
22442244
}
22452245

22462246
// This should be called after all type-specific calculated properties have
22472247
// been removed - such as collections
2248-
func (e *Entity) ValidateObject(val any, relaxedNames bool, origAttrs Attributes, path *PropPath) error {
2248+
func (e *Entity) ValidateObject(val any, namecharset string, origAttrs Attributes, path *PropPath) error {
22492249

22502250
log.VPrintf(3, ">Enter: ValidateObject(path: %s)", path)
22512251
defer log.VPrintf(3, "<Exit: ValidateObject")
@@ -2451,14 +2451,17 @@ func (e *Entity) ValidateObject(val any, relaxedNames bool, origAttrs Attributes
24512451
// And finally check to make sure it's a valid attribute name,
24522452
// but only if it's actually present in the object.
24532453
if keyPresent {
2454-
if relaxedNames {
2455-
if err := IsValidAttributeRelaxedName(key); err != nil {
2454+
if namecharset == "extended" {
2455+
if err := IsValidMapKey(key); err != nil {
24562456
return err
24572457
}
2458-
} else {
2458+
} else if namecharset == "" || namecharset == "strict" {
24592459
if err := IsValidAttributeName(key); err != nil {
24602460
return err
24612461
}
2462+
} else {
2463+
return fmt.Errorf("Unknown \"namecharset\" value: %s",
2464+
namecharset)
24622465
}
24632466
}
24642467

@@ -2517,7 +2520,7 @@ func (e *Entity) ValidateAttribute(val any, attr *Attribute, path *PropPath) err
25172520
}
25182521
*/
25192522

2520-
return e.ValidateObject(val, attr.RelaxedNames, attr.Attributes, path)
2523+
return e.ValidateObject(val, attr.NameCharSet, attr.Attributes, path)
25212524
}
25222525

25232526
ShowStack()

registry/model.go

Lines changed: 88 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import (
1616

1717
var RegexpModelName = regexp.MustCompile("^[a-z_][a-z_0-9]{0,57}$")
1818
var RegexpPropName = regexp.MustCompile("^[a-z_][a-z_0-9]{0,62}$")
19-
var RegexpPropRelaxedName = regexp.MustCompile("^[a-z_][a-z_0-9\\-]{0,62}$")
2019
var RegexpMapKey = regexp.MustCompile("^[a-z0-9][a-z0-9_.:\\-]{0,62}$")
2120
var RegexpID = regexp.MustCompile("^[a-zA-Z0-9_][a-zA-Z0-9_.\\-~@]{0,127}$")
2221

@@ -40,14 +39,6 @@ func IsValidAttributeName(name string) error {
4039
name, RegexpPropName.String())
4140
}
4241

43-
func IsValidAttributeRelaxedName(name string) error {
44-
if RegexpPropRelaxedName.MatchString(name) {
45-
return nil
46-
}
47-
return fmt.Errorf("Invalid attribute name %q, must match: %s",
48-
name, RegexpPropRelaxedName.String())
49-
}
50-
5142
func IsValidMapKey(key string) error {
5243
if RegexpMapKey.MatchString(key) {
5344
return nil
@@ -93,18 +84,18 @@ type AttrInternals struct {
9384
// 'false', but Strict needs to default to 'true'. See the custome Unmarshal
9485
// funcs in model.go for how we set those
9586
type Attribute struct {
96-
Model *Model `json:"-"`
97-
Name string `json:"name,omitempty"`
98-
Type string `json:"type,omitempty"`
99-
Target string `json:"target,omitempty"`
100-
RelaxedNames bool `json:"relaxednames,omitempty"`
101-
Description string `json:"description,omitempty"`
102-
Enum []any `json:"enum,omitempty"` // just scalars though
103-
Strict *bool `json:"strict,omitempty"`
104-
ReadOnly bool `json:"readonly,omitempty"`
105-
Immutable bool `json:"immutable,omitempty"`
106-
Required bool `json:"required,omitempty"`
107-
Default any `json:"default,omitempty"`
87+
Model *Model `json:"-"`
88+
Name string `json:"name,omitempty"`
89+
Type string `json:"type,omitempty"`
90+
Target string `json:"target,omitempty"`
91+
NameCharSet string `json:"namecharset,omitempty"`
92+
Description string `json:"description,omitempty"`
93+
Enum []any `json:"enum,omitempty"` // just scalars though
94+
Strict *bool `json:"strict,omitempty"`
95+
ReadOnly bool `json:"readonly,omitempty"`
96+
Immutable bool `json:"immutable,omitempty"`
97+
Required bool `json:"required,omitempty"`
98+
Default any `json:"default,omitempty"`
10899

109100
Attributes Attributes `json:"attributes,omitempty"` // for Objs
110101
Item *Item `json:"item,omitempty"` // for maps & arrays
@@ -118,17 +109,17 @@ type Attribute struct {
118109
}
119110

120111
type Item struct { // for maps and arrays
121-
Model *Model `json:"-"`
122-
Type string `json:"type,omitempty"`
123-
RelaxedNames bool `json:"relaxednames,omitempty"` // when 'type'=obj
124-
Attributes Attributes `json:"attributes,omitempty"` // when 'type'=obj
125-
Item *Item `json:"item,omitempty"` // when 'type'=map,array
112+
Model *Model `json:"-"`
113+
Type string `json:"type,omitempty"`
114+
NameCharSet string `json:"namecharset,omitempty"` // when 'type'=obj
115+
Attributes Attributes `json:"attributes,omitempty"` // when 'type'=obj
116+
Item *Item `json:"item,omitempty"` // when 'type'=map,array
126117
}
127118

128119
type IfValues map[string]*IfValue
129120

130121
type IfValue struct {
131-
SiblingAttributes Attributes `json:"siblingAttributes,omitempty"`
122+
SiblingAttributes Attributes `json:"siblingattributes,omitempty"`
132123
}
133124

134125
type GroupModel struct {
@@ -627,13 +618,25 @@ func (i *Item) AddAttribute(attr *Attribute) (*Attribute, error) {
627618
}
628619

629620
if attr.Name != "*" {
630-
if i.RelaxedNames {
631-
if err := IsValidAttributeRelaxedName(attr.Name); err != nil {
632-
return nil, err
621+
if i.Type == OBJECT {
622+
if i.NameCharSet == "extended" {
623+
if err := IsValidMapKey(attr.Name); err != nil {
624+
return nil, fmt.Errorf("Invalid attribute name %q, must "+
625+
"match: %s", attr.Name, RegexpMapKey.String())
626+
}
627+
} else if i.NameCharSet == "strict" || i.NameCharSet == "" {
628+
if err := IsValidAttributeName(attr.Name); err != nil {
629+
return nil, err
630+
}
631+
} else {
632+
return nil, fmt.Errorf("Invalid \"namecharset\" value: %s",
633+
i.NameCharSet)
633634
}
634635
} else {
635-
if err := IsValidAttributeName(attr.Name); err != nil {
636-
return nil, err
636+
if attr.NameCharSet != "" {
637+
return nil, fmt.Errorf("Attribute %q must not have a "+
638+
"\"namecharset\" value unless its type is \"object\"",
639+
attr.Name)
637640
}
638641
}
639642
}
@@ -1480,13 +1483,25 @@ func (a *Attribute) AddAttrArray(name string, item *Item) (*Attribute, error) {
14801483
}
14811484
func (a *Attribute) AddAttribute(attr *Attribute) (*Attribute, error) {
14821485
if attr.Name != "*" {
1483-
if a.RelaxedNames {
1484-
if err := IsValidAttributeRelaxedName(attr.Name); err != nil {
1485-
return nil, err
1486+
if a.Type == OBJECT {
1487+
if a.NameCharSet == "extended" {
1488+
if err := IsValidMapKey(attr.Name); err != nil {
1489+
return nil, fmt.Errorf("Invalid attribute name %q, must "+
1490+
"match: %s", attr.Name, RegexpMapKey.String())
1491+
}
1492+
} else if a.NameCharSet == "strict" || a.NameCharSet == "" {
1493+
if err := IsValidAttributeName(attr.Name); err != nil {
1494+
return nil, err
1495+
}
1496+
} else {
1497+
return nil, fmt.Errorf("Invalid \"namecharset\" value: %s",
1498+
a.NameCharSet)
14861499
}
14871500
} else {
1488-
if err := IsValidAttributeName(attr.Name); err != nil {
1489-
return nil, err
1501+
if a.NameCharSet != "" {
1502+
return nil, fmt.Errorf("Attribute %q must not have a "+
1503+
"\"namecharset\" value unless it is of type \"object\"",
1504+
a.Name)
14901505
}
14911506
}
14921507
}
@@ -1582,7 +1597,7 @@ func (m *Model) Verify() error {
15821597
AttrNames: map[string]bool{},
15831598
Path: NewPPP("model"),
15841599
}
1585-
if err := m.Attributes.Verify(false, ld); err != nil {
1600+
if err := m.Attributes.Verify("strict", ld); err != nil {
15861601
return err
15871602
}
15881603

@@ -1678,7 +1693,7 @@ func (gm *GroupModel) Verify(gmName string) error {
16781693
AttrNames: map[string]bool{},
16791694
Path: NewPPP("groups").P(gm.Plural),
16801695
}
1681-
if err := gm.Attributes.Verify(false, ld); err != nil {
1696+
if err := gm.Attributes.Verify("strict", ld); err != nil {
16821697
return err
16831698
}
16841699

@@ -1820,7 +1835,7 @@ func (rm *ResourceModel) Verify(rmName string) error {
18201835
// attrs[rm.Singular+"base64"] = &Attribute{Name: rm.Singular + "base64", Type: STRING}
18211836
}
18221837

1823-
if err := attrs.Verify(false, ld); err != nil {
1838+
if err := attrs.Verify("strict", ld); err != nil {
18241839
return err
18251840
}
18261841

@@ -2189,7 +2204,7 @@ func ConvertString(val string, toType string) (any, bool) {
21892204
var targetREstr = `^(?:/([^/]+)(?:/([^[/]+)(?:(?:/(versions)|(\[(?:/versions)]))?))?)?$`
21902205
var targetRE = regexp.MustCompile(targetREstr)
21912206

2192-
func (attrs Attributes) Verify(relaxedNames bool, ld *LevelData) error {
2207+
func (attrs Attributes) Verify(namecharset string, ld *LevelData) error {
21932208
ld = &LevelData{
21942209
Model: ld.Model,
21952210
AttrNames: maps.Clone(ld.AttrNames),
@@ -2219,14 +2234,17 @@ func (attrs Attributes) Verify(relaxedNames bool, ld *LevelData) error {
22192234
// Technically we should convert the XXXid into id but any XXXid needs
22202235
// to be a valid name/string so we should be ok
22212236
if name != "*" && SpecProps[name] == nil {
2222-
if relaxedNames {
2223-
if err := IsValidAttributeRelaxedName(name); err != nil {
2237+
if namecharset == "extended" {
2238+
if err := IsValidMapKey(name); err != nil {
22242239
return fmt.Errorf("Error processing %q: %s", ld.Path.UI(), err)
22252240
}
2226-
} else {
2241+
} else if namecharset == "strict" || namecharset == "" {
22272242
if err := IsValidAttributeName(name); err != nil {
22282243
return fmt.Errorf("Error processing %q: %s", ld.Path.UI(), err)
22292244
}
2245+
} else {
2246+
return fmt.Errorf("Invalid \"namecharset\" value: %s",
2247+
namecharset)
22302248
}
22312249
}
22322250
path := ld.Path.P(name)
@@ -2322,12 +2340,23 @@ func (attrs Attributes) Verify(relaxedNames bool, ld *LevelData) error {
23222340
}
23232341

23242342
if attr.Type == OBJECT {
2343+
if attr.NameCharSet != "" && attr.NameCharSet != "strict" && attr.NameCharSet != "extended" {
2344+
return fmt.Errorf("%q has an invalid \"namecharset\" value: "+
2345+
attr.NameCharSet, path.UI())
2346+
}
2347+
23252348
if attr.Item != nil {
23262349
return fmt.Errorf("%q must not have an \"item\" section", path.UI())
23272350
}
2328-
if err := attr.Attributes.Verify(attr.RelaxedNames, &LevelData{ld.Model, nil, path}); err != nil {
2351+
if err := attr.Attributes.Verify(attr.NameCharSet, &LevelData{ld.Model, nil, path}); err != nil {
23292352
return err
23302353
}
2354+
} else {
2355+
if attr.NameCharSet != "" {
2356+
return fmt.Errorf("Attribute %q must not have a "+
2357+
"\"namecharset\" value unless its type is \"object\"",
2358+
attr.Name)
2359+
}
23312360
}
23322361

23332362
if attr.Item != nil {
@@ -2359,7 +2388,7 @@ func (attrs Attributes) Verify(relaxedNames bool, ld *LevelData) error {
23592388
ld.Path.P(attr.Name).P("ifvalues").P(valStr)}
23602389

23612390
// Recursive
2362-
if err := ifValue.SiblingAttributes.Verify(relaxedNames, nextLD); err != nil {
2391+
if err := ifValue.SiblingAttributes.Verify(namecharset, nextLD); err != nil {
23632392
return err
23642393
}
23652394
}
@@ -2414,8 +2443,20 @@ func (item *Item) Verify(path *PropPath) error {
24142443
}
24152444
}
24162445

2446+
if item.Type == OBJECT {
2447+
if item.NameCharSet != "" && item.NameCharSet != "strict" && item.NameCharSet != "extended" {
2448+
return fmt.Errorf("Invalid \"namecharset\" value: %s",
2449+
item.NameCharSet)
2450+
}
2451+
} else {
2452+
if item.NameCharSet != "" {
2453+
return fmt.Errorf("%q must not have a \"namecharset\" value "+
2454+
"since it is not of type \"object\"", p.UI())
2455+
}
2456+
}
2457+
24172458
if item.Attributes != nil {
2418-
if err := item.Attributes.Verify(item.RelaxedNames, &LevelData{item.Model, nil, p}); err != nil {
2459+
if err := item.Attributes.Verify(item.NameCharSet, &LevelData{item.Model, nil, p}); err != nil {
24192460
return err
24202461
}
24212462
}

registry/model_test.go

Lines changed: 17 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,19 @@ func TestModelVerifyRegAttr(t *testing.T) {
212212
{"type - object - 2", Model{
213213
Attributes: Attributes{"x": {Name: "x", Type: OBJECT,
214214
Attributes: Attributes{}}}}, ``},
215+
{"type - object - strict '' - ", Model{
216+
Attributes: Attributes{"x": {Name: "x", Type: OBJECT,
217+
NameCharSet: ""}}}, ``},
218+
{"type - object - strict - ", Model{
219+
Attributes: Attributes{"x": {Name: "x", Type: OBJECT,
220+
NameCharSet: "strict"}}}, ``},
221+
{"type - object - extended '' - ", Model{
222+
Attributes: Attributes{"x": {Name: "x", Type: OBJECT,
223+
NameCharSet: "extended"}}}, ``},
224+
{"type - object - bad name charset - ", Model{
225+
Attributes: Attributes{"x": {Name: "x", Type: OBJECT,
226+
NameCharSet: "foo"}}},
227+
`"model.x" has an invalid "namecharset" value: foo`},
215228

216229
{"type - attr - err1", Model{
217230
Attributes: Attributes{".foo": {Name: ".foo", Type: ANY}}},
@@ -264,6 +277,10 @@ func TestModelVerifyRegAttr(t *testing.T) {
264277
Attributes: Attributes{"m": {Name: "m", Type: MAP,
265278
Item: &Item{Type: OBJECT}}}},
266279
``},
280+
{"Nested - map - obj - bad namecharset", Model{
281+
Attributes: Attributes{"m": {Name: "m", Type: MAP,
282+
Item: &Item{Type: OBJECT, NameCharSet: "foo"}}}},
283+
`Invalid "namecharset" value: foo`},
267284
{"Nested - map - obj - missing item - valid", Model{
268285
Attributes: Attributes{"m": {Name: "m", Type: MAP,
269286
Item: &Item{Type: OBJECT, Attributes: Attributes{}}}}},
@@ -718,43 +735,6 @@ func TestValidChars(t *testing.T) {
718735
}
719736
}
720737

721-
// Test relaxed attribute names
722-
match = RegexpPropRelaxedName.String()
723-
for _, test := range []struct {
724-
input string
725-
result string
726-
}{
727-
{"", `Invalid attribute name "", must match: ` + match},
728-
{"A", `Invalid attribute name "A", must match: ` + match},
729-
{"*", `Invalid attribute name "*", must match: ` + match},
730-
{"@", `Invalid attribute name "@", must match: ` + match},
731-
{"0", `Invalid attribute name "0", must match: ` + match},
732-
{"0a", `Invalid attribute name "0a", must match: ` + match},
733-
{"aZ", `Invalid attribute name "aZ", must match: ` + match},
734-
{"-aZ", `Invalid attribute name "-aZ", must match: ` + match},
735-
{a64, `Invalid attribute name "` + a64 + `", must match: ` + match},
736-
{"a", ``},
737-
{"_", ``},
738-
{"_a", ``},
739-
{"_8", ``},
740-
{"a_", ``},
741-
{"a-", ``},
742-
{"a_8", ``},
743-
{"a-8", ``},
744-
{"aa", ``},
745-
{"a9", ``},
746-
{a63, ``},
747-
} {
748-
err := IsValidAttributeRelaxedName(test.input)
749-
got := ""
750-
if err != nil {
751-
got = err.Error()
752-
}
753-
if got != test.result {
754-
t.Fatalf("Test: %s\nExp: %s\nGot: %s", test.input, test.result, got)
755-
}
756-
}
757-
758738
// Test map keys
759739
match = RegexpMapKey.String()
760740
for _, test := range []struct {

0 commit comments

Comments
 (0)