@@ -97,10 +97,7 @@ func SetResource(
97
97
case model .OpTypeGet :
98
98
op = r .Ops .ReadOne
99
99
case model .OpTypeList :
100
- return setResourceReadMany (
101
- cfg , r ,
102
- r .Ops .ReadMany , sourceVarName , targetVarName , indentLevel ,
103
- )
100
+ op = r .Ops .ReadMany
104
101
case model .OpTypeUpdate :
105
102
op = r .Ops .Update
106
103
case model .OpTypeDelete :
@@ -116,9 +113,28 @@ func SetResource(
116
113
return ""
117
114
}
118
115
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.
120
120
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
+ }
122
138
sourceVarName += "." + * wrapperFieldPath
123
139
} else {
124
140
// If the wrapper field path is not specified in the config file and if
@@ -133,6 +149,7 @@ func SetResource(
133
149
}
134
150
}
135
151
}
152
+
136
153
out := "\n "
137
154
indent := strings .Repeat ("\t " , indentLevel )
138
155
@@ -440,14 +457,30 @@ func setResourceReadMany(
440
457
var sourceElemShape * awssdkmodel.Shape
441
458
442
459
// 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
+ }
451
484
}
452
485
}
453
486
@@ -472,47 +505,53 @@ func setResourceReadMany(
472
505
473
506
// found := false
474
507
out += fmt .Sprintf ("%sfound := false\n " , indent )
508
+ elemVarName := "elem"
509
+ pathToShape := listShapeName
510
+ if wrapperFieldPath != nil {
511
+ pathToShape = * wrapperFieldPath
512
+ }
513
+
475
514
// 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
+
480
519
for memberIndex , memberName := range sourceElemShape .MemberNames () {
481
520
sourceMemberShapeRef := sourceElemShape .MemberRefs [memberName ]
482
521
sourceMemberShape := sourceMemberShapeRef .Shape
483
- sourceAdaptedVarName := "elem ." + memberName
522
+ sourceAdaptedVarName := elemVarName + " ." + memberName
484
523
if r .IsPrimaryARNField (memberName ) {
485
524
out += fmt .Sprintf (
486
- "%s \t if %s != nil {\n " , indent , sourceAdaptedVarName ,
525
+ "%sif %s != nil {\n " , innerForIndent , sourceAdaptedVarName ,
487
526
)
488
527
// if ko.Status.ACKResourceMetadata == nil {
489
528
// ko.Status.ACKResourceMetadata = &ackv1alpha1.ResourceMetadata{}
490
529
// }
491
530
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 ,
494
533
)
495
534
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 ,
498
537
)
499
538
out += fmt .Sprintf (
500
- "\t \t %s}\n " , indent ,
539
+ "\t %s}\n " , innerForIndent ,
501
540
)
502
541
// tmpARN := ackv1alpha1.AWSResourceName(*elemARN)
503
542
// ko.Status.ACKResourceMetadata.ARN = &tmpARN
504
543
out += fmt .Sprintf (
505
- "%s\t \ t tmpARN := ackv1alpha1.AWSResourceName(*%s)\n " ,
506
- indent ,
544
+ "%s\t tmpARN := ackv1alpha1.AWSResourceName(*%s)\n " ,
545
+ innerForIndent ,
507
546
sourceAdaptedVarName ,
508
547
)
509
548
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 ,
512
551
targetVarName ,
513
552
)
514
553
out += fmt .Sprintf (
515
- "\t %s}\n " , indent ,
554
+ "%s}\n " , innerForIndent ,
516
555
)
517
556
continue
518
557
}
@@ -550,7 +589,7 @@ func setResourceReadMany(
550
589
551
590
targetMemberShapeRef = f .ShapeRef
552
591
out += fmt .Sprintf (
553
- "%s \t if %s != nil {\n " , indent , sourceAdaptedVarName ,
592
+ "%sif %s != nil {\n " , innerForIndent , sourceAdaptedVarName ,
554
593
)
555
594
556
595
//ex: r.ko.Spec.CacheClusterID
@@ -565,7 +604,7 @@ func setResourceReadMany(
565
604
cfg , r ,
566
605
memberVarName ,
567
606
targetMemberShapeRef .Shape ,
568
- indentLevel + 2 ,
607
+ flIndentLvl + 1 ,
569
608
)
570
609
out += setResourceForContainer (
571
610
cfg , r ,
@@ -577,13 +616,13 @@ func setResourceReadMany(
577
616
sourceMemberShapeRef ,
578
617
f .Names .Camel ,
579
618
model .OpTypeList ,
580
- indentLevel + 2 ,
619
+ flIndentLvl + 1 ,
581
620
)
582
621
out += setResourceForScalar (
583
622
qualifiedTargetVar ,
584
623
memberVarName ,
585
624
sourceMemberShapeRef ,
586
- indentLevel + 2 ,
625
+ flIndentLvl + 1 ,
587
626
)
588
627
}
589
628
default :
@@ -594,45 +633,45 @@ func setResourceReadMany(
594
633
// }
595
634
if util .InStrings (fieldName , matchFieldNames ) {
596
635
out += fmt .Sprintf (
597
- "%s\t \ t if %s.%s != nil {\n " ,
598
- indent ,
636
+ "%s\t if %s.%s != nil {\n " ,
637
+ innerForIndent ,
599
638
targetAdaptedVarName ,
600
639
f .Names .Camel ,
601
640
)
602
641
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 ,
605
644
sourceAdaptedVarName ,
606
645
targetAdaptedVarName ,
607
646
f .Names .Camel ,
608
647
)
609
648
out += fmt .Sprintf (
610
- "%s\t \t \t \ t continue\n " , indent ,
649
+ "%s\t \t \t continue\n " , innerForIndent ,
611
650
)
612
651
out += fmt .Sprintf (
613
- "%s\t \t \t }\n " , indent ,
652
+ "%s\t \t }\n " , innerForIndent ,
614
653
)
615
654
out += fmt .Sprintf (
616
- "%s\t \t }\n " , indent ,
655
+ "%s\t }\n " , innerForIndent ,
617
656
)
618
657
}
619
658
// r.ko.Spec.CacheClusterID = elem.CacheClusterId
620
659
out += setResourceForScalar (
621
660
qualifiedTargetVar ,
622
661
sourceAdaptedVarName ,
623
662
sourceMemberShapeRef ,
624
- indentLevel + 2 ,
663
+ flIndentLvl + 1 ,
625
664
)
626
665
}
627
666
out += fmt .Sprintf (
628
- "%s%s } else {\n " , indent , indent ,
667
+ "%s} else {\n " , innerForIndent ,
629
668
)
630
669
out += fmt .Sprintf (
631
- "%s%s%s%s .%s = nil\n " , indent , indent , indent ,
670
+ "%s\t %s .%s = nil\n " , innerForIndent ,
632
671
targetAdaptedVarName , f .Names .Camel ,
633
672
)
634
673
out += fmt .Sprintf (
635
- "%s%s }\n " , indent , indent ,
674
+ "%s}\n " , innerForIndent ,
636
675
)
637
676
}
638
677
// When we don't have custom matching/filtering logic for the list
@@ -642,12 +681,12 @@ func setResourceReadMany(
642
681
// match. Thus, we will break here only when getting a record where
643
682
// all match fields have matched.
644
683
out += fmt .Sprintf (
645
- "%s \t found = true\n " , indent ,
684
+ "%sfound = true\n " , innerForIndent ,
646
685
)
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
+
651
690
// if !found {
652
691
// return nil, ackerr.NotFound
653
692
// }
@@ -1566,3 +1605,88 @@ func setResourceForScalar(
1566
1605
out += fmt .Sprintf ("%s%s = %s\n " , indent , targetVar , setTo )
1567
1606
return out
1568
1607
}
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