@@ -22,22 +22,26 @@ import (
2222)
2323
2424// ReferenceFieldsValidation produces the go code to validate reference field and
25- // corresponding identifier field.
25+ // corresponding identifier field. Iterates through all references within
26+ // slices, if necessary.
27+ // for _, iter0 := range ko.Spec.Routes {
28+ // if iter0.GatewayRef != nil && iter0.GatewayID != nil {
29+ // return ackerr.ResourceReferenceAndIDNotSupportedFor("Routes.GatewayID", "Routes.GatewayRef")
30+ // }
31+ // }
2632// Sample code:
2733// if ko.Spec.APIRef != nil && ko.Spec.APIID != nil {
28- // return ackerr.ResourceReferenceAndIDNotSupportedFor("APIID", "APIRef")
29- // }
30- // if ko.Spec.APIRef == nil && ko.Spec.APIID == nil {
31- // return ackerr.ResourceReferenceOrIDRequiredFor("APIID", "APIRef")
32- // }
34+ // return ackerr.ResourceReferenceAndIDNotSupportedFor("APIID", "APIRef")
35+ // }
36+ // if ko.Spec.APIRef == nil && ko.Spec.APIID == nil {
37+ // return ackerr.ResourceReferenceOrIDRequiredFor("APIID", "APIRef")
38+ // }
3339func ReferenceFieldsValidation (
3440 crd * model.CRD ,
3541 sourceVarName string ,
3642 indentLevel int ,
3743) string {
3844 out := ""
39- fieldAccessPrefix := fmt .Sprintf ("%s%s" , sourceVarName ,
40- crd .Config ().PrefixConfig .SpecField )
4145 // Sorted fieldnames are used for consistent code-generation
4246 for _ , fieldName := range crd .SortedFieldNames () {
4347 field := crd .Fields [fieldName ]
@@ -47,20 +51,48 @@ func ReferenceFieldsValidation(
4751 fp := fieldpath .FromString (field .Path )
4852 // remove fieldName from fieldPath before adding nil checks
4953 fp .Pop ()
50- fieldNamePrefix := crd .Config ().PrefixConfig .SpecField
54+
55+ // prefix of the field path for referencing in the model
56+ fieldNamePrefix := ""
57+ // prefix of the field path for the generated code
58+ pathVarPrefix := fmt .Sprintf ("%s%s" , sourceVarName , crd .Config ().PrefixConfig .SpecField )
59+
5160 // this loop outputs a nil-guard for each level of nested field path
61+ // or an iterator for any level that is a slice
62+ fieldDepth := 0
5263 for fp .Size () > 0 {
5364 fIndent = strings .Repeat ("\t " , fIndentLevel )
54- fieldNamePrefix = fmt .Sprintf ("%s.%s" , fieldNamePrefix , fp .PopFront ())
55- out += fmt .Sprintf ("%sif %s%s != nil {\n " , fIndent , sourceVarName , fieldNamePrefix )
65+ currentField := fp .PopFront ()
66+
67+ if fieldNamePrefix == "" {
68+ fieldNamePrefix = currentField
69+ } else {
70+ fieldNamePrefix = fmt .Sprintf ("%s.%s" , fieldNamePrefix , currentField )
71+ }
72+ pathVarPrefix = fmt .Sprintf ("%s.%s" , pathVarPrefix , currentField )
73+
74+ fieldConfig , ok := crd .Fields [fieldNamePrefix ]
75+ if ! ok {
76+ panic (fmt .Sprintf ("CRD %s has no Field with path %s" , crd .Kind , fieldNamePrefix ))
77+ }
78+
79+ if fieldConfig .ShapeRef .Shape .Type == "list" {
80+ out += fmt .Sprintf ("%sfor _, iter%d := range %s {\n " , fIndent , fieldDepth , pathVarPrefix )
81+ // reset the path variable name
82+ pathVarPrefix = fmt .Sprintf ("iter%d" , fieldDepth )
83+ } else {
84+ out += fmt .Sprintf ("%sif %s != nil {\n " , fIndent , pathVarPrefix )
85+ }
86+
5687 fIndentLevel ++
88+ fieldDepth ++
5789 }
90+
5891 fIndent = strings .Repeat ("\t " , fIndentLevel )
5992 // Validation to make sure both target field and reference are
6093 // not present at the same time in desired resource
6194 out += fmt .Sprintf ("%sif %s.%s != nil" +
62- " && %s.%s != nil {\n " , fIndent , fieldAccessPrefix ,
63- field .ReferenceFieldPath (), fieldAccessPrefix , field .Path )
95+ " && %s.%s != nil {\n " , fIndent , pathVarPrefix , field .GetReferenceFieldName ().Camel , pathVarPrefix , field .Names .Camel )
6496 out += fmt .Sprintf ("%s\t return " +
6597 "ackerr.ResourceReferenceAndIDNotSupportedFor(\" %s\" , \" %s\" )\n " ,
6698 fIndent , field .Path , field .ReferenceFieldPath ())
@@ -78,8 +110,8 @@ func ReferenceFieldsValidation(
78110 // field is present in the resource
79111 if field .IsRequired () {
80112 out += fmt .Sprintf ("%sif %s.%s == nil &&" +
81- " %s.%s == nil {\n " , fIndent , fieldAccessPrefix ,
82- field .ReferenceFieldPath (), fieldAccessPrefix , field .Path )
113+ " %s.%s == nil {\n " , fIndent , pathVarPrefix ,
114+ field .ReferenceFieldPath (), pathVarPrefix , field .Path )
83115 out += fmt .Sprintf ("%s\t return " +
84116 "ackerr.ResourceReferenceOrIDRequiredFor(\" %s\" , \" %s\" )\n " ,
85117 fIndent , field .Path , field .ReferenceFieldPath ())
@@ -94,32 +126,65 @@ func ReferenceFieldsValidation(
94126// a non-nil reference field is present in a resource. This checks helps in deciding
95127// whether ACK.ReferencesResolved condition should be added to resource status
96128// Sample Code:
129+ // if ko.Spec.Routes != nil {
130+ // for _, iter35 := range ko.Spec.Routes {
131+ // if iter35.GatewayRef != nil {
132+ // return true
133+ // }
134+ // }
135+ // }
97136// return false || (ko.Spec.APIRef != nil)
98137func ReferenceFieldsPresent (
99138 crd * model.CRD ,
100139 sourceVarName string ,
101140) string {
102- out := "false"
141+ iteratorsOut := ""
142+ returnOut := "return false"
103143 fieldAccessPrefix := fmt .Sprintf ("%s%s" , sourceVarName ,
104144 crd .Config ().PrefixConfig .SpecField )
105145 // Sorted fieldnames are used for consistent code-generation
106- for _ , fieldName := range crd .SortedFieldNames () {
146+ for fieldIndex , fieldName := range crd .SortedFieldNames () {
107147 field := crd .Fields [fieldName ]
108148 if field .HasReference () {
109- out += " || ("
110149 fp := fieldpath .FromString (field .Path )
111150 // remove fieldName from fieldPath before adding nil checks
112151 // for nested fieldPath
113152 fp .Pop ()
114- fieldNamePrefix := ""
115- for fp .Size () > 0 {
116- fieldNamePrefix = fmt .Sprintf ("%s.%s" , fieldNamePrefix , fp .PopFront ())
117- out += fmt .Sprintf ("%s%s != nil && " , fieldAccessPrefix , fieldNamePrefix )
153+
154+ // Determine whether the field is nested
155+ if fp .Size () > 0 {
156+ // Determine whether the field is inside a slice
157+ parentField , ok := crd .Fields [fp .String ()]
158+ if ! ok {
159+ panic (fmt .Sprintf ("CRD %s has no Field with path %s" , crd .Kind , fp .String ()))
160+ }
161+
162+ if parentField .ShapeRef .Shape .Type == "list" {
163+ iteratorsOut += fmt .Sprintf ("if %s {\n " , nestedStructNilCheck (* fp .Copy (), fieldAccessPrefix ))
164+ iteratorsOut += fmt .Sprintf ("\t for _, iter%d := range %s.%s {\n " , fieldIndex , fieldAccessPrefix , parentField .Path )
165+ iteratorsOut += fmt .Sprintf ("\t \t if iter%d.%s != nil {\n " , fieldIndex , field .GetReferenceFieldName ().Camel )
166+ iteratorsOut += fmt .Sprintf ("\t \t \t return true\n " )
167+ iteratorsOut += fmt .Sprintf ("\t \t }\n " )
168+ iteratorsOut += fmt .Sprintf ("\t }\n " )
169+ iteratorsOut += fmt .Sprintf ("}\n " )
170+ continue
171+ }
118172 }
119- out += fmt .Sprintf ("%s.%s != nil" , fieldAccessPrefix ,
173+
174+ nilCheck := nestedStructNilCheck (* fp .Copy (), fieldAccessPrefix ) + " && " + fmt .Sprintf ("%s.%s != nil" , fieldAccessPrefix ,
120175 field .ReferenceFieldPath ())
121- out += ")"
176+ returnOut += " || (" + strings . TrimPrefix ( nilCheck , " && " ) + ")"
122177 }
123178 }
124- return out
179+ return iteratorsOut + returnOut
180+ }
181+
182+ func nestedStructNilCheck (path fieldpath.Path , fieldAccessPrefix string ) string {
183+ out := ""
184+ fieldNamePrefix := ""
185+ for path .Size () > 0 {
186+ fieldNamePrefix = fmt .Sprintf ("%s.%s" , fieldNamePrefix , path .PopFront ())
187+ out += fmt .Sprintf ("%s%s != nil && " , fieldAccessPrefix , fieldNamePrefix )
188+ }
189+ return strings .TrimSuffix (out , " && " )
125190}
0 commit comments