Skip to content

Commit be9e3ab

Browse files
authored
Implement ReadMany operation for SetSDK generator function (#156)
Issue #, if available: [#891](aws-controllers-k8s/community#891) Description of changes: * adds `setSDKReadMany` func to SetSDK * adds unit test Testing: * `make test` ✅ * `make build-controller` ✅ ``` // newListRequestPayload returns SDK-specific struct for the HTTP request // payload of the List API call for the resource func (rm *resourceManager) newListRequestPayload( r *resource, ) (*svcsdk.DescribeVpcsInput, error) { res := &svcsdk.DescribeVpcsInput{} if r.ko.Spec.DryRun != nil { res.SetDryRun(*r.ko.Spec.DryRun) } if r.ko.Status.VPCID != nil { f4 := []*string{} f4 = append(f4, r.ko.Status.VPCID) res.SetVpcIds(f4) } return res, nil } ``` By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent 23278de commit be9e3ab

File tree

3 files changed

+175
-2
lines changed

3 files changed

+175
-2
lines changed

pkg/generate/code/set_sdk.go

Lines changed: 151 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@ import (
1919
"strings"
2020

2121
awssdkmodel "github.com/aws/aws-sdk-go/private/model/api"
22+
"github.com/gertd/go-pluralize"
2223

2324
ackgenconfig "github.com/aws-controllers-k8s/code-generator/pkg/generate/config"
2425
"github.com/aws-controllers-k8s/code-generator/pkg/model"
2526
"github.com/aws-controllers-k8s/code-generator/pkg/names"
27+
"github.com/aws-controllers-k8s/code-generator/pkg/util"
2628
)
2729

2830
// SetSDK returns the Go code that sets an SDK input shape's member fields from
@@ -98,6 +100,8 @@ func SetSDK(
98100
op = r.Ops.ReadOne
99101
case model.OpTypeList:
100102
op = r.Ops.ReadMany
103+
return setSDKReadMany(cfg, r, op,
104+
sourceVarName, targetVarName, indentLevel)
101105
case model.OpTypeUpdate:
102106
op = r.Ops.Update
103107
case model.OpTypeDelete:
@@ -718,6 +722,151 @@ func SetSDKSetAttributes(
718722
return out
719723
}
720724

725+
// setSDKReadMany is a special-case handling of those APIs where there is no
726+
// ReadOne operation and instead the only way to grab information for a single
727+
// object is to call the ReadMany/List operation with one of more filtering
728+
// fields-- specifically identifier(s). This method populates this identifier
729+
// field with the identifier shared between the shape and the CR. Note, in the
730+
// case of multiple matching identifiers, the identifier field containing 'Id'
731+
// will be the only field populated.
732+
//
733+
// As an example, DescribeVpcs EC2 API call doesn't have a ReadOne operation or
734+
// required fields. However, the input shape VpcIds field can be populated using
735+
// a VpcId, a field in the VPC CR's Status. Therefore, populate VpcIds field
736+
// with the *single* VpcId value to ensure the returned array from the API call
737+
// consists only of the desired Vpc.
738+
//
739+
// Sample Output:
740+
//
741+
// if r.ko.Status.VPCID != nil {
742+
// f4 := []*string{}
743+
// f4 = append(f4, r.ko.Status.VPCID)
744+
// res.SetVpcIds(f4)
745+
// }
746+
func setSDKReadMany(
747+
cfg *ackgenconfig.Config,
748+
r *model.CRD,
749+
op *awssdkmodel.Operation,
750+
sourceVarName string,
751+
targetVarName string,
752+
indentLevel int,
753+
) string {
754+
inputShape := op.InputRef.Shape
755+
if inputShape == nil {
756+
return ""
757+
}
758+
759+
out := "\n"
760+
indent := strings.Repeat("\t", indentLevel)
761+
762+
resVarPath := ""
763+
pluralize := pluralize.NewClient()
764+
opConfig, override := cfg.OverrideValues(op.Name)
765+
shapeIdentifiers := FindIdentifiersInShape(r, inputShape)
766+
var err error
767+
for memberIndex, memberName := range inputShape.MemberNames() {
768+
if override {
769+
value, ok := opConfig[memberName]
770+
memberShapeRef, _ := inputShape.MemberRefs[memberName]
771+
memberShape := memberShapeRef.Shape
772+
if ok {
773+
switch memberShape.Type {
774+
case "boolean", "integer":
775+
case "string":
776+
value = "\"" + value + "\""
777+
default:
778+
panic(fmt.Sprintf("Unsupported shape type %s in "+
779+
"generate.code.setSDKReadMany", memberShape.Type))
780+
}
781+
782+
out += fmt.Sprintf("%s%s.Set%s(%s)\n", indent, targetVarName, memberName, value)
783+
continue
784+
}
785+
}
786+
787+
// Field renames are handled in getSanitizedMemberPath
788+
resVarPath, err = getSanitizedMemberPath(memberName, r, op, sourceVarName)
789+
if err != nil {
790+
// if it's an identifier field check for singular/plural
791+
if util.InStrings(memberName, shapeIdentifiers) {
792+
var flipped string
793+
if pluralize.IsPlural(memberName) {
794+
flipped = pluralize.Singular(memberName)
795+
} else {
796+
flipped = pluralize.Plural(memberName)
797+
}
798+
// If there are multiple identifiers, then prioritize the
799+
// 'Id' identifier.
800+
if resVarPath == "" || (!strings.HasSuffix(resVarPath, "Id") ||
801+
!strings.HasSuffix(resVarPath, "Ids")) {
802+
resVarPath, err = getSanitizedMemberPath(flipped, r, op, sourceVarName)
803+
if err != nil {
804+
panic(fmt.Sprintf(
805+
"Unable to locate identifier field %s in "+
806+
"%s Spec/Status in generate.code.setSDKReadMany", flipped, r.Kind))
807+
}
808+
}
809+
} else {
810+
// TODO(jaypipes): check generator config for exceptions?
811+
continue
812+
}
813+
}
814+
815+
memberShapeRef, _ := inputShape.MemberRefs[memberName]
816+
memberShape := memberShapeRef.Shape
817+
out += fmt.Sprintf(
818+
"%sif %s != nil {\n", indent, resVarPath,
819+
)
820+
821+
switch memberShape.Type {
822+
case "list":
823+
// Expecting slice of identifiers
824+
memberVarName := fmt.Sprintf("f%d", memberIndex)
825+
// f0 := []*string{}
826+
out += varEmptyConstructorSDKType(
827+
cfg, r,
828+
memberVarName,
829+
memberShape,
830+
indentLevel+1,
831+
)
832+
833+
// f0 = append(f0, sourceVarName)
834+
out += fmt.Sprintf("%s\t%s = append(%s, %s)\n", indent,
835+
memberVarName, memberVarName, resVarPath)
836+
837+
// res.SetIds(f0)
838+
out += setSDKForScalar(
839+
cfg, r,
840+
memberName,
841+
targetVarName,
842+
inputShape.Type,
843+
sourceVarName,
844+
memberVarName,
845+
memberShapeRef,
846+
indentLevel+1,
847+
)
848+
default:
849+
// For ReadMany that have a singular identifier field.
850+
// ex: DescribeReplicationGroups
851+
out += setSDKForScalar(
852+
cfg, r,
853+
memberName,
854+
targetVarName,
855+
inputShape.Type,
856+
sourceVarName,
857+
resVarPath,
858+
memberShapeRef,
859+
indentLevel+1,
860+
)
861+
}
862+
out += fmt.Sprintf(
863+
"%s}\n", indent,
864+
)
865+
}
866+
867+
return out
868+
}
869+
721870
// setSDKForContainer returns a string of Go code that sets the value of a
722871
// target variable to that of a source variable. When the source variable type
723872
// is a map, struct or slice type, then this function is called recursively on
@@ -969,7 +1118,7 @@ func SetSDKForStruct(
9691118
}
9701119

9711120
// setSDKForSlice returns a string of Go code that sets a target variable value
972-
// to a source variable when the type of the source variable is a struct.
1121+
// to a source variable when the type of the source variable is a slice.
9731122
func setSDKForSlice(
9741123
cfg *ackgenconfig.Config,
9751124
r *model.CRD,
@@ -1034,7 +1183,7 @@ func setSDKForSlice(
10341183
}
10351184

10361185
// setSDKForMap returns a string of Go code that sets a target variable value
1037-
// to a source variable when the type of the source variable is a struct.
1186+
// to a source variable when the type of the source variable is a map.
10381187
func setSDKForMap(
10391188
cfg *ackgenconfig.Config,
10401189
r *model.CRD,

pkg/generate/code/set_sdk_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1778,3 +1778,25 @@ func TestSetSDK_MQ_Broker_Create(t *testing.T) {
17781778
code.SetSDK(crd.Config(), crd, model.OpTypeCreate, "r.ko", "res", 1),
17791779
)
17801780
}
1781+
1782+
func TestSetSDK_EC2_VPC_ReadMany(t *testing.T) {
1783+
assert := assert.New(t)
1784+
require := require.New(t)
1785+
1786+
g := testutil.NewModelForService(t, "ec2")
1787+
1788+
crd := testutil.GetCRDByName(t, g, "Vpc")
1789+
require.NotNil(crd)
1790+
1791+
expected := `
1792+
if r.ko.Status.VPCID != nil {
1793+
f4 := []*string{}
1794+
f4 = append(f4, r.ko.Status.VPCID)
1795+
res.SetVpcIds(f4)
1796+
}
1797+
`
1798+
assert.Equal(
1799+
expected,
1800+
code.SetSDK(crd.Config(), crd, model.OpTypeList, "r.ko", "res", 1),
1801+
)
1802+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
ignore:
2+
field_paths:
3+
- CreateVpcInput.DryRun
24
resource_names:
35
- AccountAttribute
46
- CapacityReservation

0 commit comments

Comments
 (0)