@@ -98,7 +98,7 @@ func ReferenceFieldsValidation(
98
98
out += fmt .Sprintf ("%sif %s.%s != nil" +
99
99
" && %s.%s != nil {\n " , fIndent , pathVarPrefix , field .GetReferenceFieldName ().Camel , pathVarPrefix , field .Names .Camel )
100
100
out += fmt .Sprintf ("%s\t return " +
101
- "ackerr.ResourceReferenceAndIDNotSupportedFor(\" %s \" , \" %s \" )\n " ,
101
+ "ackerr.ResourceReferenceAndIDNotSupportedFor(%q, %q )\n " ,
102
102
fIndent , field .Path , field .ReferenceFieldPath ())
103
103
104
104
// Close out all the curly braces with proper indentation
@@ -117,7 +117,7 @@ func ReferenceFieldsValidation(
117
117
" %s.%s == nil {\n " , fIndent , pathVarPrefix ,
118
118
field .ReferenceFieldPath (), pathVarPrefix , field .Path )
119
119
out += fmt .Sprintf ("%s\t return " +
120
- "ackerr.ResourceReferenceOrIDRequiredFor(\" %s \" , \" %s \" )\n " ,
120
+ "ackerr.ResourceReferenceOrIDRequiredFor(%q, %q )\n " ,
121
121
fIndent , field .Path , field .ReferenceFieldPath ())
122
122
out += fmt .Sprintf ("%s}\n " , fIndent )
123
123
}
@@ -185,6 +185,122 @@ func ReferenceFieldsPresent(
185
185
return iteratorsOut + returnOut
186
186
}
187
187
188
+ // ResolveReferencesForField produces Go code for accessing all references that
189
+ // are related to the given concrete field, determining whether its in a valid
190
+ // condition and updating the concrete field with the referenced value.
191
+ // Sample code (resolving a nested singular reference):
192
+ //
193
+ // ```
194
+ //
195
+ // if ko.Spec.APIRef != nil && ko.Spec.APIRef.From != nil {
196
+ // arr := ko.Spec.APIRef.From
197
+ // if arr == nil || arr.Name == nil || *arr.Name == "" {
198
+ // return fmt.Errorf("provided resource reference is nil or empty: APIRef")
199
+ // }
200
+ // obj := &svcapitypes.API{}
201
+ // if err := getReferencedResourceState_API(ctx, apiReader, obj, *arr.Name, namespace); err != nil {
202
+ // return err
203
+ // }
204
+ // ko.Spec.APIID = (*string)(obj.Status.APIID)
205
+ // }
206
+ //
207
+ // ```
208
+ func ResolveReferencesForField (field * model.Field , sourceVarName string , indentLevel int ) string {
209
+ r := field .CRD
210
+ fp := fieldpath .FromString (field .Path )
211
+
212
+ outPrefix := ""
213
+ outSuffix := ""
214
+
215
+ fieldAccessPrefix := fmt .Sprintf ("%s%s" , sourceVarName , r .Config ().PrefixConfig .SpecField )
216
+ targetVarName := fmt .Sprintf ("%s.%s" , fieldAccessPrefix , field .Path )
217
+
218
+ for idx := 0 ; idx < fp .Size (); idx ++ {
219
+ curFP := fp .CopyAt (idx ).String ()
220
+ cur , ok := r .Fields [curFP ]
221
+ if ! ok {
222
+ panic (fmt .Sprintf ("unable to find field with path %q. crd: %q" , curFP , r .Kind ))
223
+ }
224
+
225
+ ref := cur .ShapeRef
226
+
227
+ indent := strings .Repeat ("\t " , indentLevel + idx )
228
+
229
+ switch ref .Shape .Type {
230
+ case ("structure" ):
231
+ fieldAccessPrefix = fmt .Sprintf ("%s.%s" , fieldAccessPrefix , fp .At (idx ))
232
+
233
+ outPrefix += fmt .Sprintf ("%sif %s != nil {\n " , indent , fieldAccessPrefix )
234
+ outSuffix = fmt .Sprintf ("%s}\n %s" , indent , outSuffix )
235
+ case ("list" ):
236
+ if (fp .Size () - idx ) > 1 {
237
+ // TODO(nithomso): add support for structs nested within lists
238
+ // The logic for structs nested within lists needs to not only
239
+ // be added here, but also in a custom patching solution since
240
+ // it isn't supported by `StrategicMergePatch`
241
+ // see https://github.com/aws-controllers-k8s/community/issues/1291
242
+ panic (fmt .Errorf ("references within lists inside lists aren't supported. crd: %q, path: %q" , r .Kind , field .Path ))
243
+ }
244
+ fieldAccessPrefix = fmt .Sprintf ("%s.%s" , fieldAccessPrefix , cur .GetReferenceFieldName ().Camel )
245
+
246
+ iterVarName := fmt .Sprintf ("iter%d" , idx )
247
+ aggRefName := fmt .Sprintf ("resolved%d" , idx )
248
+
249
+ // base case for references in a list
250
+ outPrefix += fmt .Sprintf ("%sif len(%s) > 0 {\n " , indent , fieldAccessPrefix )
251
+ outPrefix += fmt .Sprintf ("%s\t %s := %s{}\n " , indent , aggRefName , field .GoType )
252
+ outPrefix += fmt .Sprintf ("%s\t for _, %s := range %s {\n " , indent , iterVarName , fieldAccessPrefix )
253
+
254
+ fieldAccessPrefix = iterVarName
255
+ outPrefix += fmt .Sprintf ("%s\t \t arr := %s.From\n " , indent , fieldAccessPrefix )
256
+ outPrefix += fmt .Sprintf ("%s\t \t if arr == nil || arr.Name == nil || *arr.Name == \" \" {\n " , indent )
257
+ outPrefix += fmt .Sprintf ("%s\t \t \t return fmt.Errorf(\" provided resource reference is nil or empty: %s\" )\n " , indent , field .ReferenceFieldPath ())
258
+ outPrefix += fmt .Sprintf ("%s\t \t }\n " , indent )
259
+
260
+ outPrefix += getReferencedStateForField (field , indentLevel + idx + 1 )
261
+
262
+ outPrefix += fmt .Sprintf ("%s\t \t %s = append(%s, (%s)(obj.%s))\n " , indent , aggRefName , aggRefName , field .ShapeRef .Shape .MemberRef .GoType (), field .FieldConfig .References .Path )
263
+ outPrefix += fmt .Sprintf ("%s\t }\n " , indent )
264
+ outPrefix += fmt .Sprintf ("%s\t %s = %s\n " , indent , targetVarName , aggRefName )
265
+ outPrefix += fmt .Sprintf ("%s}\n " , indent )
266
+ case ("map" ):
267
+ panic ("references cannot be within a map" )
268
+ default :
269
+ // base case for single references
270
+ fieldAccessPrefix = fmt .Sprintf ("%s.%s" , fieldAccessPrefix , cur .GetReferenceFieldName ().Camel )
271
+
272
+ outPrefix += fmt .Sprintf ("%sif %s != nil && %s.From != nil {\n " , indent , fieldAccessPrefix , fieldAccessPrefix )
273
+ outPrefix += fmt .Sprintf ("%s\t arr := %s.From\n " , indent , fieldAccessPrefix )
274
+ outPrefix += fmt .Sprintf ("%s\t if arr == nil || arr.Name == nil || *arr.Name == \" \" {\n " , indent )
275
+ outPrefix += fmt .Sprintf ("%s\t \t return fmt.Errorf(\" provided resource reference is nil or empty: %s\" )\n " , indent , field .ReferenceFieldPath ())
276
+ outPrefix += fmt .Sprintf ("%s\t }\n " , indent )
277
+
278
+ outPrefix += getReferencedStateForField (field , indentLevel + idx )
279
+
280
+ outPrefix += fmt .Sprintf ("%s\t %s = (%s)(obj.%s)\n " , indent , targetVarName , field .GoType , field .FieldConfig .References .Path )
281
+ outPrefix += fmt .Sprintf ("%s}\n " , indent )
282
+ }
283
+ }
284
+
285
+ return outPrefix + outSuffix
286
+ }
287
+
288
+ func getReferencedStateForField (field * model.Field , indentLevel int ) string {
289
+ out := ""
290
+ indent := strings .Repeat ("\t " , indentLevel )
291
+
292
+ if field .FieldConfig .References .ServiceName == "" {
293
+ out += fmt .Sprintf ("%s\t obj := &svcapitypes.%s{}\n " , indent , field .FieldConfig .References .Resource )
294
+ } else {
295
+ out += fmt .Sprintf ("%s\t obj := &%sapitypes.%s{}\n " , indent , field .ReferencedServiceName (), field .FieldConfig .References .Resource )
296
+ }
297
+ out += fmt .Sprintf ("%s\t if err := getReferencedResourceState_%s(ctx, apiReader, obj, *arr.Name, namespace); err != nil {\n " , indent , field .FieldConfig .References .Resource )
298
+ out += fmt .Sprintf ("%s\t \t return err\n " , indent )
299
+ out += fmt .Sprintf ("%s\t }\n " , indent )
300
+
301
+ return out
302
+ }
303
+
188
304
func nestedStructNilCheck (path fieldpath.Path , fieldAccessPrefix string ) string {
189
305
out := ""
190
306
fieldNamePrefix := ""
0 commit comments