Skip to content

Commit bbbc11e

Browse files
committed
unpack struct element/value fields of list/map
A previous commit added functionality to the API/resource inference process to "unpack" a struct field's member fields into the `pkg/model.Field.MemberFields` map. However, I had failed to account for lists or maps of structs. This patch accounts for the list or map of structs fields, stepping into the list field's element struct type and adding a Field definition to the containing Field's `MemberFields` map for each member field in the element struct type. Same for containing shapes of type "map". With this patch, we're now able to "access" via field path any nested field (including its corresponding `FieldConfig` object) which means that in following patches we can finally resolve the "different Go types for nested fields" problem we've had in the code generator. Related issue: aws-controllers-k8s/community#1065 Signed-off-by: Jay Pipes <[email protected]>
1 parent 0e45f8d commit bbbc11e

File tree

4 files changed

+100
-9
lines changed

4 files changed

+100
-9
lines changed

pkg/model/field.go

+36-3
Original file line numberDiff line numberDiff line change
@@ -154,15 +154,48 @@ func NewField(
154154
if shapeRef != nil {
155155
shape = shapeRef.Shape
156156
}
157+
// this is a pointer to the "parent" containing Shape when the field being
158+
// processed here is a structure or a list/map of structures.
159+
var containerShape *awssdkmodel.Shape = shape
157160

158161
if shape != nil {
159162
gte, gt, gtwp = CleanGoType(crd.sdkAPI, crd.cfg, shape, cfg)
160-
if shape.Type == "structure" {
163+
for {
164+
// If the field is a slice or map of structs, we want to add
165+
// MemberFields that describe the list or value struct elements so
166+
// that a field path can be used to "find" nested struct member
167+
// fields.
168+
//
169+
// For example, the EC2 resource DHCPOptions has a Field called
170+
// DHCPConfigurations which is of type []*NewDHCPConfiguration
171+
// where the NewDHCPConfiguration struct contains two fields, Key
172+
// and Values. If we want to be able to refer to the
173+
// DHCPOptions.DHCPConfigurations.Values field by field path, we
174+
// need a Field.MemberField that describes the
175+
// NewDHCPConfiguration.Values field.
176+
//
177+
// Here, we essentially dive down into list or map fields,
178+
// searching for whether the list or map fields have structure list
179+
// element or value element types and then rely on the code below
180+
// to "unpack" those struct member fields.
181+
if containerShape.Type == "list" {
182+
containerShape = containerShape.MemberRef.Shape
183+
continue
184+
} else if containerShape.Type == "map" {
185+
containerShape = containerShape.ValueRef.Shape
186+
continue
187+
}
188+
break
189+
}
190+
if containerShape.Type == "structure" {
161191
// "unpack" the member fields composing this struct field...
162-
for _, memberName := range shape.MemberNames() {
192+
for _, memberName := range containerShape.MemberNames() {
163193
cleanMemberNames := names.New(memberName)
164194
memberPath := path + "." + cleanMemberNames.Camel
165-
memberField := NewField(crd, memberPath, cleanMemberNames, shape.MemberRefs[memberName], cfg)
195+
memberShape := containerShape.MemberRefs[memberName]
196+
memberField := NewField(
197+
crd, memberPath, cleanMemberNames, memberShape, cfg,
198+
)
166199
memberFields[cleanMemberNames.Camel] = memberField
167200
}
168201
}

pkg/model/field_test.go

+59-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func TestMemberFields(t *testing.T) {
3333
require.NotNil(ltdField.ShapeRef)
3434
require.Equal(ltdField.ShapeRef.Shape.Type, "structure")
3535

36-
assert.NotNil(ltdField.MemberFields)
36+
require.NotNil(ltdField.MemberFields)
3737

3838
// HibernationOptions is a member of the LaunchTemplateData structure and
3939
// is itself another structure field. Make sure that the Field definition
@@ -44,6 +44,63 @@ func TestMemberFields(t *testing.T) {
4444
assert.NotNil(hoField.MemberFields)
4545

4646
hocField := hoField.MemberFields["Configured"]
47-
assert.NotNil(hocField)
47+
require.NotNil(hocField)
4848
assert.Equal(hocField.Path, "LaunchTemplateData.HibernationOptions.Configured")
4949
}
50+
51+
func TestMemberFields_Containers_ListOfStruct(t *testing.T) {
52+
assert := assert.New(t)
53+
require := require.New(t)
54+
55+
g := testutil.NewModelForService(t, "ec2")
56+
57+
crds, err := g.GetCRDs()
58+
require.Nil(err)
59+
60+
crd := getCRDByName("DHCPOptions", crds)
61+
require.NotNil(crd)
62+
63+
// The DHCPOptions resource has a DHCPConfigurations field that is of type
64+
// []*NewDHCPConfiguration. Here, we test to make sure that the inference
65+
// process deduced the DHCPOptions.DHCPConfigurations Field object's Values
66+
// Field properly.
67+
dhcpConfsField := crd.Fields["DHCPConfigurations"]
68+
require.NotNil(dhcpConfsField)
69+
70+
require.NotNil(dhcpConfsField.MemberFields)
71+
valuesField := dhcpConfsField.MemberFields["Values"]
72+
require.NotNil(valuesField)
73+
require.NotNil(valuesField.ShapeRef)
74+
require.NotNil(valuesField.ShapeRef.Shape)
75+
assert.Equal(valuesField.ShapeRef.Shape.Type, "list")
76+
assert.Equal(valuesField.Path, "DHCPConfigurations.Values")
77+
}
78+
79+
func TestMemberFields_Containers_MapOfStruct(t *testing.T) {
80+
assert := assert.New(t)
81+
require := require.New(t)
82+
83+
g := testutil.NewModelForService(t, "sagemaker")
84+
85+
crds, err := g.GetCRDs()
86+
require.Nil(err)
87+
88+
crd := getCRDByName("TrialComponent", crds)
89+
require.NotNil(crd)
90+
91+
// The TrialComponent resource has an InputArtifacts field that is of type
92+
// map[string]*TrialComponentArtifact. TrialComponentArtifact is a struct
93+
// with two member fields, Value and MediaType. Here, we test to make sure
94+
// that the inference process deduced the TrialComponent.InputArtifacts
95+
// Field object's Value Field properly.
96+
inArtifactsField := crd.Fields["InputArtifacts"]
97+
require.NotNil(inArtifactsField)
98+
99+
require.NotNil(inArtifactsField.MemberFields)
100+
valueField := inArtifactsField.MemberFields["Value"]
101+
require.NotNil(valueField)
102+
require.NotNil(valueField.ShapeRef)
103+
require.NotNil(valueField.ShapeRef.Shape)
104+
assert.Equal(valueField.ShapeRef.Shape.Type, "string")
105+
assert.Equal(valueField.Path, "InputArtifacts.Value")
106+
}

pkg/model/util_test.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ package model_test
1515

1616
import (
1717
"sort"
18+
"strings"
1819

1920
ackmodel "github.com/aws-controllers-k8s/code-generator/pkg/model"
2021
)
@@ -30,7 +31,7 @@ func attrCamelNames(fields map[string]*ackmodel.Field) []string {
3031

3132
func getCRDByName(name string, crds []*ackmodel.CRD) *ackmodel.CRD {
3233
for _, c := range crds {
33-
if c.Names.Original == name {
34+
if strings.EqualFold(c.Names.Original, name) {
3435
return c
3536
}
3637
}
@@ -39,7 +40,7 @@ func getCRDByName(name string, crds []*ackmodel.CRD) *ackmodel.CRD {
3940

4041
func getTypeDefByName(name string, tdefs []*ackmodel.TypeDef) *ackmodel.TypeDef {
4142
for _, td := range tdefs {
42-
if td.Names.Original == name {
43+
if strings.EqualFold(td.Names.Original, name) {
4344
return td
4445
}
4546
}

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,10 @@ ignore:
6565
- Project
6666
# TrainingJob
6767
- TransformJob
68-
- TrialComponent
68+
#- TrialComponent
6969
- Trial
7070
- UserProfile
7171
- Workforce
7272
- Workteam
7373
shape_names:
74-
- TagList
74+
- TagList

0 commit comments

Comments
 (0)