@@ -97,10 +97,7 @@ func SetResource(
9797 case model .OpTypeGet :
9898 op = r .Ops .ReadOne
9999 case model .OpTypeList :
100- return setResourceReadMany (
101- cfg , r ,
102- r .Ops .ReadMany , sourceVarName , targetVarName , indentLevel ,
103- )
100+ op = r .Ops .ReadMany
104101 case model .OpTypeUpdate :
105102 op = r .Ops .Update
106103 case model .OpTypeDelete :
@@ -116,9 +113,28 @@ func SetResource(
116113 return ""
117114 }
118115
119- // Use the wrapper field path if it's given in the ack-generate config file.
116+ // If the output shape has a list containing the resource,
117+ // then call setResourceReadMany to generate for-range loops.
118+ // Output shape will be a list for ReadMany operations or if
119+ // designated via output wrapper config.
120120 wrapperFieldPath := r .GetOutputWrapperFieldPath (op )
121- if wrapperFieldPath != nil {
121+ if op == r .Ops .ReadMany {
122+ return setResourceReadMany (
123+ cfg , r ,
124+ op , sourceVarName , targetVarName , indentLevel ,
125+ )
126+ } else if wrapperFieldPath != nil {
127+ // fieldpath api requires fully-qualified path
128+ qwfp := fieldpath .FromString (op .OutputRef .ShapeName + "." + * wrapperFieldPath )
129+ for _ , sref := range qwfp .IterShapeRefs (& op .OutputRef ) {
130+ // if there's at least 1 list to unpack, call setResourceReadMany
131+ if sref .Shape .Type == "list" {
132+ return setResourceReadMany (
133+ cfg , r ,
134+ op , sourceVarName , targetVarName , indentLevel ,
135+ )
136+ }
137+ }
122138 sourceVarName += "." + * wrapperFieldPath
123139 } else {
124140 // If the wrapper field path is not specified in the config file and if
@@ -133,6 +149,7 @@ func SetResource(
133149 }
134150 }
135151 }
152+
136153 out := "\n "
137154 indent := strings .Repeat ("\t " , indentLevel )
138155
@@ -440,14 +457,30 @@ func setResourceReadMany(
440457 var sourceElemShape * awssdkmodel.Shape
441458
442459 // Find the element in the output shape that contains the list of
443- // resources. This heuristic is simplistic (just look for the field with a
444- // list type) but seems to be followed consistently by the aws-sdk-go for
445- // List operations.
446- for memberName , memberShapeRef := range outputShape .MemberRefs {
447- if memberShapeRef .Shape .Type == "list" {
448- listShapeName = memberName
449- sourceElemShape = memberShapeRef .Shape .MemberRef .Shape
450- break
460+ // resources:
461+ // Check if there's a wrapperFieldPath, which will
462+ // point directly to the shape.
463+ wrapperFieldPath := r .GetOutputWrapperFieldPath (op )
464+ if wrapperFieldPath != nil {
465+ // fieldpath API needs fully qualified name
466+ wfp := fieldpath .FromString (outputShape .ShapeName + "." + * wrapperFieldPath )
467+ wfpShapeRef := wfp .ShapeRef (& op .OutputRef )
468+ if wfpShapeRef != nil {
469+ listShapeName = wfpShapeRef .ShapeName
470+ sourceElemShape = wfpShapeRef .Shape .MemberRef .Shape
471+ }
472+ }
473+
474+ // If listShape can't be found using wrapperFieldPath,
475+ // then fall back to looking for the first field with a list type;
476+ // this heuristic seems to work for most list operations in aws-sdk-go.
477+ if listShapeName == "" {
478+ for memberName , memberShapeRef := range outputShape .MemberRefs {
479+ if memberShapeRef .Shape .Type == "list" {
480+ listShapeName = memberName
481+ sourceElemShape = memberShapeRef .Shape .MemberRef .Shape
482+ break
483+ }
451484 }
452485 }
453486
@@ -472,47 +505,53 @@ func setResourceReadMany(
472505
473506 // found := false
474507 out += fmt .Sprintf ("%sfound := false\n " , indent )
508+ elemVarName := "elem"
509+ pathToShape := listShapeName
510+ if wrapperFieldPath != nil {
511+ pathToShape = * wrapperFieldPath
512+ }
513+
475514 // for _, elem := range resp.CacheClusters {
476- out += fmt . Sprintf (
477- "%sfor _, elem := range %s.%s { \n " ,
478- indent , sourceVarName , listShapeName ,
479- )
515+ opening , closing , flIndentLvl := generateForRangeLoops ( & op . OutputRef , pathToShape , sourceVarName , elemVarName , indentLevel )
516+ innerForIndent := strings . Repeat ( " \t " , flIndentLvl )
517+ out += opening
518+
480519 for memberIndex , memberName := range sourceElemShape .MemberNames () {
481520 sourceMemberShapeRef := sourceElemShape .MemberRefs [memberName ]
482521 sourceMemberShape := sourceMemberShapeRef .Shape
483- sourceAdaptedVarName := "elem ." + memberName
522+ sourceAdaptedVarName := elemVarName + " ." + memberName
484523 if r .IsPrimaryARNField (memberName ) {
485524 out += fmt .Sprintf (
486- "%s \t if %s != nil {\n " , indent , sourceAdaptedVarName ,
525+ "%sif %s != nil {\n " , innerForIndent , sourceAdaptedVarName ,
487526 )
488527 // if ko.Status.ACKResourceMetadata == nil {
489528 // ko.Status.ACKResourceMetadata = &ackv1alpha1.ResourceMetadata{}
490529 // }
491530 out += fmt .Sprintf (
492- "%s\t \ t if %s.Status.ACKResourceMetadata == nil {\n " ,
493- indent , targetVarName ,
531+ "%s\t if %s.Status.ACKResourceMetadata == nil {\n " ,
532+ innerForIndent , targetVarName ,
494533 )
495534 out += fmt .Sprintf (
496- "%s\t \t \t %s.Status.ACKResourceMetadata = &ackv1alpha1.ResourceMetadata{}\n " ,
497- indent , targetVarName ,
535+ "%s\t \t %s.Status.ACKResourceMetadata = &ackv1alpha1.ResourceMetadata{}\n " ,
536+ innerForIndent , targetVarName ,
498537 )
499538 out += fmt .Sprintf (
500- "\t \t %s}\n " , indent ,
539+ "\t %s}\n " , innerForIndent ,
501540 )
502541 // tmpARN := ackv1alpha1.AWSResourceName(*elemARN)
503542 // ko.Status.ACKResourceMetadata.ARN = &tmpARN
504543 out += fmt .Sprintf (
505- "%s\t \ t tmpARN := ackv1alpha1.AWSResourceName(*%s)\n " ,
506- indent ,
544+ "%s\t tmpARN := ackv1alpha1.AWSResourceName(*%s)\n " ,
545+ innerForIndent ,
507546 sourceAdaptedVarName ,
508547 )
509548 out += fmt .Sprintf (
510- "%s\t \t %s.Status.ACKResourceMetadata.ARN = &tmpARN\n " ,
511- indent ,
549+ "%s\t %s.Status.ACKResourceMetadata.ARN = &tmpARN\n " ,
550+ innerForIndent ,
512551 targetVarName ,
513552 )
514553 out += fmt .Sprintf (
515- "\t %s}\n " , indent ,
554+ "%s}\n " , innerForIndent ,
516555 )
517556 continue
518557 }
@@ -550,7 +589,7 @@ func setResourceReadMany(
550589
551590 targetMemberShapeRef = f .ShapeRef
552591 out += fmt .Sprintf (
553- "%s \t if %s != nil {\n " , indent , sourceAdaptedVarName ,
592+ "%sif %s != nil {\n " , innerForIndent , sourceAdaptedVarName ,
554593 )
555594
556595 //ex: r.ko.Spec.CacheClusterID
@@ -565,7 +604,7 @@ func setResourceReadMany(
565604 cfg , r ,
566605 memberVarName ,
567606 targetMemberShapeRef .Shape ,
568- indentLevel + 2 ,
607+ flIndentLvl + 1 ,
569608 )
570609 out += setResourceForContainer (
571610 cfg , r ,
@@ -577,13 +616,13 @@ func setResourceReadMany(
577616 sourceMemberShapeRef ,
578617 f .Names .Camel ,
579618 model .OpTypeList ,
580- indentLevel + 2 ,
619+ flIndentLvl + 1 ,
581620 )
582621 out += setResourceForScalar (
583622 qualifiedTargetVar ,
584623 memberVarName ,
585624 sourceMemberShapeRef ,
586- indentLevel + 2 ,
625+ flIndentLvl + 1 ,
587626 )
588627 }
589628 default :
@@ -594,45 +633,45 @@ func setResourceReadMany(
594633 // }
595634 if util .InStrings (fieldName , matchFieldNames ) {
596635 out += fmt .Sprintf (
597- "%s\t \ t if %s.%s != nil {\n " ,
598- indent ,
636+ "%s\t if %s.%s != nil {\n " ,
637+ innerForIndent ,
599638 targetAdaptedVarName ,
600639 f .Names .Camel ,
601640 )
602641 out += fmt .Sprintf (
603- "%s\t \t \ t if *%s != *%s.%s {\n " ,
604- indent ,
642+ "%s\t \t if *%s != *%s.%s {\n " ,
643+ innerForIndent ,
605644 sourceAdaptedVarName ,
606645 targetAdaptedVarName ,
607646 f .Names .Camel ,
608647 )
609648 out += fmt .Sprintf (
610- "%s\t \t \t \ t continue\n " , indent ,
649+ "%s\t \t \t continue\n " , innerForIndent ,
611650 )
612651 out += fmt .Sprintf (
613- "%s\t \t \t }\n " , indent ,
652+ "%s\t \t }\n " , innerForIndent ,
614653 )
615654 out += fmt .Sprintf (
616- "%s\t \t }\n " , indent ,
655+ "%s\t }\n " , innerForIndent ,
617656 )
618657 }
619658 // r.ko.Spec.CacheClusterID = elem.CacheClusterId
620659 out += setResourceForScalar (
621660 qualifiedTargetVar ,
622661 sourceAdaptedVarName ,
623662 sourceMemberShapeRef ,
624- indentLevel + 2 ,
663+ flIndentLvl + 1 ,
625664 )
626665 }
627666 out += fmt .Sprintf (
628- "%s%s } else {\n " , indent , indent ,
667+ "%s} else {\n " , innerForIndent ,
629668 )
630669 out += fmt .Sprintf (
631- "%s%s%s%s .%s = nil\n " , indent , indent , indent ,
670+ "%s\t %s .%s = nil\n " , innerForIndent ,
632671 targetAdaptedVarName , f .Names .Camel ,
633672 )
634673 out += fmt .Sprintf (
635- "%s%s }\n " , indent , indent ,
674+ "%s}\n " , innerForIndent ,
636675 )
637676 }
638677 // When we don't have custom matching/filtering logic for the list
@@ -642,12 +681,12 @@ func setResourceReadMany(
642681 // match. Thus, we will break here only when getting a record where
643682 // all match fields have matched.
644683 out += fmt .Sprintf (
645- "%s \t found = true\n " , indent ,
684+ "%sfound = true\n " , innerForIndent ,
646685 )
647- out += fmt . Sprintf (
648- "%s \t break \n " , indent ,
649- )
650- out += fmt . Sprintf ( "%s} \n " , indent )
686+
687+ // End of for-range loops
688+ out += closing
689+
651690 // if !found {
652691 // return nil, ackerr.NotFound
653692 // }
@@ -1566,3 +1605,88 @@ func setResourceForScalar(
15661605 out += fmt .Sprintf ("%s%s = %s\n " , indent , targetVar , setTo )
15671606 return out
15681607}
1608+
1609+ // generateForRangeLoops returns strings of Go code and an int
1610+ // representing indentLevel of the inner-most for loop + 1.
1611+ // This function unpacks a collection from a shapeRef
1612+ // using path and builds the opening and closing
1613+ // pieces of a for-range loop written in Go in order
1614+ // to access the element contained within the collection.
1615+ // The name of this element value is designated with outputVarName:
1616+ // ex: 'for _, <outputVarName> := ...'
1617+ // Limitations: path supports lists and structs only and
1618+ // the 'break' is coupled with for-range loop to only take
1619+ // first element of a list.
1620+ //
1621+ // Sample Input:
1622+ // - shapeRef: DescribeInstancesOutputShape
1623+ // - path: Reservations.Instances
1624+ // - sourceVarName: resp
1625+ // - outputVarName: elem
1626+ // - indentLevel: 1
1627+ //
1628+ // Sample Output (omit formatting for readability):
1629+ // - opening: "for _, iter0 := range resp.Reservations {
1630+ // for _, elem := range iter0.Instances {"
1631+ // - closing: "break } break }"
1632+ // - updatedIndentLevel: 3
1633+ func generateForRangeLoops (
1634+ // shapeRef of the shape containing element
1635+ shapeRef * awssdkmodel.ShapeRef ,
1636+ // path is the path to the element relative to shapeRef
1637+ path string ,
1638+ // sourceVarName is the name of struct or field used to access source value
1639+ sourceVarName string ,
1640+ // outputVarName is the desired name of the element, once unwrapped
1641+ outputVarName string ,
1642+ indentLevel int ,
1643+ ) (string , string , int ) {
1644+ opening , closing := "" , ""
1645+ updatedIndentLevel := indentLevel
1646+
1647+ fp := fieldpath .FromString (path )
1648+ unwrapCount := 0
1649+ iterVarName := fmt .Sprintf ("iter%d" , unwrapCount )
1650+ collectionVarName := sourceVarName
1651+ unpackShape := shapeRef .Shape
1652+
1653+ for fp .Size () > 0 {
1654+ pathPart := fp .PopFront ()
1655+ partShapeRef , _ := unpackShape .MemberRefs [pathPart ]
1656+ unpackShape = partShapeRef .Shape
1657+ indent := strings .Repeat ("\t " , updatedIndentLevel )
1658+ iterVarName = fmt .Sprintf ("iter%d" , unwrapCount )
1659+ collectionVarName += "." + pathPart
1660+
1661+ // Using the fieldpath as a guide, unwrap the shapeRef
1662+ // to generate for-range loops. If pathPart points
1663+ // to a struct member, then simply append struct name
1664+ // to collectionVarName and move on to unwrap the next pathPart/shape.
1665+ // If pathPart points to a list member, then generate for-range loop
1666+ // code and update collectionVarName, unpackShape, and updatedIndentLevel
1667+ // for processing the next loop, if applicable.
1668+ if partShapeRef .Shape .Type == "list" {
1669+ // ex: for _, iter0 := range resp.Reservations {
1670+ opening += fmt .Sprintf ("%sfor _, %s := range %s {\n " , indent , iterVarName , collectionVarName )
1671+ // ex:
1672+ // break
1673+ // }
1674+ closeLoop := fmt .Sprintf ("%s\t break\n %s}\n " , indent , indent )
1675+ if closing != "" {
1676+ // nested loops need to output inner most closing braces first
1677+ closeLoop += closing
1678+ closing = closeLoop
1679+ } else {
1680+ closing += closeLoop
1681+ }
1682+ // reference iterVarName in subsequent for-loop, if any
1683+ collectionVarName = iterVarName
1684+ unpackShape = partShapeRef .Shape .MemberRef .Shape
1685+ updatedIndentLevel += 1
1686+ }
1687+ unwrapCount += 1
1688+ }
1689+ // replace inner-most range loop value's name with desired outputVarName
1690+ opening = strings .Replace (opening , iterVarName , outputVarName , 1 )
1691+ return opening , closing , updatedIndentLevel
1692+ }
0 commit comments