@aws-amplify/datastore 3.7.3 → 3.7.4-custom-pk.86

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/CHANGELOG.md +144 -17
  2. package/README.md +154 -0
  3. package/dist/aws-amplify-datastore.js +3740 -1599
  4. package/dist/aws-amplify-datastore.js.map +1 -1
  5. package/dist/aws-amplify-datastore.min.js +25 -95
  6. package/dist/aws-amplify-datastore.min.js.map +1 -1
  7. package/lib/authModeStrategies/multiAuthStrategy.d.ts +2 -2
  8. package/lib/authModeStrategies/multiAuthStrategy.js +32 -26
  9. package/lib/authModeStrategies/multiAuthStrategy.js.map +1 -1
  10. package/lib/datastore/datastore.d.ts +17 -16
  11. package/lib/datastore/datastore.js +291 -83
  12. package/lib/datastore/datastore.js.map +1 -1
  13. package/lib/index.d.ts +3 -19
  14. package/lib/predicates/index.d.ts +3 -2
  15. package/lib/predicates/index.js +12 -2
  16. package/lib/predicates/index.js.map +1 -1
  17. package/lib/storage/adapter/AsyncStorageAdapter.d.ts +6 -2
  18. package/lib/storage/adapter/AsyncStorageAdapter.js +361 -193
  19. package/lib/storage/adapter/AsyncStorageAdapter.js.map +1 -1
  20. package/lib/storage/adapter/AsyncStorageDatabase.d.ts +4 -4
  21. package/lib/storage/adapter/AsyncStorageDatabase.js +59 -28
  22. package/lib/storage/adapter/AsyncStorageDatabase.js.map +1 -1
  23. package/lib/storage/adapter/InMemoryStore.js +2 -2
  24. package/lib/storage/adapter/InMemoryStore.js.map +1 -1
  25. package/lib/storage/adapter/IndexedDBAdapter.d.ts +7 -2
  26. package/lib/storage/adapter/IndexedDBAdapter.js +454 -252
  27. package/lib/storage/adapter/IndexedDBAdapter.js.map +1 -1
  28. package/lib/storage/adapter/index.d.ts +1 -1
  29. package/lib/storage/storage.d.ts +3 -3
  30. package/lib/storage/storage.js +106 -28
  31. package/lib/storage/storage.js.map +1 -1
  32. package/lib/sync/index.d.ts +23 -7
  33. package/lib/sync/index.js +18 -17
  34. package/lib/sync/index.js.map +1 -1
  35. package/lib/sync/merger.d.ts +3 -3
  36. package/lib/sync/merger.js +8 -6
  37. package/lib/sync/merger.js.map +1 -1
  38. package/lib/sync/outbox.d.ts +2 -2
  39. package/lib/sync/outbox.js +12 -9
  40. package/lib/sync/outbox.js.map +1 -1
  41. package/lib/sync/processors/errorMaps.d.ts +17 -0
  42. package/lib/sync/processors/errorMaps.js +125 -0
  43. package/lib/sync/processors/errorMaps.js.map +1 -0
  44. package/lib/sync/processors/mutation.d.ts +22 -4
  45. package/lib/sync/processors/mutation.js +119 -51
  46. package/lib/sync/processors/mutation.js.map +1 -1
  47. package/lib/sync/processors/subscription.d.ts +4 -2
  48. package/lib/sync/processors/subscription.js +76 -41
  49. package/lib/sync/processors/subscription.js.map +1 -1
  50. package/lib/sync/processors/sync.d.ts +4 -2
  51. package/lib/sync/processors/sync.js +62 -27
  52. package/lib/sync/processors/sync.js.map +1 -1
  53. package/lib/sync/utils.d.ts +3 -2
  54. package/lib/sync/utils.js +58 -7
  55. package/lib/sync/utils.js.map +1 -1
  56. package/lib/types.d.ts +90 -34
  57. package/lib/types.js +31 -2
  58. package/lib/types.js.map +1 -1
  59. package/lib/util.d.ts +48 -33
  60. package/lib/util.js +219 -57
  61. package/lib/util.js.map +1 -1
  62. package/lib-esm/authModeStrategies/multiAuthStrategy.d.ts +2 -2
  63. package/lib-esm/authModeStrategies/multiAuthStrategy.js +32 -26
  64. package/lib-esm/authModeStrategies/multiAuthStrategy.js.map +1 -1
  65. package/lib-esm/datastore/datastore.d.ts +17 -16
  66. package/lib-esm/datastore/datastore.js +293 -85
  67. package/lib-esm/datastore/datastore.js.map +1 -1
  68. package/lib-esm/index.d.ts +3 -19
  69. package/lib-esm/predicates/index.d.ts +3 -2
  70. package/lib-esm/predicates/index.js +13 -3
  71. package/lib-esm/predicates/index.js.map +1 -1
  72. package/lib-esm/storage/adapter/AsyncStorageAdapter.d.ts +6 -2
  73. package/lib-esm/storage/adapter/AsyncStorageAdapter.js +361 -193
  74. package/lib-esm/storage/adapter/AsyncStorageAdapter.js.map +1 -1
  75. package/lib-esm/storage/adapter/AsyncStorageDatabase.d.ts +4 -4
  76. package/lib-esm/storage/adapter/AsyncStorageDatabase.js +60 -29
  77. package/lib-esm/storage/adapter/AsyncStorageDatabase.js.map +1 -1
  78. package/lib-esm/storage/adapter/InMemoryStore.js +2 -2
  79. package/lib-esm/storage/adapter/InMemoryStore.js.map +1 -1
  80. package/lib-esm/storage/adapter/IndexedDBAdapter.d.ts +7 -2
  81. package/lib-esm/storage/adapter/IndexedDBAdapter.js +454 -252
  82. package/lib-esm/storage/adapter/IndexedDBAdapter.js.map +1 -1
  83. package/lib-esm/storage/adapter/index.d.ts +1 -1
  84. package/lib-esm/storage/storage.d.ts +3 -3
  85. package/lib-esm/storage/storage.js +106 -28
  86. package/lib-esm/storage/storage.js.map +1 -1
  87. package/lib-esm/sync/index.d.ts +23 -7
  88. package/lib-esm/sync/index.js +20 -19
  89. package/lib-esm/sync/index.js.map +1 -1
  90. package/lib-esm/sync/merger.d.ts +3 -3
  91. package/lib-esm/sync/merger.js +8 -6
  92. package/lib-esm/sync/merger.js.map +1 -1
  93. package/lib-esm/sync/outbox.d.ts +2 -2
  94. package/lib-esm/sync/outbox.js +13 -10
  95. package/lib-esm/sync/outbox.js.map +1 -1
  96. package/lib-esm/sync/processors/errorMaps.d.ts +17 -0
  97. package/lib-esm/sync/processors/errorMaps.js +119 -0
  98. package/lib-esm/sync/processors/errorMaps.js.map +1 -0
  99. package/lib-esm/sync/processors/mutation.d.ts +22 -4
  100. package/lib-esm/sync/processors/mutation.js +122 -54
  101. package/lib-esm/sync/processors/mutation.js.map +1 -1
  102. package/lib-esm/sync/processors/subscription.d.ts +4 -2
  103. package/lib-esm/sync/processors/subscription.js +77 -42
  104. package/lib-esm/sync/processors/subscription.js.map +1 -1
  105. package/lib-esm/sync/processors/sync.d.ts +4 -2
  106. package/lib-esm/sync/processors/sync.js +62 -27
  107. package/lib-esm/sync/processors/sync.js.map +1 -1
  108. package/lib-esm/sync/utils.d.ts +3 -2
  109. package/lib-esm/sync/utils.js +58 -8
  110. package/lib-esm/sync/utils.js.map +1 -1
  111. package/lib-esm/types.d.ts +90 -34
  112. package/lib-esm/types.js +29 -3
  113. package/lib-esm/types.js.map +1 -1
  114. package/lib-esm/util.d.ts +48 -33
  115. package/lib-esm/util.js +207 -54
  116. package/lib-esm/util.js.map +1 -1
  117. package/package.json +10 -9
  118. package/src/authModeStrategies/multiAuthStrategy.ts +24 -21
  119. package/src/datastore/datastore.ts +448 -148
  120. package/src/predicates/index.ts +32 -10
  121. package/src/storage/adapter/AsyncStorageAdapter.ts +305 -78
  122. package/src/storage/adapter/AsyncStorageDatabase.ts +65 -26
  123. package/src/storage/adapter/InMemoryStore.ts +2 -2
  124. package/src/storage/adapter/IndexedDBAdapter.ts +344 -101
  125. package/src/storage/adapter/index.ts +1 -1
  126. package/src/storage/storage.ts +85 -24
  127. package/src/sync/index.ts +74 -58
  128. package/src/sync/merger.ts +16 -4
  129. package/src/sync/outbox.ts +22 -8
  130. package/src/sync/processors/errorMaps.ts +93 -0
  131. package/src/sync/processors/mutation.ts +143 -84
  132. package/src/sync/processors/subscription.ts +67 -36
  133. package/src/sync/processors/sync.ts +37 -12
  134. package/src/sync/utils.ts +67 -16
  135. package/src/types.ts +252 -50
  136. package/src/util.ts +256 -42
  137. package/ssr/package.json +1 -1
