@@ -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 .
9731122func 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 .
10381187func setSDKForMap (
10391188 cfg * ackgenconfig.Config ,
10401189 r * model.CRD ,
0 commit comments