Skip to content

Commit b24c062

Browse files
authored
always use single dot fieldpath notation (#261)
This patch completes earlier work that was done in the `pkg/fieldpath` package that only uses a single dot notation to refer to a *field* with type *list* or *map* within a field path. We previously were using a double dot in field paths in `generator.yaml` config files to refer to fields of type list or map. This was erroneous, since referring to the *field* (not the *value* of the field) only requires a single dot in order for the fieldpath to "locate" a field within a path. In this PR, we simplify and DRY up the inference code in `pkg/model/model.go`'s processing of "nested fields" so that the field path added for all Field objects uses this single-dot notation only. Signed-off-by: Jay Pipes <[email protected]> By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent d9d3390 commit b24c062

File tree

6 files changed

+65
-132
lines changed

6 files changed

+65
-132
lines changed

pkg/generate/code/set_sdk.go

+2-4
Original file line numberDiff line numberDiff line change
@@ -1145,16 +1145,14 @@ func setSDKForSlice(
11451145
//
11461146
// f0elem.SetMyField(*f0iter)
11471147
containerFieldName := ""
1148-
sourceAttributePath := sourceFieldPath
11491148
if targetShape.MemberRef.Shape.Type == "structure" {
11501149
containerFieldName = targetFieldName
1151-
sourceAttributePath = sourceFieldPath + "."
11521150
}
11531151
out += setSDKForContainer(
11541152
cfg, r,
11551153
containerFieldName,
11561154
elemVarName,
1157-
sourceAttributePath,
1155+
sourceFieldPath,
11581156
iterVarName,
11591157
&targetShape.MemberRef,
11601158
indentLevel+1,
@@ -1218,7 +1216,7 @@ func setSDKForMap(
12181216
cfg, r,
12191217
containerFieldName,
12201218
valVarName,
1221-
sourceFieldPath+".",
1219+
sourceFieldPath,
12221220
valIterVarName,
12231221
&targetShape.ValueRef,
12241222
indentLevel+1,

pkg/model/field.go

-18
Original file line numberDiff line numberDiff line change
@@ -111,24 +111,6 @@ func (f *Field) IsReference() bool {
111111
return trimmedGoType == "*ackv1alpha1.AWSResourceReferenceWrapper"
112112
}
113113

114-
// ParentFieldPath takes a field path and returns the field path of the
115-
// containing "parent" field. For example, if the field path
116-
// `Users..Credentials.Login` is passed in, this function returns
117-
// `Users..Credentials`. If `Users..Password` is supplied, this function
118-
// returns `Users`, etc.
119-
func ParentFieldPath(path string) string {
120-
parts := strings.Split(path, ".")
121-
// Pop the last element of the supplied field path
122-
parts = parts[0 : len(parts)-1]
123-
// If the parent field's type is a list or map, there will be two dots ".."
124-
// in the supplied field path. We don't want the returned field path to end
125-
// in a dot, since that would be invalid, so we trim it off here
126-
if parts[len(parts)-1] == "" {
127-
parts = parts[0 : len(parts)-1]
128-
}
129-
return strings.Join(parts, ".")
130-
}
131-
132114
// NewReferenceField returns a pointer to a new Field object.
133115
// The go-type of field is either slice of '*AWSResourceReferenceWrapper' or
134116
// '*AWSResourceReferenceWrapper' depending on whether 'shapeRef' parameter

pkg/model/field_test.go

-29
Original file line numberDiff line numberDiff line change
@@ -6,38 +6,9 @@ import (
66
"github.com/stretchr/testify/assert"
77
"github.com/stretchr/testify/require"
88

9-
"github.com/aws-controllers-k8s/code-generator/pkg/model"
109
"github.com/aws-controllers-k8s/code-generator/pkg/testutil"
1110
)
1211

13-
func TestParentFieldPath(t *testing.T) {
14-
assert := assert.New(t)
15-
testCases := []struct {
16-
subject string
17-
want string
18-
}{
19-
{
20-
"Repository.Name",
21-
"Repository",
22-
},
23-
{
24-
"Users..Password",
25-
"Users",
26-
},
27-
{
28-
"User.Credentials..Password",
29-
"User.Credentials",
30-
},
31-
}
32-
33-
for _, tc := range testCases {
34-
result := model.ParentFieldPath(
35-
tc.subject,
36-
)
37-
assert.Equal(tc.want, result)
38-
}
39-
}
40-
4112
func TestMemberFields(t *testing.T) {
4213
assert := assert.New(t)
4314
require := require.New(t)

pkg/model/model.go

+61-79
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ func (m *Model) GetCRDs() ([]*CRD, error) {
295295
// This is the place that we build out the CRD.Fields map with
296296
// `pkg/model.Field` objects that represent the non-top-level Spec and
297297
// Status fields.
298-
m.processNestedFields(crds)
298+
m.processFields(crds)
299299
m.crds = crds
300300
return crds, nil
301301
}
@@ -472,9 +472,10 @@ func replaceSecretAttrGoType(
472472
field *Field,
473473
tdefs []*TypeDef,
474474
) {
475-
fieldPath := field.Path
476-
parentFieldPath := ParentFieldPath(field.Path)
477-
parentField, ok := crd.Fields[parentFieldPath]
475+
fieldPath := ackfp.FromString(field.Path)
476+
parentFieldPath := fieldPath.Copy()
477+
parentFieldPath.Pop()
478+
parentField, ok := crd.Fields[parentFieldPath.String()]
478479
if !ok {
479480
msg := fmt.Sprintf(
480481
"Cannot find parent field at parent path %s for %s",
@@ -546,25 +547,25 @@ func replaceSecretAttrGoType(
546547
attr.GoType = "*ackv1alpha1.SecretKeyReference"
547548
}
548549

549-
// processNestedFields is responsible for walking all of the CRDs' Spec and
550+
// processFields is responsible for walking all of the CRDs' Spec and
550551
// Status fields' Shape objects and adding `pkg/model.Field` objects for all
551552
// nested fields along with that `Field`'s `Config` object that allows us to
552553
// determine if the TypeDef associated with that nested field should have its
553554
// data type overridden (e.g. for SecretKeyReferences)
554-
func (m *Model) processNestedFields(crds []*CRD) {
555+
func (m *Model) processFields(crds []*CRD) {
555556
for _, crd := range crds {
556557
for _, field := range crd.SpecFields {
557-
m.processNestedField(crd, field)
558+
m.processTopLevelField(crd, field)
558559
}
559560
for _, field := range crd.StatusFields {
560-
m.processNestedField(crd, field)
561+
m.processTopLevelField(crd, field)
561562
}
562563
}
563564
}
564565

565-
// processNestedField processes any nested fields (non-scalar fields associated
566+
// processTopLevelField processes any nested fields (non-scalar fields associated
566567
// with the Spec and Status objects)
567-
func (m *Model) processNestedField(
568+
func (m *Model) processTopLevelField(
568569
crd *CRD,
569570
field *Field,
570571
) {
@@ -581,106 +582,87 @@ func (m *Model) processNestedField(
581582
fieldType := fieldShape.Type
582583
switch fieldType {
583584
case "structure":
584-
m.processNestedStructField(crd, field.Path+".", field)
585+
m.processStructField(crd, field.Path+".", field)
585586
case "list":
586-
m.processNestedListField(crd, field.Path+"..", field)
587+
m.processListField(crd, field.Path+".", field)
587588
case "map":
588-
m.processNestedMapField(crd, field.Path+"..", field)
589+
m.processMapField(crd, field.Path+".", field)
589590
}
590591
}
591592
}
592593

593-
// processNestedStructField recurses through the members of a nested field that
594-
// is a struct type and adds any Field objects to the supplied CRD.
595-
func (m *Model) processNestedStructField(
594+
// processField adds a new Field definition for a field within the CR
595+
func (m *Model) processField(
596596
crd *CRD,
597-
baseFieldPath string,
598-
baseField *Field,
597+
parentFieldPath string,
598+
parentField *Field,
599+
fieldName string,
600+
fieldShapeRef *awssdkmodel.ShapeRef,
599601
) {
600602
fieldConfigs := crd.Config().ResourceFields(crd.Names.Original)
601-
baseFieldShape := baseField.ShapeRef.Shape
602-
for memberName, memberRef := range baseFieldShape.MemberRefs {
603-
memberNames := names.New(memberName)
604-
memberShape := memberRef.Shape
605-
memberShapeType := memberShape.Type
606-
fieldPath := baseFieldPath + memberNames.Camel
607-
fieldConfig := fieldConfigs[fieldPath]
608-
field := NewField(crd, fieldPath, memberNames, memberRef, fieldConfig)
609-
switch memberShapeType {
610-
case "structure":
611-
m.processNestedStructField(crd, fieldPath+".", field)
612-
case "list":
613-
m.processNestedListField(crd, fieldPath+"..", field)
614-
case "map":
615-
m.processNestedMapField(crd, fieldPath+"..", field)
616-
}
617-
crd.Fields[fieldPath] = field
603+
fieldNames := names.New(fieldName)
604+
fieldShape := fieldShapeRef.Shape
605+
fieldShapeType := fieldShape.Type
606+
fieldPath := parentFieldPath + fieldNames.Camel
607+
fieldConfig := fieldConfigs[fieldPath]
608+
field := NewField(crd, fieldPath, fieldNames, fieldShapeRef, fieldConfig)
609+
switch fieldShapeType {
610+
case "structure":
611+
m.processStructField(crd, fieldPath+".", field)
612+
case "list":
613+
m.processListField(crd, fieldPath+".", field)
614+
case "map":
615+
m.processMapField(crd, fieldPath+".", field)
616+
}
617+
crd.Fields[fieldPath] = field
618+
}
619+
620+
// processStructField recurses through the members of a nested field that
621+
// is a struct type and adds any Field objects to the supplied CRD.
622+
func (m *Model) processStructField(
623+
crd *CRD,
624+
fieldPath string,
625+
field *Field,
626+
) {
627+
fieldShape := field.ShapeRef.Shape
628+
for memberName, memberRef := range fieldShape.MemberRefs {
629+
m.processField(crd, fieldPath, field, memberName, memberRef)
618630
}
619631
}
620632

621-
// processNestedListField recurses through the members of a nested field that
633+
// processListField recurses through the members of a nested field that
622634
// is a list type that has a struct element type and adds any Field objects to
623635
// the supplied CRD.
624-
func (m *Model) processNestedListField(
636+
func (m *Model) processListField(
625637
crd *CRD,
626-
baseFieldPath string,
627-
baseField *Field,
638+
fieldPath string,
639+
field *Field,
628640
) {
629-
baseFieldShape := baseField.ShapeRef.Shape
630-
elementFieldShape := baseFieldShape.MemberRef.Shape
641+
fieldShape := field.ShapeRef.Shape
642+
elementFieldShape := fieldShape.MemberRef.Shape
631643
if elementFieldShape.Type != "structure" {
632644
return
633645
}
634-
fieldConfigs := crd.Config().ResourceFields(crd.Names.Original)
635646
for memberName, memberRef := range elementFieldShape.MemberRefs {
636-
memberNames := names.New(memberName)
637-
memberShape := memberRef.Shape
638-
memberShapeType := memberShape.Type
639-
fieldPath := baseFieldPath + memberNames.Camel
640-
fieldConfig := fieldConfigs[fieldPath]
641-
field := NewField(crd, fieldPath, memberNames, memberRef, fieldConfig)
642-
switch memberShapeType {
643-
case "structure":
644-
m.processNestedStructField(crd, fieldPath+".", field)
645-
case "list":
646-
m.processNestedListField(crd, fieldPath+"..", field)
647-
case "map":
648-
m.processNestedMapField(crd, fieldPath+"..", field)
649-
}
650-
crd.Fields[fieldPath] = field
647+
m.processField(crd, fieldPath, field, memberName, memberRef)
651648
}
652649
}
653650

654-
// processNestedMapField recurses through the members of a nested field that
651+
// processMapField recurses through the members of a nested field that
655652
// is a map type that has a struct value type and adds any Field objects to
656653
// the supplied CRD.
657-
func (m *Model) processNestedMapField(
654+
func (m *Model) processMapField(
658655
crd *CRD,
659-
baseFieldPath string,
660-
baseField *Field,
656+
fieldPath string,
657+
field *Field,
661658
) {
662-
baseFieldShape := baseField.ShapeRef.Shape
663-
valueFieldShape := baseFieldShape.ValueRef.Shape
659+
fieldShape := field.ShapeRef.Shape
660+
valueFieldShape := fieldShape.ValueRef.Shape
664661
if valueFieldShape.Type != "structure" {
665662
return
666663
}
667-
fieldConfigs := crd.Config().ResourceFields(crd.Names.Original)
668664
for memberName, memberRef := range valueFieldShape.MemberRefs {
669-
memberNames := names.New(memberName)
670-
memberShape := memberRef.Shape
671-
memberShapeType := memberShape.Type
672-
fieldPath := baseFieldPath + memberNames.Camel
673-
fieldConfig := fieldConfigs[fieldPath]
674-
field := NewField(crd, fieldPath, memberNames, memberRef, fieldConfig)
675-
switch memberShapeType {
676-
case "structure":
677-
m.processNestedStructField(crd, fieldPath+".", field)
678-
case "list":
679-
m.processNestedListField(crd, fieldPath+"..", field)
680-
case "map":
681-
m.processNestedMapField(crd, fieldPath+"..", field)
682-
}
683-
crd.Fields[fieldPath] = field
665+
m.processField(crd, fieldPath, field, memberName, memberRef)
684666
}
685667
}
686668

pkg/model/model_mq_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ func TestMQ_Broker(t *testing.T) {
3535
// (which is a `[]*User` type) is findable in the CRD's Fields collection
3636
// by the path `Spec.Users..Password` and that the FieldConfig associated
3737
// with this Field is marked as a SecretKeyReference.
38-
passFieldPath := "Users..Password"
38+
passFieldPath := "Users.Password"
3939
passField, found := crd.Fields[passFieldPath]
4040
require.True(found)
4141
require.NotNil(passField.FieldConfig)

pkg/testdata/models/apis/mq/0000-00-00/generator.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ resources:
1010
sdk_delete_pre_build_request:
1111
template_path: sdk_delete_pre_build_request.go.tpl
1212
fields:
13-
Users..Password:
13+
Users.Password:
1414
is_secret: true

0 commit comments

Comments
 (0)