Skip to content

Commit 47e9bc8

Browse files
authored
Corrected metadata generation for Map fields (#15960)
1 parent f62dc03 commit 47e9bc8

File tree

6 files changed

+145
-147
lines changed

6 files changed

+145
-147
lines changed

mmv1/api/metadata/field.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,28 @@
11
package metadata
22

33
import (
4+
"slices"
45
"strings"
56

67
"github.com/GoogleCloudPlatform/magic-modules/mmv1/api"
78
"github.com/GoogleCloudPlatform/magic-modules/mmv1/google"
89
)
910

1011
func FromProperties(props []*api.Type) []Field {
12+
// Sort props by lineage
13+
slices.SortFunc(props, func(a, b *api.Type) int {
14+
if strings.Join(a.Lineage(), ".") < strings.Join(b.Lineage(), ".") {
15+
return -1
16+
}
17+
return 1
18+
})
19+
1120
var fields []Field
1221
for _, p := range props {
22+
// Skip non-maps with nested fields
23+
if !p.IsA("Map") && len(p.NestedProperties()) > 0 {
24+
continue
25+
}
1326
f := Field{
1427
Json: p.IsJsonField(),
1528
ProviderOnly: p.ProviderOnly(),
@@ -22,6 +35,13 @@ func FromProperties(props []*api.Type) []Field {
2235
if p.ProviderOnly() || !IsDefaultLineage(lineage, apiLineage) {
2336
f.Field = strings.Join(lineage, ".")
2437
}
38+
// For maps (which all have nested children), modify the entry slightly; the map field itself is skipped,
39+
// but we need a `key` API field that corresponds to the key_name of the map field.
40+
if p.IsA("Map") {
41+
f.ApiField += ".key"
42+
f.Field = strings.Join(append(lineage, p.KeyName), ".")
43+
}
44+
2545
fields = append(fields, f)
2646
}
2747
return fields

mmv1/api/metadata/field_test.go

Lines changed: 115 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@ import (
44
"testing"
55

66
"github.com/GoogleCloudPlatform/magic-modules/mmv1/api"
7+
"github.com/GoogleCloudPlatform/magic-modules/mmv1/google"
78
"github.com/google/go-cmp/cmp"
89
)
910

1011
func TestFromProperties(t *testing.T) {
1112
cases := []struct {
1213
name string
1314
resourceMetadata *api.Resource
15+
virtualFields []*api.Type
16+
parameters []*api.Type
1417
properties []*api.Type
1518
wantFields []Field
1619
}{
@@ -59,6 +62,113 @@ func TestFromProperties(t *testing.T) {
5962
},
6063
},
6164
},
65+
{
66+
name: "nested field",
67+
properties: []*api.Type{
68+
{
69+
Name: "root",
70+
Type: "NestedObject",
71+
Properties: []*api.Type{
72+
{
73+
Name: "foo",
74+
Type: "NestedObject",
75+
Properties: []*api.Type{
76+
{
77+
Name: "bars",
78+
Type: "Array",
79+
ItemType: &api.Type{
80+
Type: "NestedObject",
81+
Properties: []*api.Type{
82+
{
83+
Name: "fooBar",
84+
Type: "String",
85+
},
86+
},
87+
},
88+
},
89+
},
90+
},
91+
},
92+
},
93+
},
94+
wantFields: []Field{
95+
{
96+
ApiField: "root.foo.bars.fooBar",
97+
},
98+
},
99+
},
100+
{
101+
name: "nested virtual",
102+
virtualFields: []*api.Type{
103+
{
104+
Name: "root",
105+
Type: "NestedObject",
106+
Properties: []*api.Type{
107+
{
108+
Name: "foo",
109+
Type: "String",
110+
},
111+
},
112+
},
113+
},
114+
wantFields: []Field{
115+
{
116+
Field: "root.foo",
117+
ProviderOnly: true,
118+
},
119+
},
120+
},
121+
{
122+
name: "nested param",
123+
parameters: []*api.Type{
124+
{
125+
Name: "root",
126+
Type: "NestedObject",
127+
Properties: []*api.Type{
128+
{
129+
Name: "foo",
130+
Type: "String",
131+
UrlParamOnly: true,
132+
},
133+
},
134+
},
135+
},
136+
wantFields: []Field{
137+
{
138+
Field: "root.foo",
139+
ProviderOnly: true,
140+
},
141+
},
142+
},
143+
{
144+
name: "map",
145+
properties: []*api.Type{
146+
{
147+
Name: "root",
148+
Type: "Map",
149+
KeyName: "whatever",
150+
ValueType: &api.Type{
151+
Type: "NestedObject",
152+
Properties: []*api.Type{
153+
{
154+
Name: "foo",
155+
Type: "String",
156+
},
157+
},
158+
},
159+
},
160+
},
161+
wantFields: []Field{
162+
{
163+
Field: "root.whatever",
164+
ApiField: "root.key",
165+
},
166+
{
167+
Field: "root.foo",
168+
ApiField: "root.value.foo",
169+
},
170+
},
171+
},
62172
}
63173

64174
for _, tc := range cases {
@@ -69,12 +179,12 @@ func TestFromProperties(t *testing.T) {
69179
if r == nil {
70180
r = &api.Resource{}
71181
}
182+
r.VirtualFields = tc.virtualFields
183+
r.Parameters = tc.parameters
184+
r.Properties = tc.properties
185+
r.SetDefault(&api.Product{})
72186

73-
for _, p := range tc.properties {
74-
p.SetDefault(r)
75-
}
76-
77-
got := FromProperties(tc.properties)
187+
got := FromProperties(r.AllNestedProperties(google.Concat(r.RootProperties(), r.UserVirtualFields())))
78188
if diff := cmp.Diff(tc.wantFields, got); diff != "" {
79189
t.Errorf("FromProperties() mismatch (-want +got):\n%s", diff)
80190
}

mmv1/api/metadata/metadata.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package metadata
22

33
import (
44
"github.com/GoogleCloudPlatform/magic-modules/mmv1/api"
5+
"github.com/GoogleCloudPlatform/magic-modules/mmv1/google"
56
)
67

78
// FromResource returns a Metadata object based on a Resource.
@@ -16,7 +17,7 @@ func FromResource(r api.Resource) Metadata {
1617
CaiAssetNameFormat: r.CAIFormatOverride(),
1718
ApiVariantPatterns: r.ApiVariantPatterns,
1819
AutogenStatus: r.AutogenStatus != "",
19-
Fields: FromProperties(r.LeafProperties()),
20+
Fields: FromProperties(r.AllNestedProperties(google.Concat(r.RootProperties(), r.UserVirtualFields()))),
2021
}
2122

2223
if m.ApiVersion == "" {

mmv1/api/resource.go

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -752,28 +752,6 @@ func (r Resource) RootProperties() []*Type {
752752
return props
753753
}
754754

755-
// Returns a sorted list of all "leaf" properties, meaning properties that have
756-
// no children.
757-
func (r Resource) LeafProperties() []*Type {
758-
types := r.AllNestedProperties(google.Concat(r.RootProperties(), r.UserVirtualFields()))
759-
760-
// Remove types that have children, because we only want "leaf" fields
761-
types = slices.DeleteFunc(types, func(t *Type) bool {
762-
nestedProperties := t.NestedProperties()
763-
return len(nestedProperties) > 0
764-
})
765-
766-
// Sort types by lineage
767-
slices.SortFunc(types, func(a, b *Type) int {
768-
if strings.Join(a.Lineage(), ".") < strings.Join(b.Lineage(), ".") {
769-
return -1
770-
}
771-
return 1
772-
})
773-
774-
return types
775-
}
776-
777755
// Return the product-level async object, or the resource-specific one
778756
// if one exists.
779757
func (r Resource) GetAsync() *Async {

mmv1/api/resource_test.go

Lines changed: 0 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -204,123 +204,6 @@ func TestResourceServiceVersion(t *testing.T) {
204204
}
205205
}
206206

207-
func TestLeafProperties(t *testing.T) {
208-
t.Parallel()
209-
210-
cases := []struct {
211-
description string
212-
obj Resource
213-
expected Type
214-
}{
215-
{
216-
description: "non-nested type",
217-
obj: Resource{
218-
BaseUrl: "test",
219-
Properties: []*Type{
220-
{
221-
Name: "basic",
222-
Type: "String",
223-
},
224-
},
225-
},
226-
expected: Type{
227-
Name: "basic",
228-
},
229-
},
230-
{
231-
description: "nested type",
232-
obj: Resource{
233-
BaseUrl: "test",
234-
Properties: []*Type{
235-
{
236-
Name: "root",
237-
Type: "NestedObject",
238-
Properties: []*Type{
239-
{
240-
Name: "foo",
241-
Type: "NestedObject",
242-
Properties: []*Type{
243-
{
244-
Name: "bars",
245-
Type: "Array",
246-
ItemType: &Type{
247-
Type: "NestedObject",
248-
Properties: []*Type{
249-
{
250-
Name: "fooBar",
251-
Type: "String",
252-
},
253-
},
254-
},
255-
},
256-
},
257-
},
258-
},
259-
},
260-
},
261-
},
262-
expected: Type{
263-
Name: "fooBar",
264-
},
265-
},
266-
{
267-
description: "nested virtual",
268-
obj: Resource{
269-
BaseUrl: "test",
270-
VirtualFields: []*Type{
271-
{
272-
Name: "root",
273-
Type: "NestedObject",
274-
Properties: []*Type{
275-
{
276-
Name: "foo",
277-
Type: "String",
278-
},
279-
},
280-
},
281-
},
282-
},
283-
expected: Type{
284-
Name: "foo",
285-
},
286-
},
287-
{
288-
description: "nested param",
289-
obj: Resource{
290-
BaseUrl: "test",
291-
Parameters: []*Type{
292-
{
293-
Name: "root",
294-
Type: "NestedObject",
295-
Properties: []*Type{
296-
{
297-
Name: "foo",
298-
Type: "String",
299-
},
300-
},
301-
},
302-
},
303-
},
304-
expected: Type{
305-
Name: "foo",
306-
},
307-
},
308-
}
309-
310-
for _, tc := range cases {
311-
tc := tc
312-
313-
t.Run(tc.description, func(t *testing.T) {
314-
t.Parallel()
315-
316-
tc.obj.SetDefault(nil)
317-
if got, want := tc.obj.LeafProperties(), tc.expected; got[0].Name != want.Name {
318-
t.Errorf("expected %q to be %q", got[0].Name, want.Name)
319-
}
320-
})
321-
}
322-
}
323-
324207
// TestMagicianLocation verifies that the current package is being executed from within
325208
// the RELATIVE_MAGICIAN_LOCATION ("mmv1/") directory structure. This ensures that references
326209
// to files relative to this location will remain valid even if the repository structure

mmv1/api/type.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -531,8 +531,8 @@ func (t Type) Lineage() []string {
531531
return []string{google.Underscore(t.Name)}
532532
}
533533

534-
// Skip arrays because otherwise the array will be included twice
535-
if t.ParentMetadata.IsA("Array") {
534+
// Skip arrays & maps because otherwise the parent field name will be duplicated
535+
if t.ParentMetadata.IsA("Array") || t.ParentMetadata.IsA("Map") {
536536
return t.ParentMetadata.Lineage()
537537
}
538538

@@ -555,6 +555,12 @@ func (t Type) ApiLineage() []string {
555555
return t.ParentMetadata.ApiLineage()
556556
}
557557

558+
// Insert `value` for children of Map fields, and exclude this type because
559+
// it will have the same Name as the parent field.
560+
if t.ParentMetadata.IsA("Map") {
561+
return append(t.ParentMetadata.ApiLineage(), "value")
562+
}
563+
558564
return append(t.ParentMetadata.ApiLineage(), t.ApiName)
559565
}
560566

0 commit comments

Comments
 (0)