Skip to content

Commit c7b19a3

Browse files
authored
collect Field structs for members of structures (#258)
When inferring the structure of a CRD and its fields, we create a `pkg/model.Field` struct for each *top-level* field in the CRD's Spec or Status. The `pkg/model.GetCRDs()` function ends up calling `pkg/model.CRD:AddSpecField()` and `pkg/model.CRD:AddStatusField()` for those field definitions that the API inference stage of the generation pipeline discovers in the aws-sdk-go API model. Because we were creating `pkg/model.Field` objects for only the top-level fields in Spec and Status and the `pkg/model.Field` object is what was tied to a `pkg/generate/config.FieldConfig` object, this meant that only the top-level field definitions were able to be *configured* via the generator.yaml file. This is a problem for more complex APIs like EC2 which have resources with top-level fields that are structures themselves. We were not able to configure nested fields properly because we were not actually constructing a `pkg/model.Field` object for these nested fields. This PR enhances the API inference process inside the code generator to detect when a new `pkg/model.Field` refers to an `aws-sdk-go/private/model.Shape` that is of type "structure" and recurse through that Shape's MemberRefs collection, building a map of `pkg/model.Field` objects for each member field along with a fieldpath that allows these nested struct member fields to later be "found" by the code generator. Issue: aws-controllers-k8s/community#1100 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 3e18472 commit c7b19a3

File tree

2 files changed

+56
-0
lines changed

2 files changed

+56
-0
lines changed

pkg/model/field.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ type Field struct {
4747
GoTypeWithPkgName string
4848
ShapeRef *awssdkmodel.ShapeRef
4949
FieldConfig *ackgenconfig.FieldConfig
50+
51+
// MemberFields is a map, keyed by the *renamed, cleaned, camel-cased name* of
52+
// member fields when this Field is a struct type.
53+
MemberFields map[string]*Field
5054
}
5155

5256
// IsRequired checks the FieldConfig for Field and returns if the field is
@@ -162,6 +166,7 @@ func NewField(
162166
shapeRef *awssdkmodel.ShapeRef,
163167
cfg *ackgenconfig.FieldConfig,
164168
) *Field {
169+
memberFields := map[string]*Field{}
165170
var gte, gt, gtwp string
166171
var shape *awssdkmodel.Shape
167172
if shapeRef != nil {
@@ -170,6 +175,15 @@ func NewField(
170175

171176
if shape != nil {
172177
gte, gt, gtwp = CleanGoType(crd.sdkAPI, crd.cfg, shape, cfg)
178+
if shape.Type == "structure" {
179+
// "unpack" the member fields composing this struct field...
180+
for _, memberName := range shape.MemberNames() {
181+
cleanMemberNames := names.New(memberName)
182+
memberPath := path + "." + cleanMemberNames.Camel
183+
memberField := NewField(crd, memberPath, cleanMemberNames, shape.MemberRefs[memberName], cfg)
184+
memberFields[cleanMemberNames.Camel] = memberField
185+
}
186+
}
173187
} else {
174188
gte = "string"
175189
gt = "*string"
@@ -184,5 +198,6 @@ func NewField(
184198
GoTypeElem: gte,
185199
GoTypeWithPkgName: gtwp,
186200
FieldConfig: cfg,
201+
MemberFields: memberFields,
187202
}
188203
}

pkg/model/field_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ import (
44
"testing"
55

66
"github.com/stretchr/testify/assert"
7+
"github.com/stretchr/testify/require"
78

89
"github.com/aws-controllers-k8s/code-generator/pkg/model"
10+
"github.com/aws-controllers-k8s/code-generator/pkg/testutil"
911
)
1012

1113
func TestParentFieldPath(t *testing.T) {
@@ -35,3 +37,42 @@ func TestParentFieldPath(t *testing.T) {
3537
assert.Equal(tc.want, result)
3638
}
3739
}
40+
41+
func TestMemberFields(t *testing.T) {
42+
assert := assert.New(t)
43+
require := require.New(t)
44+
45+
g := testutil.NewModelForService(t, "ec2")
46+
47+
crds, err := g.GetCRDs()
48+
require.Nil(err)
49+
50+
crd := getCRDByName("LaunchTemplate", crds)
51+
require.NotNil(crd)
52+
53+
specFields := crd.SpecFields
54+
55+
// LaunchTemplate.Spec.LaunchTemplateData field is itself a struct field
56+
// with a number of member fields. We are checking here to ensure that the
57+
// LaunchTemplate.Spec.LaunchTemplateData Field definition properly
58+
// gathered the member field information.
59+
ltdField := specFields["LaunchTemplateData"]
60+
require.NotNil(ltdField)
61+
require.Equal(ltdField.Path, "LaunchTemplateData")
62+
require.NotNil(ltdField.ShapeRef)
63+
require.Equal(ltdField.ShapeRef.Shape.Type, "structure")
64+
65+
assert.NotNil(ltdField.MemberFields)
66+
67+
// HibernationOptions is a member of the LaunchTemplateData structure and
68+
// is itself another structure field. Make sure that the Field definition
69+
// for this nested structure member field exists.
70+
hoField := ltdField.MemberFields["HibernationOptions"]
71+
assert.NotNil(hoField)
72+
assert.Equal(hoField.Path, "LaunchTemplateData.HibernationOptions")
73+
assert.NotNil(hoField.MemberFields)
74+
75+
hocField := hoField.MemberFields["Configured"]
76+
assert.NotNil(hocField)
77+
assert.Equal(hocField.Path, "LaunchTemplateData.HibernationOptions.Configured")
78+
}

0 commit comments

Comments
 (0)