22
22
import com .amplifyframework .api .graphql .MutationType ;
23
23
import com .amplifyframework .core .model .AuthRule ;
24
24
import com .amplifyframework .core .model .AuthStrategy ;
25
+ import com .amplifyframework .core .model .LoadedModelReference ;
25
26
import com .amplifyframework .core .model .Model ;
26
27
import com .amplifyframework .core .model .ModelAssociation ;
27
28
import com .amplifyframework .core .model .ModelField ;
28
29
import com .amplifyframework .core .model .ModelIdentifier ;
30
+ import com .amplifyframework .core .model .ModelReference ;
29
31
import com .amplifyframework .core .model .ModelSchema ;
30
32
import com .amplifyframework .core .model .SerializedCustomType ;
31
33
import com .amplifyframework .core .model .SerializedModel ;
@@ -171,7 +173,7 @@ public static Map<String, Object> getDeleteMutationInputMap(
171
173
@ NonNull ModelSchema schema , @ NonNull Model instance ) throws AmplifyException {
172
174
final Map <String , Object > input = new HashMap <>();
173
175
for (String fieldName : schema .getPrimaryIndexFields ()) {
174
- input .put (fieldName , extractFieldValue (fieldName , instance , schema ));
176
+ input .put (fieldName , extractFieldValue (fieldName , instance , schema , true ));
175
177
}
176
178
return input ;
177
179
}
@@ -224,21 +226,30 @@ private static Map<String, Object> extractFieldLevelData(
224
226
continue ;
225
227
}
226
228
227
- Object fieldValue = extractFieldValue (modelField .getName (), instance , schema );
229
+ Object fieldValue = extractFieldValue (modelField .getName (), instance , schema , false );
230
+ Object underlyingFieldValue = fieldValue ;
231
+ if (modelField .isModelReference () && fieldValue != null ) {
232
+ ModelReference <?> modelReference = (ModelReference <?>) fieldValue ;
233
+ if (modelReference instanceof LoadedModelReference ) {
234
+ underlyingFieldValue = ((LoadedModelReference ) modelReference ).getValue ();
235
+ }
236
+ }
228
237
229
238
if (association == null ) {
230
239
result .put (fieldName , fieldValue );
231
240
} else if (association .isOwner ()) {
232
- if (fieldValue == null && MutationType .CREATE .equals (type )) {
241
+ if ((fieldValue == null ||
242
+ (modelField .isModelReference () && underlyingFieldValue == null )) &&
243
+ MutationType .CREATE .equals (type )) {
233
244
// Do not set null values on associations for create mutations.
234
245
} else if (schema .getVersion () >= 1 && association .getTargetNames () != null
235
246
&& association .getTargetNames ().length > 0 ) {
236
247
// When target name length is more than 0 there are two scenarios, one is when
237
248
// there is custom primary key and other is when we have composite primary key.
238
- insertForeignKeyValues (result , modelField , fieldValue , association );
249
+ insertForeignKeyValues (result , modelField , fieldValue , underlyingFieldValue , association );
239
250
} else {
240
251
String targetName = association .getTargetName ();
241
- result .put (targetName , extractAssociateId (modelField , fieldValue ));
252
+ result .put (targetName , extractAssociateId (modelField , fieldValue , underlyingFieldValue ));
242
253
}
243
254
}
244
255
// Ignore if field is associated, but is not a "belongsTo" relationship
@@ -250,58 +261,94 @@ private static void insertForeignKeyValues(
250
261
Map <String , Object > result ,
251
262
ModelField modelField ,
252
263
Object fieldValue ,
264
+ Object underlyingFieldValue ,
253
265
ModelAssociation association ) {
254
266
if (modelField .isModel () && fieldValue == null ) {
255
- // When there is no model field value, set null for removal of values or deassociation .
267
+ // When there is no model field value, set null for removal of values or association .
256
268
for (String key : association .getTargetNames ()) {
257
269
result .put (key , null );
258
270
}
259
- } else if (modelField .isModel () && fieldValue instanceof Model ) {
260
- if (((Model ) fieldValue ).resolveIdentifier () instanceof ModelIdentifier <?>) {
261
- final ModelIdentifier <?> primaryKey = (ModelIdentifier <?>) ((Model ) fieldValue ).resolveIdentifier ();
262
- ListIterator <String > targetNames = Arrays .asList (association .getTargetNames ()).listIterator ();
263
- Iterator <? extends Serializable > sortedKeys = primaryKey .sortedKeys ().listIterator ();
271
+ } else if ((modelField .isModel () || modelField .isModelReference ()) && underlyingFieldValue instanceof Model ) {
272
+ if (((Model ) underlyingFieldValue ).resolveIdentifier () instanceof ModelIdentifier <?>) {
273
+ // Here, we are unwrapping our ModelReference to grab our foreign keys.
274
+ // If we have a ModelIdentifier, we can pull all the key values, but we don't have
275
+ // the key names. We must grab those from the association target names
276
+ final ModelIdentifier <?> primaryKey =
277
+ (ModelIdentifier <?>) ((Model ) underlyingFieldValue ).resolveIdentifier ();
278
+ ListIterator <String > targetNames =
279
+ Arrays .asList (association .getTargetNames ()).listIterator ();
280
+ Iterator <? extends Serializable > sortedKeys =
281
+ primaryKey .sortedKeys ().listIterator ();
264
282
265
283
result .put (targetNames .next (), primaryKey .key ());
266
284
267
285
while (targetNames .hasNext ()) {
268
286
result .put (targetNames .next (), sortedKeys .next ());
269
287
}
270
- } else if ((fieldValue instanceof SerializedModel )) {
271
- SerializedModel serializedModel = ((SerializedModel ) fieldValue );
288
+ } else if ((underlyingFieldValue instanceof SerializedModel )) {
289
+ SerializedModel serializedModel = ((SerializedModel ) underlyingFieldValue );
272
290
ModelSchema serializedSchema = serializedModel .getModelSchema ();
273
291
if (serializedSchema != null &&
274
292
serializedSchema .getPrimaryIndexFields ().size () > 1 ) {
275
293
276
- ListIterator <String > primaryKeyFieldsIterator = serializedSchema .getPrimaryIndexFields ()
294
+ ListIterator <String > primaryKeyFieldsIterator =
295
+ serializedSchema .getPrimaryIndexFields ()
277
296
.listIterator ();
278
297
for (String targetName : association .getTargetNames ()) {
279
298
result .put (targetName , serializedModel .getSerializedData ()
280
299
.get (primaryKeyFieldsIterator .next ()));
281
300
}
282
301
} else {
283
- result .put (association .getTargetNames ()[0 ], ((Model ) fieldValue ).resolveIdentifier ().toString ());
302
+ // our key was not a ModelIdentifier type, so it must be a singular primary key
303
+ result .put (
304
+ association .getTargetNames ()[0 ],
305
+ ((Model ) underlyingFieldValue ).resolveIdentifier ().toString ()
306
+ );
284
307
}
285
308
} else {
286
- result .put (association .getTargetNames ()[0 ], ((Model ) fieldValue ).resolveIdentifier ().toString ());
309
+ // our key was not a ModelIdentifier type, so it must be a singular primary key
310
+ result .put (
311
+ association .getTargetNames ()[0 ],
312
+ ((Model ) underlyingFieldValue ).resolveIdentifier ().toString ()
313
+ );
314
+ }
315
+ } else if (modelField .isModelReference () && fieldValue instanceof ModelReference ) {
316
+ // Here we are unwrapping our ModelReference and inserting
317
+ Map <String , Object > identifiers = ((ModelReference <?>) fieldValue ).getIdentifier ();
318
+ if (identifiers .isEmpty ()) {
319
+ for (String key : association .getTargetNames ()) {
320
+ result .put (key , null );
321
+ }
287
322
}
288
323
}
289
324
}
290
325
291
- private static Object extractAssociateId (ModelField modelField , Object fieldValue ) {
292
- if (modelField .isModel () && fieldValue instanceof Model ) {
293
- return ((Model ) fieldValue ).resolveIdentifier ();
326
+ private static Object extractAssociateId (ModelField modelField , Object fieldValue , Object underlyingFieldValue ) {
327
+ if (( modelField .isModel () || modelField . isModelReference ()) && underlyingFieldValue instanceof Model ) {
328
+ return ((Model ) underlyingFieldValue ).resolveIdentifier ();
294
329
} else if (modelField .isModel () && fieldValue instanceof Map ) {
295
330
return ((Map <?, ?>) fieldValue ).get ("id" );
296
331
} else if (modelField .isModel () && fieldValue == null ) {
297
332
// When there is no model field value, set null for removal of values or deassociation.
298
333
return null ;
334
+ } else if (modelField .isModelReference () && fieldValue instanceof ModelReference ) {
335
+ Map <String , Object > identifiers = ((ModelReference <?>) fieldValue ).getIdentifier ();
336
+ if (identifiers .isEmpty ()) {
337
+ return null ;
338
+ } else {
339
+ return identifiers .get ("id" );
340
+ }
299
341
} else {
300
342
throw new IllegalStateException ("Associated data is not Model or Map." );
301
343
}
302
344
}
303
345
304
- private static Object extractFieldValue (String fieldName , Model instance , ModelSchema schema )
346
+ private static Object extractFieldValue (
347
+ String fieldName ,
348
+ Model instance ,
349
+ ModelSchema schema ,
350
+ Boolean extractLazyValue
351
+ )
305
352
throws AmplifyException {
306
353
if (instance instanceof SerializedModel ) {
307
354
SerializedModel serializedModel = (SerializedModel ) instance ;
@@ -316,7 +363,13 @@ private static Object extractFieldValue(String fieldName, Model instance, ModelS
316
363
try {
317
364
Field privateField = instance .getClass ().getDeclaredField (fieldName );
318
365
privateField .setAccessible (true );
319
- return privateField .get (instance );
366
+ Object fieldInstance = privateField .get (instance );
367
+ // In some cases, we don't want to return a ModelReference value. If extractLazyValue
368
+ // is set, we unwrap the reference to grab to value underneath
369
+ if (extractLazyValue && fieldInstance != null && privateField .getType () == LoadedModelReference .class ) {
370
+ return ((LoadedModelReference <?>) fieldInstance ).getValue ();
371
+ }
372
+ return fieldInstance ;
320
373
} catch (Exception exception ) {
321
374
throw new AmplifyException (
322
375
"An invalid field was provided. " + fieldName + " is not present in " + schema .getName (),
0 commit comments