@@ -29,7 +29,7 @@ export interface Adapter extends SystemComponent {
29
29
  firstOrLast: QueryOne
30
30
  ): Promise<T | undefined>;
31
31
  batchSave<T extends PersistentModel>(
32
- modelConstructor: PersistentModelConstructor<any>,
32
+ modelConstructor: PersistentModelConstructor<T>,
33
33
  items: ModelInstanceMetadata[]
34
34
  ): Promise<[T, OpType][]>;
35
35
  }
@@ -16,8 +16,10 @@ import {
16
16
  PredicatesGroup,
17
17
  QueryOne,
18
18
  SchemaNamespace,
19
+ InternalSubscriptionMessage,
19
20
  SubscriptionMessage,
20
21
  isTargetNameAssociation,
22
+ // isTargetNamesAssociation,
21
23
  } from '../types';
22
24
  import {
23
25
  isModelConstructor,
@@ -25,11 +27,12 @@ import {
25
27
  validatePredicate,
26
28
  valuesEqual,
27
29
  } from '../util';
30
+ import { getIdentifierValue } from '../sync/utils';
28
31
  import { Adapter } from './adapter';
29
32
  import getDefaultAdapter from './adapter/getDefaultAdapter';
30
33
 
31
34
  export type StorageSubscriptionMessage<T extends PersistentModel> =
32
- SubscriptionMessage<T> & {
35
+ InternalSubscriptionMessage<T> & {
33
36
  mutator?: Symbol;
34
37
  };
35
38
 
@@ -111,7 +114,7 @@ class StorageClass implements StorageFacade {
111
114
  const result = await this.adapter.save(model, condition);
112
115
 
113
116
  result.forEach(r => {
114
- const [originalElement, opType] = r;
117
+ const [savedElement, opType] = r;
115
118
 
116
119
  // truthy when save is called by the Merger
117
120
  const syncResponse = !!mutator;
@@ -120,9 +123,21 @@ class StorageClass implements StorageFacade {
120
123
  // don't attempt to calc mutation input when storage.save
121
124
  // is called by Merger, i.e., when processing an AppSync response
122
125
  if (opType === OpType.UPDATE && !syncResponse) {
126
+ //
127
+ // TODO: LOOK!!!
128
+ // the `model` used here is in effect regardless of what model
129
+ // comes back from adapter.save().
130
+ // Prior to fix, SQLite adapter had been returning two models
131
+ // of different types, resulting in invalid outbox entries.
132
+ //
133
+ // the bug is essentially fixed in SQLite adapter.
134
+ // leaving as-is, because it's currently unclear whether anything
135
+ // depends on this remaining as-is.
136
+ //
137
+
123
138
  updateMutationInput = this.getUpdateMutationInput(
124
139
  model,
125
- originalElement,
140
+ savedElement,
126
141
  patchesTuple
127
142
  );
128
143
  // // an update without changed user fields
@@ -132,11 +147,10 @@ class StorageClass implements StorageFacade {
132
147
  }
133
148
  }
134
149
 
135
- const element = updateMutationInput || originalElement;
150
+ const element = updateMutationInput || savedElement;
136
151
 
137
- const modelConstructor = (
138
- Object.getPrototypeOf(originalElement) as Object
139
- ).constructor as PersistentModelConstructor<T>;
152
+ const modelConstructor = (Object.getPrototypeOf(savedElement) as Object)
153
+ .constructor as PersistentModelConstructor<T>;
140
154
 
141
155
  this.pushStream.next({
142
156
  model: modelConstructor,
@@ -144,6 +158,7 @@ class StorageClass implements StorageFacade {
144
158
  element,
145
159
  mutator,
146
160
  condition: ModelPredicateCreator.getPredicates(condition, false),
161
+ savedElement,
147
162
  });
148
163
  });
149
164
 
@@ -175,7 +190,21 @@ class StorageClass implements StorageFacade {
175
190
  condition
176
191
  );
177
192
 
178
- const modelIds = new Set(models.map(({ id }) => id));
193
+ const modelConstructor = isModelConstructor(modelOrModelConstructor)
194
+ ? modelOrModelConstructor
195
+ : (Object.getPrototypeOf(modelOrModelConstructor || {})
196
+ .constructor as PersistentModelConstructor<T>);
197
+ const namespaceName = this.namespaceResolver(modelConstructor);
198
+
199
+ const modelDefinition =
200
+ this.schema.namespaces[namespaceName].models[modelConstructor.name];
201
+
202
+ const modelIds = new Set(
203
+ models.map(model => {
204
+ const modelId = getIdentifierValue(modelDefinition, model);
205
+ return modelId;
206
+ })
207
+ );
179
208
 
180
209
  if (
181
210
  !isModelConstructor(modelOrModelConstructor) &&
@@ -191,7 +220,8 @@ class StorageClass implements StorageFacade {
191
220
  let theCondition: PredicatesGroup<any>;
192
221
 
193
222
  if (!isModelConstructor(modelOrModelConstructor)) {
194
- theCondition = modelIds.has(model.id)
223
+ const modelId = getIdentifierValue(modelDefinition, model);
224
+ theCondition = modelIds.has(modelId)
195
225
  ? ModelPredicateCreator.getPredicates(condition, false)
196
226
  : undefined;
197
227
  }
@@ -324,24 +354,55 @@ class StorageClass implements StorageFacade {
324
354
 
325
355
  // set original values for these fields
326
356
  updatedFields.forEach((field: string) => {
327
- const targetName: any = isTargetNameAssociation(
357
+ // CPK TODO: rename:
358
+ const targetNames: any = isTargetNameAssociation(
328
359
  fields[field]?.association
329
360
  );
330
361
 
331
- // if field refers to a belongsTo relation, use the target field instead
332
- const key = targetName || field;
362
+ if (Array.isArray(targetNames)) {
363
+ // if field refers to a belongsTo relation, use the target field instead
364
+
365
+ for (const targetName of targetNames) {
366
+ // check field values by value. Ignore unchanged fields
367
+ if (!valuesEqual(source[targetName], originalElement[targetName])) {
368
+ // if the field was updated to 'undefined', replace with 'null' for compatibility with JSON and GraphQL
369
+
370
+ updatedElement[targetName] =
371
+ originalElement[targetName] === undefined
372
+ ? null
373
+ : originalElement[targetName];
374
+
375
+ for (const fieldSet of compositeKeys) {
376
+ // include all of the fields that comprise the composite key
377
+ if (fieldSet.has(targetName)) {
378
+ for (const compositeField of fieldSet) {
379
+ updatedElement[compositeField] =
380
+ originalElement[compositeField];
381
+ }
382
+ }
383
+ }
384
+ }
385
+ }
386
+ } else {
387
+ // Backwards compatibility pre-CPK
388
+
389
+ // if field refers to a belongsTo relation, use the target field instead
390
+ const key = targetNames || field;
333
391
 
334
- // check field values by value. Ignore unchanged fields
335
- if (!valuesEqual(source[key], originalElement[key])) {
336
- // if the field was updated to 'undefined', replace with 'null' for compatibility with JSON and GraphQL
337
- updatedElement[key] =
338
- originalElement[key] === undefined ? null : originalElement[key];
392
+ // check field values by value. Ignore unchanged fields
393
+ if (!valuesEqual(source[key], originalElement[key])) {
394
+ // if the field was updated to 'undefined', replace with 'null' for compatibility with JSON and GraphQL
339
395
 
340
- for (const fieldSet of compositeKeys) {
341
- // include all of the fields that comprise the composite key
342
- if (fieldSet.has(key)) {
343
- for (const compositeField of fieldSet) {
344
- updatedElement[compositeField] = originalElement[compositeField];
396
+ updatedElement[key] =
397
+ originalElement[key] === undefined ? null : originalElement[key];
398
+
399
+ for (const fieldSet of compositeKeys) {
400
+ // include all of the fields that comprise the composite key
401
+ if (fieldSet.has(key)) {
402
+ for (const compositeField of fieldSet) {
403
+ updatedElement[compositeField] =
404
+ originalElement[compositeField];
405
+ }
345
406
  }
346
407
  }
347
408
  }
@@ -408,7 +469,7 @@ class ExclusiveStorage implements StorageFacade {
408
469
  patchesTuple?: [Patch[], PersistentModel]
409
470
  ): Promise<[T, OpType.INSERT | OpType.UPDATE][]> {
410
471
  return this.runExclusive<[T, OpType.INSERT | OpType.UPDATE][]>(storage =>
411
- storage.save<T>(model, condition, mutator, patchesTuple)
472
+ storage.save(model, condition, mutator, patchesTuple)
412
473
  );
413
474
  }
414
475
 
@@ -476,7 +537,7 @@ class ExclusiveStorage implements StorageFacade {
476
537
  }
477
538
 
478
539
  batchSave<T extends PersistentModel>(
479
- modelConstructor: PersistentModelConstructor<any>,
540
+ modelConstructor: PersistentModelConstructor<T>,
480
541
  items: ModelInstanceMetadata[]
481
542
  ): Promise<[T, OpType][]> {
482
543
  return this.storage.batchSave(modelConstructor, items);
package/src/sync/index.ts CHANGED
@@ -21,6 +21,10 @@ import {
21
21
  TypeConstructorMap,
22
22
  ModelPredicate,
23
23
  AuthModeStrategy,
24
+ ManagedIdentifier,
25
+ OptionallyManagedIdentifier,
26
+ __modelMeta__,
27
+ AmplifyContext,
24
28
  } from '../types';
25
29
  import { exhaustiveCheck, getNow, SYNC, USER } from '../util';
26
30
  import DataStoreConnectivity from './datastoreConnectivity';
@@ -31,6 +35,7 @@ import { CONTROL_MSG, SubscriptionProcessor } from './processors/subscription';
31
35
  import { SyncProcessor } from './processors/sync';
32
36
  import {
33
37
  createMutationInstanceFromModelOperation,
38
+ getIdentifierValue,
34
39
  predicateToGraphQLCondition,
35
40
  TransformerMutationType,
36
41
  } from './utils';
@@ -45,25 +50,26 @@ type StartParams = {
45
50
  };
46
51
 
47
52
  export declare class MutationEvent {
48
- constructor(init: ModelInit<MutationEvent>);
49
- static copyOf(
50
- src: MutationEvent,
51
- mutator: (draft: MutableModel<MutationEvent>) => void | MutationEvent
52
- ): MutationEvent;
53
+ readonly [__modelMeta__]: {
54
+ identifier: OptionallyManagedIdentifier<MutationEvent, 'id'>;
55
+ };
53
56
  public readonly id: string;
54
57
  public readonly model: string;
55
58
  public readonly operation: TransformerMutationType;
56
59
  public readonly modelId: string;
57
60
  public readonly condition: string;
58
- public data: string;
61
+ public readonly data: string;
62
+ constructor(init: ModelInit<MutationEvent>);
63
+ static copyOf(
64
+ src: MutationEvent,
65
+ mutator: (draft: MutableModel<MutationEvent>) => void | MutationEvent
66
+ ): MutationEvent;
59
67
  }
60
68
 
61
- declare class ModelMetadata {
62
- constructor(init: ModelInit<ModelMetadata>);
63
- static copyOf(
64
- src: ModelMetadata,
65
- mutator: (draft: MutableModel<ModelMetadata>) => void | ModelMetadata
66
- ): ModelMetadata;
69
+ export declare class ModelMetadata {
70
+ readonly [__modelMeta__]: {
71
+ identifier: ManagedIdentifier<ModelMetadata, 'id'>;
72
+ };
67
73
  public readonly id: string;
68
74
  public readonly namespace: string;
69
75
  public readonly model: string;
@@ -71,6 +77,11 @@ declare class ModelMetadata {
71
77
  public readonly lastSync?: number;
72
78
  public readonly lastFullSync?: number;
73
79
  public readonly lastSyncPredicate?: null | string;
80
+ constructor(init: ModelInit<ModelMetadata>);
81
+ static copyOf(
82
+ src: ModelMetadata,
83
+ mutator: (draft: MutableModel<ModelMetadata>) => void | ModelMetadata
84
+ ): ModelMetadata;
74
85
  }
75
86
 
76
87
  export enum ControlMessage {
@@ -113,17 +124,16 @@ export class SyncEngine {
113
124
  private readonly userModelClasses: TypeConstructorMap,
114
125
  private readonly storage: Storage,
115
126
  private readonly modelInstanceCreator: ModelInstanceCreator,
116
- private readonly maxRecordsToSync: number,
117
- private readonly syncPageSize: number,
118
127
  conflictHandler: ConflictHandler,
119
128
  errorHandler: ErrorHandler,
120
129
  private readonly syncPredicates: WeakMap<SchemaModel, ModelPredicate<any>>,
121
130
  private readonly amplifyConfig: Record<string, any> = {},
122
- private readonly authModeStrategy: AuthModeStrategy
131
+ private readonly authModeStrategy: AuthModeStrategy,
132
+ private readonly amplifyContext: AmplifyContext
123
133
  ) {
124
134
  const MutationEvent = this.modelClasses[
125
135
  'MutationEvent'
126
- ] as PersistentModelConstructor<any>;
136
+ ] as PersistentModelConstructor<MutationEvent>;
127
137
 
128
138
  this.outbox = new MutationEventOutbox(
129
139
  this.schema,
@@ -138,14 +148,20 @@ export class SyncEngine {
138
148
  this.schema,
139
149
  this.syncPredicates,
140
150
  this.amplifyConfig,
141
- this.authModeStrategy
151
+ this.authModeStrategy,
152
+ errorHandler,
153
+ this.amplifyContext
142
154
  );
155
+
143
156
  this.subscriptionsProcessor = new SubscriptionProcessor(
144
157
  this.schema,
145
158
  this.syncPredicates,
146
159
  this.amplifyConfig,
147
- this.authModeStrategy
160
+ this.authModeStrategy,
161
+ errorHandler,
162
+ this.amplifyContext
148
163
  );
164
+
149
165
  this.mutationsProcessor = new MutationProcessor(
150
166
  this.schema,
151
167
  this.storage,
@@ -155,9 +171,11 @@ export class SyncEngine {
155
171
  MutationEvent,
156
172
  this.amplifyConfig,
157
173
  this.authModeStrategy,
174
+ errorHandler,
158
175
  conflictHandler,
159
- errorHandler
176
+ this.amplifyContext
160
177
  );
178
+
161
179
  this.datastoreConnectivity = new DataStoreConnectivity();
162
180
  }
163
181
 
@@ -189,11 +207,9 @@ export class SyncEngine {
189
207
  });
190
208
 
191
209
  let ctlSubsObservable: Observable<CONTROL_MSG>;
192
- let dataSubsObservable: Observable<[
193
- TransformerMutationType,
194
- SchemaModel,
195
- PersistentModel
196
- ]>;
210
+ let dataSubsObservable: Observable<
211
+ [TransformerMutationType, SchemaModel, PersistentModel]
212
+ >;
197
213
 
198
214
  if (isNode) {
199
215
  logger.warn(
@@ -244,8 +260,8 @@ export class SyncEngine {
244
260
  //#region Base & Sync queries
245
261
  try {
246
262
  await new Promise((resolve, reject) => {
247
- const syncQuerySubscription = this.syncQueriesObservable().subscribe(
248
- {
263
+ const syncQuerySubscription =
264
+ this.syncQueriesObservable().subscribe({
249
265
  next: message => {
250
266
  const { type } = message;
251
267
 
@@ -263,8 +279,7 @@ export class SyncEngine {
263
279
  error: error => {
264
280
  reject(error);
265
281
  },
266
- }
267
- );
282
+ });
268
283
 
269
284
  if (syncQuerySubscription) {
270
285
  subscriptions.push(syncQuerySubscription);
@@ -291,12 +306,11 @@ export class SyncEngine {
291
306
  );
292
307
 
293
308
  this.storage.runExclusive(storage =>
294
- this.modelMerger.merge(storage, model)
309
+ this.modelMerger.merge(storage, model, modelDefinition)
295
310
  );
296
311
 
297
312
  observer.next({
298
- type:
299
- ControlMessage.SYNC_ENGINE_OUTBOX_MUTATION_PROCESSED,
313
+ type: ControlMessage.SYNC_ENGINE_OUTBOX_MUTATION_PROCESSED,
300
314
  data: {
301
315
  model: modelConstructor,
302
316
  element: model,
@@ -329,7 +343,7 @@ export class SyncEngine {
329
343
  );
330
344
 
331
345
  this.storage.runExclusive(storage =>
332
- this.modelMerger.merge(storage, model)
346
+ this.modelMerger.merge(storage, model, modelDefinition)
333
347
  );
334
348
  }
335
349
  )
@@ -358,18 +372,20 @@ export class SyncEngine {
358
372
  .observe(null, null, ownSymbol)
359
373
  .filter(({ model }) => {
360
374
  const modelDefinition = this.getModelDefinition(model);
361
-
362
375
  return modelDefinition.syncable === true;
363
376
  })
364
377
  .subscribe({
365
378
  next: async ({ opType, model, element, condition }) => {
366
- const namespace = this.schema.namespaces[
367
- this.namespaceResolver(model)
368
- ];
379
+ const namespace =
380
+ this.schema.namespaces[this.namespaceResolver(model)];
369
381
  const MutationEventConstructor = this.modelClasses[
370
382
  'MutationEvent'
371
383
  ] as PersistentModelConstructor<MutationEvent>;
372
- const graphQLCondition = predicateToGraphQLCondition(condition);
384
+ const modelDefinition = this.getModelDefinition(model);
385
+ const graphQLCondition = predicateToGraphQLCondition(
386
+ condition,
387
+ modelDefinition
388
+ );
373
389
  const mutationEvent = createMutationInstanceFromModelOperation(
374
390
  namespace.relationships,
375
391
  this.getModelDefinition(model),
@@ -534,7 +550,9 @@ export class SyncEngine {
534
550
 
535
551
  const oneByOne: ModelInstanceMetadata[] = [];
536
552
  const page = items.filter(item => {
537
- if (!idsInOutbox.has(item.id)) {
553
+ const itemId = getIdentifierValue(modelDefinition, item);
554
+
555
+ if (!idsInOutbox.has(itemId)) {
538
556
  return true;
539
557
  }
540
558
 
@@ -547,7 +565,8 @@ export class SyncEngine {
547
565
  for (const item of oneByOne) {
548
566
  const opType = await this.modelMerger.merge(
549
567
  storage,
550
- item
568
+ item,
569
+ modelDefinition
551
570
  );
552
571
 
553
572
  if (opType !== undefined) {
@@ -559,7 +578,8 @@ export class SyncEngine {
559
578
  ...(await this.modelMerger.mergePage(
560
579
  storage,
561
580
  modelConstructor,
562
- page
581
+ page,
582
+ modelDefinition
563
583
  ))
564
584
  );
565
585
 
@@ -603,16 +623,15 @@ export class SyncEngine {
603
623
  isFullSync ? startedAt : lastFullSync
604
624
  );
605
625
 
606
- modelMetadata = (this.modelClasses
607
- .ModelMetadata as PersistentModelConstructor<any>).copyOf(
608
- modelMetadata,
609
- draft => {
610
- draft.lastSync = startedAt;
611
- draft.lastFullSync = isFullSync
612
- ? startedAt
613
- : modelMetadata.lastFullSync;
614
- }
615
- );
626
+ modelMetadata = (
627
+ this.modelClasses
628
+ .ModelMetadata as PersistentModelConstructor<ModelMetadata>
629
+ ).copyOf(modelMetadata, draft => {
630
+ draft.lastSync = startedAt;
631
+ draft.lastFullSync = isFullSync
632
+ ? startedAt
633
+ : modelMetadata.lastFullSync;
634
+ });
616
635
 
617
636
  await this.storage.save(
618
637
  modelMetadata,
@@ -707,7 +726,7 @@ export class SyncEngine {
707
726
 
708
727
  private async setupModels(params: StartParams) {
709
728
  const { fullSyncInterval } = params;
710
- const ModelMetadata = this.modelClasses
729
+ const ModelMetadataConstructor = this.modelClasses
711
730
  .ModelMetadata as PersistentModelConstructor<ModelMetadata>;
712
731
 
713
732
  const models: [string, SchemaModel][] = [];
@@ -739,7 +758,7 @@ export class SyncEngine {
739
758
 
740
759
  if (modelMetadata === undefined) {
741
760
  [[savedModel]] = await this.storage.save(
742
- this.modelInstanceCreator(ModelMetadata, {
761
+ this.modelInstanceCreator(ModelMetadataConstructor, {
743
762
  model: model.name,
744
763
  namespace,
745
764
  lastSync: null,
@@ -757,9 +776,7 @@ export class SyncEngine {
757
776
  const syncPredicateUpdated = prevSyncPredicate !== lastSyncPredicate;
758
777
 
759
778
  [[savedModel]] = await this.storage.save(
760
- (this.modelClasses.ModelMetadata as PersistentModelConstructor<
761
- any
762
- >).copyOf(modelMetadata, draft => {
779
+ ModelMetadataConstructor.copyOf(modelMetadata, draft => {
763
780
  draft.fullSyncInterval = fullSyncInterval;
764
781
  // perform a base sync if the syncPredicate changed in between calls to DataStore.start
765
782
  // ensures that the local store contains all the data specified by the syncExpression
@@ -819,9 +836,8 @@ export class SyncEngine {
819
836
  ): SchemaModel {
820
837
  const namespaceName = this.namespaceResolver(modelConstructor);
821
838
 
822
- const modelDefinition = this.schema.namespaces[namespaceName].models[
823
- modelConstructor.name
824
- ];
839
+ const modelDefinition =
840
+ this.schema.namespaces[namespaceName].models[modelConstructor.name];
825
841
 
826
842
  return modelDefinition;
827
843
  }
@@ -3,8 +3,12 @@ import {
3
3
  ModelInstanceMetadata,
4
4
  OpType,
5
5
  PersistentModelConstructor,
6
+ SchemaModel,
6
7
  } from '../types';
7
8
  import { MutationEventOutbox } from './outbox';
9
+ import { getIdentifierValue } from './utils';
10
+
11
+ // https://github.com/aws-amplify/amplify-js/blob/datastore-docs/packages/datastore/docs/sync-engine.md#merger
8
12
  class ModelMerger {
9
13
  constructor(
10
14
  private readonly outbox: MutationEventOutbox,
@@ -13,10 +17,15 @@ class ModelMerger {
13
17
 
14
18
  public async merge<T extends ModelInstanceMetadata>(
15
19
  storage: Storage,
16
- model: T
20
+ model: T,
21
+ modelDefinition: SchemaModel
17
22
  ): Promise<OpType> {
18
23
  let result: OpType;
19
- const mutationsForModel = await this.outbox.getForModel(storage, model);
24
+ const mutationsForModel = await this.outbox.getForModel(
25
+ storage,
26
+ model,
27
+ modelDefinition
28
+ );
20
29
 
21
30
  const isDelete = model._deleted;
22
31
 
@@ -35,13 +44,16 @@ class ModelMerger {
35
44
  public async mergePage(
36
45
  storage: Storage,
37
46
  modelConstructor: PersistentModelConstructor<any>,
38
- items: ModelInstanceMetadata[]
47
+ items: ModelInstanceMetadata[],
48
+ modelDefinition: SchemaModel
39
49
  ): Promise<[ModelInstanceMetadata, OpType][]> {
40
50
  const itemsMap: Map<string, ModelInstanceMetadata> = new Map();
41
51
 
42
52
  for (const item of items) {
43
53
  // merge items by model id. Latest record for a given id remains.
44
- itemsMap.set(item.id, item);
54
+ const modelId = getIdentifierValue(modelDefinition, item);
55
+
56
+ itemsMap.set(modelId, item);
45
57
  }
46
58
 
47
59
  const page = [...itemsMap.values()];
@@ -11,11 +11,13 @@ import {
11
11
  PersistentModel,
12
12
  PersistentModelConstructor,
13
13
  QueryOne,
14
+ SchemaModel,
14
15
  } from '../types';
15
16
  import { USER, SYNC, valuesEqual } from '../util';
16
- import { TransformerMutationType } from './utils';
17
+ import { getIdentifierValue, TransformerMutationType } from './utils';
17
18
 
18
19
  // TODO: Persist deleted ids
20
+ // https://github.com/aws-amplify/amplify-js/blob/datastore-docs/packages/datastore/docs/sync-engine.md#outbox
19
21
  class MutationEventOutbox {
20
22
  private inProgressMutationEventId: string;
21
23
 
@@ -34,6 +36,8 @@ class MutationEventOutbox {
34
36
  const mutationEventModelDefinition =
35
37
  this.schema.namespaces[SYNC].models['MutationEvent'];
36
38
 
39
+ // `id` is the id of the record in the mutationEvent
40
+ // `modelId` is the id of the actual record that was mutated
37
41
  const predicate = ModelPredicateCreator.createFromExisting<MutationEvent>(
38
42
  mutationEventModelDefinition,
39
43
  c =>
@@ -42,13 +46,16 @@ class MutationEventOutbox {
42
46
  .id('ne', this.inProgressMutationEventId)
43
47
  );
44
48
 
49
+ // Check if there are any other records with same id
45
50
  const [first] = await s.query(this.MutationEvent, predicate);
46
51
 
52
+ // No other record with same modelId, so enqueue
47
53
  if (first === undefined) {
48
54
  await s.save(mutationEvent, undefined, this.ownSymbol);
49
55
  return;
50
56
  }
51
57
 
58
+ // There was an enqueued mutation for the modelId, so continue
52
59
  const { operation: incomingMutationType } = mutationEvent;
53
60
 
54
61
  if (first.operation === TransformerMutationType.CREATE) {
@@ -121,16 +128,19 @@ class MutationEventOutbox {
121
128
 
122
129
  public async getForModel<T extends PersistentModel>(
123
130
  storage: StorageFacade,
124
- model: T
131
+ model: T,
132
+ userModelDefinition: SchemaModel
125
133
  ): Promise<MutationEvent[]> {
126
134
  const mutationEventModelDefinition =
127
135
  this.schema.namespaces[SYNC].models.MutationEvent;
128
136
 
137
+ const modelId = getIdentifierValue(userModelDefinition, model);
138
+
129
139
  const mutationEvents = await storage.query(
130
140
  this.MutationEvent,
131
141
  ModelPredicateCreator.createFromExisting(
132
142
  mutationEventModelDefinition,
133
- c => c.modelId('eq', model.id)
143
+ c => c.modelId('eq', modelId)
134
144
  )
135
145
  );
136
146
 
@@ -186,9 +196,14 @@ class MutationEventOutbox {
186
196
  const mutationEventModelDefinition =
187
197
  this.schema.namespaces[SYNC].models['MutationEvent'];
188
198
 
199
+ const userModelDefinition =
200
+ this.schema.namespaces['user'].models[head.model];
201
+
202
+ const recordId = getIdentifierValue(userModelDefinition, record);
203
+
189
204
  const predicate = ModelPredicateCreator.createFromExisting<MutationEvent>(
190
205
  mutationEventModelDefinition,
191
- c => c.modelId('eq', record.id).id('ne', this.inProgressMutationEventId)
206
+ c => c.modelId('eq', recordId).id('ne', this.inProgressMutationEventId)
192
207
  );
193
208
 
194
209
  const outdatedMutations = await storage.query(
@@ -223,11 +238,11 @@ class MutationEventOutbox {
223
238
  previous: MutationEvent,
224
239
  current: MutationEvent
225
240
  ): MutationEvent {
226
- const { _version, id, _lastChangedAt, _deleted, ...previousData } =
227
- JSON.parse(previous.data);
241
+ const { _version, _lastChangedAt, _deleted, ...previousData } = JSON.parse(
242
+ previous.data
243
+ );
228
244
 
229
245
  const {
230
- id: __id,
231
246
  _version: __version,
232
247
  _lastChangedAt: __lastChangedAt,
233
248
  _deleted: __deleted,
@@ -235,7 +250,6 @@ class MutationEventOutbox {
235
250
  } = JSON.parse(current.data);
236
251
 
237
252
  const data = JSON.stringify({
238
- id,
239
253
  _version,
240
254
  _lastChangedAt,
241
255
  _deleted,