Skip to content

Commit 1b70869

Browse files
authored
Merge pull request #45039 from hashicorp/td-test-gen-centralize-ri-parsing
generators: Centralize Resource Identity annotation parsing
2 parents f87c9ec + b497386 commit 1b70869

File tree

10 files changed

+477
-562
lines changed

10 files changed

+477
-562
lines changed

internal/generate/attrconsts/constOrQuote.gtpl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ import (
1212
// Otherwise, it returns the attribute quoted. This is intended for use in
1313
// generated code and templates.
1414
func ConstOrQuote(constant string) string {
15+
if constant == "" {
16+
return ""
17+
}
18+
1519
allConstants := map[string]string{
1620
{{- range .Constants }}
1721
"{{ .Literal }}": "Attr{{ .Constant }}",
Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package common
5+
6+
import (
7+
"errors"
8+
"fmt"
9+
"slices"
10+
"strconv"
11+
"strings"
12+
13+
tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices"
14+
namesgen "github.com/hashicorp/terraform-provider-aws/names/generate"
15+
)
16+
17+
type TriBoolean uint
18+
19+
const (
20+
TriBooleanUnset TriBoolean = iota
21+
TriBooleanTrue
22+
TriBooleanFalse
23+
)
24+
25+
func TriBool(b bool) TriBoolean {
26+
if b {
27+
return TriBooleanTrue
28+
} else {
29+
return TriBooleanFalse
30+
}
31+
}
32+
33+
type Implementation string
34+
35+
const (
36+
ImplementationFramework Implementation = "framework"
37+
ImplementationSDK Implementation = "sdk"
38+
)
39+
40+
type ResourceIdentity struct {
41+
isARNIdentity bool
42+
isCustomInherentRegionIdentity bool
43+
isSingletonIdentity bool
44+
identityAttributeName string
45+
IdentityDuplicateAttrNames []string
46+
IdentityAttributes []IdentityAttribute
47+
MutableIdentity bool
48+
IdentityVersion int64
49+
SDKv2IdentityUpgraders []string
50+
CustomInherentRegionParser string
51+
HasV6_0NullValuesError bool
52+
HasV6_0RefreshError bool
53+
}
54+
55+
func (r ResourceIdentity) HasResourceIdentity() bool {
56+
return r.IsParameterizedIdentity() || r.isARNIdentity || r.isSingletonIdentity || r.isCustomInherentRegionIdentity
57+
}
58+
59+
func (r ResourceIdentity) HasInherentRegionIdentity() bool {
60+
return r.isARNIdentity || r.isCustomInherentRegionIdentity
61+
}
62+
63+
func (r ResourceIdentity) IsARNIdentity() bool {
64+
return r.isARNIdentity
65+
}
66+
67+
func (r ResourceIdentity) IsCustomInherentRegionIdentity() bool {
68+
return r.isCustomInherentRegionIdentity
69+
}
70+
71+
func (r ResourceIdentity) IsParameterizedIdentity() bool {
72+
return len(r.IdentityAttributes) > 0
73+
}
74+
75+
func (r ResourceIdentity) IsSingletonIdentity() bool {
76+
return r.isSingletonIdentity
77+
}
78+
79+
func (r ResourceIdentity) IdentityAttribute() string {
80+
return namesgen.ConstOrQuote(r.IdentityAttributeName())
81+
}
82+
83+
func (r ResourceIdentity) IdentityAttributeName() string {
84+
return r.identityAttributeName
85+
}
86+
87+
func (r ResourceIdentity) HasIdentityDuplicateAttrs() bool {
88+
return len(r.IdentityDuplicateAttrNames) > 0
89+
}
90+
91+
func (r ResourceIdentity) IdentityDuplicateAttrs() []string {
92+
return tfslices.ApplyToAll(r.IdentityDuplicateAttrNames, func(s string) string {
93+
return namesgen.ConstOrQuote(s)
94+
})
95+
}
96+
97+
type IdentityAttribute struct {
98+
Name_ string
99+
Optional bool
100+
ResourceAttributeName_ string
101+
TestNotNull bool
102+
}
103+
104+
func (a IdentityAttribute) Name() string {
105+
return namesgen.ConstOrQuote(a.Name_)
106+
}
107+
108+
func (a IdentityAttribute) ResourceAttributeName() string {
109+
return namesgen.ConstOrQuote(a.ResourceAttributeName_)
110+
}
111+
112+
func ParseResourceIdentity(annotationName string, args Args, implementation Implementation, d *ResourceIdentity, goImports *[]GoImport) error {
113+
switch annotationName {
114+
case "ArnIdentity":
115+
d.isARNIdentity = true
116+
if len(args.Positional) == 0 {
117+
d.identityAttributeName = "arn"
118+
} else {
119+
d.identityAttributeName = args.Positional[0]
120+
}
121+
122+
parseIdentityDuplicateAttrNames(args, implementation, d)
123+
124+
case "CustomInherentRegionIdentity":
125+
d.isCustomInherentRegionIdentity = true
126+
127+
if len(args.Positional) < 2 {
128+
return errors.New("CustomInherentRegionIdentity missing required parameters")
129+
}
130+
131+
d.identityAttributeName = args.Positional[0]
132+
133+
parseIdentityDuplicateAttrNames(args, implementation, d)
134+
135+
attr := args.Positional[1]
136+
if funcName, importSpec, err := ParseIdentifierSpec(attr); err != nil {
137+
return fmt.Errorf("%q: %w", attr, err)
138+
} else {
139+
d.CustomInherentRegionParser = funcName
140+
if importSpec != nil {
141+
*goImports = append(*goImports, *importSpec)
142+
}
143+
}
144+
145+
case "IdentityAttribute":
146+
if len(args.Positional) == 0 {
147+
return errors.New("no Identity attribute name")
148+
}
149+
150+
identityAttribute := IdentityAttribute{
151+
Name_: args.Positional[0],
152+
}
153+
154+
if attr, ok := args.Keyword["optional"]; ok {
155+
if b, err := ParseBoolAttr("optional", attr); err != nil {
156+
return err
157+
} else {
158+
identityAttribute.Optional = b
159+
}
160+
}
161+
162+
if attr, ok := args.Keyword["resourceAttributeName"]; ok {
163+
identityAttribute.ResourceAttributeName_ = attr
164+
}
165+
166+
if attr, ok := args.Keyword["testNotNull"]; ok {
167+
if b, err := ParseBoolAttr("testNotNull", attr); err != nil {
168+
return err
169+
} else {
170+
identityAttribute.TestNotNull = b
171+
}
172+
}
173+
174+
d.IdentityAttributes = append(d.IdentityAttributes, identityAttribute)
175+
176+
case "IdentityVersion":
177+
attr := args.Positional[0]
178+
if i, err := strconv.ParseInt(attr, 10, 64); err != nil {
179+
return fmt.Errorf("invalid IdentityVersion value: %q. Should be integer value.", attr)
180+
} else {
181+
d.IdentityVersion = i
182+
}
183+
184+
if attr, ok := args.Keyword["sdkV2IdentityUpgraders"]; ok {
185+
attrs := strings.Split(attr, ";")
186+
d.SDKv2IdentityUpgraders = attrs
187+
}
188+
189+
case "MutableIdentity":
190+
d.MutableIdentity = true
191+
192+
case "SingletonIdentity":
193+
d.isSingletonIdentity = true
194+
195+
// FIXME: Not actually for Global, but the value is never used
196+
d.identityAttributeName = "region"
197+
198+
parseIdentityDuplicateAttrNames(args, implementation, d)
199+
200+
// TODO: allow underscore?
201+
case "V60SDKv2Fix":
202+
d.HasV6_0NullValuesError = true
203+
204+
if attr, ok := args.Keyword["v60RefreshError"]; ok {
205+
if b, err := ParseBoolAttr("v60RefreshError", attr); err != nil {
206+
return err
207+
} else {
208+
d.HasV6_0RefreshError = b
209+
}
210+
}
211+
}
212+
213+
return nil
214+
}
215+
216+
type GoImport struct {
217+
Path string
218+
Alias string
219+
}
220+
221+
func ParseIdentifierSpec(s string) (string, *GoImport, error) {
222+
parts := strings.Split(s, ";")
223+
switch len(parts) {
224+
case 1:
225+
return parts[0], nil, nil
226+
227+
case 2:
228+
return parts[1], &GoImport{
229+
Path: parts[0],
230+
}, nil
231+
232+
case 3:
233+
return parts[2], &GoImport{
234+
Path: parts[0],
235+
Alias: parts[1],
236+
}, nil
237+
238+
default:
239+
return "", nil, fmt.Errorf("invalid generator value: %q", s)
240+
}
241+
}
242+
243+
func ParseBoolAttr(name, value string) (bool, error) {
244+
if b, err := strconv.ParseBool(value); err != nil {
245+
return b, fmt.Errorf("invalid %s value %q: Should be boolean value.", name, value)
246+
} else {
247+
return b, nil
248+
}
249+
}
250+
251+
func parseIdentityDuplicateAttrNames(args Args, implementation Implementation, d *ResourceIdentity) {
252+
var attrs []string
253+
if attr, ok := args.Keyword["identityDuplicateAttributes"]; ok {
254+
attrs = strings.Split(attr, ";")
255+
}
256+
if implementation == ImplementationSDK {
257+
attrs = append(attrs, "id")
258+
}
259+
260+
// Sort `id` to first position, the rest alphabetically
261+
slices.SortFunc(attrs, func(a, b string) int {
262+
if a == "id" {
263+
return -1
264+
} else if b == "id" {
265+
return 1
266+
} else {
267+
return strings.Compare(a, b)
268+
}
269+
})
270+
d.IdentityDuplicateAttrNames = slices.Compact(attrs)
271+
}

0 commit comments

Comments
 (0)