@aws-amplify/datastore 3.12.12 → 3.12.13-custom-pk.1
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.
- package/dist/aws-amplify-datastore.js +1854 -965
- package/dist/aws-amplify-datastore.js.map +1 -1
- package/dist/aws-amplify-datastore.min.js +7 -7
- package/dist/aws-amplify-datastore.min.js.map +1 -1
- package/lib/datastore/datastore.d.ts +13 -16
- package/lib/datastore/datastore.js +130 -63
- package/lib/datastore/datastore.js.map +1 -1
- package/lib/index.d.ts +3 -19
- package/lib/predicates/index.d.ts +3 -2
- package/lib/predicates/index.js +12 -2
- package/lib/predicates/index.js.map +1 -1
- package/lib/storage/adapter/AsyncStorageAdapter.d.ts +4 -3
- package/lib/storage/adapter/AsyncStorageAdapter.js +354 -203
- package/lib/storage/adapter/AsyncStorageAdapter.js.map +1 -1
- package/lib/storage/adapter/AsyncStorageDatabase.d.ts +14 -4
- package/lib/storage/adapter/AsyncStorageDatabase.js +65 -28
- package/lib/storage/adapter/AsyncStorageDatabase.js.map +1 -1
- package/lib/storage/adapter/IndexedDBAdapter.d.ts +5 -4
- package/lib/storage/adapter/IndexedDBAdapter.js +389 -267
- package/lib/storage/adapter/IndexedDBAdapter.js.map +1 -1
- package/lib/storage/adapter/index.d.ts +1 -1
- package/lib/storage/storage.d.ts +1 -1
- package/lib/storage/storage.js +92 -27
- package/lib/storage/storage.js.map +1 -1
- package/lib/sync/index.d.ts +21 -4
- package/lib/sync/index.js +13 -11
- package/lib/sync/index.js.map +1 -1
- package/lib/sync/merger.d.ts +3 -3
- package/lib/sync/merger.js +7 -6
- package/lib/sync/merger.js.map +1 -1
- package/lib/sync/outbox.d.ts +2 -2
- package/lib/sync/outbox.js +11 -9
- package/lib/sync/outbox.js.map +1 -1
- package/lib/sync/processors/mutation.js +60 -42
- package/lib/sync/processors/mutation.js.map +1 -1
- package/lib/sync/processors/subscription.js.map +1 -1
- package/lib/sync/processors/sync.js.map +1 -1
- package/lib/sync/utils.d.ts +3 -2
- package/lib/sync/utils.js +61 -8
- package/lib/sync/utils.js.map +1 -1
- package/lib/types.d.ts +64 -25
- package/lib/types.js +10 -1
- package/lib/types.js.map +1 -1
- package/lib/util.d.ts +56 -24
- package/lib/util.js +334 -170
- package/lib/util.js.map +1 -1
- package/lib-esm/datastore/datastore.d.ts +13 -16
- package/lib-esm/datastore/datastore.js +132 -65
- package/lib-esm/datastore/datastore.js.map +1 -1
- package/lib-esm/index.d.ts +3 -19
- package/lib-esm/predicates/index.d.ts +3 -2
- package/lib-esm/predicates/index.js +13 -3
- package/lib-esm/predicates/index.js.map +1 -1
- package/lib-esm/storage/adapter/AsyncStorageAdapter.d.ts +4 -3
- package/lib-esm/storage/adapter/AsyncStorageAdapter.js +355 -204
- package/lib-esm/storage/adapter/AsyncStorageAdapter.js.map +1 -1
- package/lib-esm/storage/adapter/AsyncStorageDatabase.d.ts +14 -4
- package/lib-esm/storage/adapter/AsyncStorageDatabase.js +66 -29
- package/lib-esm/storage/adapter/AsyncStorageDatabase.js.map +1 -1
- package/lib-esm/storage/adapter/IndexedDBAdapter.d.ts +5 -4
- package/lib-esm/storage/adapter/IndexedDBAdapter.js +390 -268
- package/lib-esm/storage/adapter/IndexedDBAdapter.js.map +1 -1
- package/lib-esm/storage/adapter/index.d.ts +1 -1
- package/lib-esm/storage/storage.d.ts +1 -1
- package/lib-esm/storage/storage.js +92 -27
- package/lib-esm/storage/storage.js.map +1 -1
- package/lib-esm/sync/index.d.ts +21 -4
- package/lib-esm/sync/index.js +15 -13
- package/lib-esm/sync/index.js.map +1 -1
- package/lib-esm/sync/merger.d.ts +3 -3
- package/lib-esm/sync/merger.js +7 -6
- package/lib-esm/sync/merger.js.map +1 -1
- package/lib-esm/sync/outbox.d.ts +2 -2
- package/lib-esm/sync/outbox.js +12 -10
- package/lib-esm/sync/outbox.js.map +1 -1
- package/lib-esm/sync/processors/mutation.js +61 -43
- package/lib-esm/sync/processors/mutation.js.map +1 -1
- package/lib-esm/sync/processors/subscription.js.map +1 -1
- package/lib-esm/sync/processors/sync.js.map +1 -1
- package/lib-esm/sync/utils.d.ts +3 -2
- package/lib-esm/sync/utils.js +62 -10
- package/lib-esm/sync/utils.js.map +1 -1
- package/lib-esm/types.d.ts +64 -25
- package/lib-esm/types.js +9 -2
- package/lib-esm/types.js.map +1 -1
- package/lib-esm/util.d.ts +56 -24
- package/lib-esm/util.js +334 -170
- package/lib-esm/util.js.map +1 -1
- package/package.json +7 -7
- package/src/datastore/datastore.ts +253 -113
- package/src/predicates/index.ts +32 -10
- package/src/storage/adapter/AsyncStorageAdapter.ts +309 -93
- package/src/storage/adapter/AsyncStorageDatabase.ts +74 -26
- package/src/storage/adapter/IndexedDBAdapter.ts +319 -136
- package/src/storage/adapter/index.ts +1 -1
- package/src/storage/storage.ts +68 -21
- package/src/sync/index.ts +41 -26
- package/src/sync/merger.ts +14 -4
- package/src/sync/outbox.ts +21 -8
- package/src/sync/processors/mutation.ts +49 -45
- package/src/sync/processors/subscription.ts +0 -1
- package/src/sync/processors/sync.ts +1 -3
- package/src/sync/utils.ts +69 -12
- package/src/types.ts +181 -29
- package/src/util.ts +415 -176
|
@@ -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<
|
|
32
|
+
modelConstructor: PersistentModelConstructor<T>,
|
|
33
33
|
items: ModelInstanceMetadata[]
|
|
34
34
|
): Promise<[T, OpType][]>;
|
|
35
35
|
}
|
package/src/storage/storage.ts
CHANGED
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
validatePredicate,
|
|
27
27
|
valuesEqual,
|
|
28
28
|
} from '../util';
|
|
29
|
+
import { getIdentifierValue } from '../sync/utils';
|
|
29
30
|
import { Adapter } from './adapter';
|
|
30
31
|
import getDefaultAdapter from './adapter/getDefaultAdapter';
|
|
31
32
|
|
|
@@ -188,7 +189,21 @@ class StorageClass implements StorageFacade {
|
|
|
188
189
|
condition
|
|
189
190
|
);
|
|
190
191
|
|
|
191
|
-
const
|
|
192
|
+
const modelConstructor = isModelConstructor(modelOrModelConstructor)
|
|
193
|
+
? modelOrModelConstructor
|
|
194
|
+
: (Object.getPrototypeOf(modelOrModelConstructor || {})
|
|
195
|
+
.constructor as PersistentModelConstructor<T>);
|
|
196
|
+
const namespaceName = this.namespaceResolver(modelConstructor);
|
|
197
|
+
|
|
198
|
+
const modelDefinition =
|
|
199
|
+
this.schema.namespaces[namespaceName].models[modelConstructor.name];
|
|
200
|
+
|
|
201
|
+
const modelIds = new Set(
|
|
202
|
+
models.map(model => {
|
|
203
|
+
const modelId = getIdentifierValue(modelDefinition, model);
|
|
204
|
+
return modelId;
|
|
205
|
+
})
|
|
206
|
+
);
|
|
192
207
|
|
|
193
208
|
if (
|
|
194
209
|
!isModelConstructor(modelOrModelConstructor) &&
|
|
@@ -204,7 +219,8 @@ class StorageClass implements StorageFacade {
|
|
|
204
219
|
let theCondition: PredicatesGroup<any>;
|
|
205
220
|
|
|
206
221
|
if (!isModelConstructor(modelOrModelConstructor)) {
|
|
207
|
-
|
|
222
|
+
const modelId = getIdentifierValue(modelDefinition, model);
|
|
223
|
+
theCondition = modelIds.has(modelId)
|
|
208
224
|
? ModelPredicateCreator.getPredicates(condition, false)
|
|
209
225
|
: undefined;
|
|
210
226
|
}
|
|
@@ -337,30 +353,65 @@ class StorageClass implements StorageFacade {
|
|
|
337
353
|
|
|
338
354
|
// set original values for these fields
|
|
339
355
|
updatedFields.forEach((field: string) => {
|
|
340
|
-
const
|
|
356
|
+
const targetNames: any = isTargetNameAssociation(
|
|
341
357
|
fields[field]?.association
|
|
342
358
|
);
|
|
343
359
|
|
|
344
|
-
|
|
345
|
-
|
|
360
|
+
if (Array.isArray(targetNames)) {
|
|
361
|
+
// if field refers to a belongsTo relation, use the target field instead
|
|
362
|
+
|
|
363
|
+
for (const targetName of targetNames) {
|
|
364
|
+
// check field values by value. Ignore unchanged fields
|
|
365
|
+
if (!valuesEqual(source[targetName], originalElement[targetName])) {
|
|
366
|
+
// if the field was updated to 'undefined', replace with 'null' for compatibility with JSON and GraphQL
|
|
367
|
+
|
|
368
|
+
updatedElement[targetName] =
|
|
369
|
+
originalElement[targetName] === undefined
|
|
370
|
+
? null
|
|
371
|
+
: originalElement[targetName];
|
|
372
|
+
|
|
373
|
+
for (const fieldSet of compositeKeys) {
|
|
374
|
+
// include all of the fields that comprise the composite key
|
|
375
|
+
if (fieldSet.has(targetName)) {
|
|
376
|
+
for (const compositeField of fieldSet) {
|
|
377
|
+
updatedElement[compositeField] =
|
|
378
|
+
originalElement[compositeField];
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
} else {
|
|
385
|
+
// Backwards compatibility pre-CPK
|
|
386
|
+
|
|
387
|
+
// if field refers to a belongsTo relation, use the target field instead
|
|
388
|
+
const key = targetNames || field;
|
|
389
|
+
|
|
390
|
+
// check field values by value. Ignore unchanged fields
|
|
391
|
+
if (!valuesEqual(source[key], originalElement[key])) {
|
|
392
|
+
// if the field was updated to 'undefined', replace with 'null' for compatibility with JSON and GraphQL
|
|
346
393
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
// if the field was updated to 'undefined', replace with 'null' for compatibility with JSON and GraphQL
|
|
350
|
-
updatedElement[key] =
|
|
351
|
-
originalElement[key] === undefined ? null : originalElement[key];
|
|
394
|
+
updatedElement[key] =
|
|
395
|
+
originalElement[key] === undefined ? null : originalElement[key];
|
|
352
396
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
397
|
+
for (const fieldSet of compositeKeys) {
|
|
398
|
+
// include all of the fields that comprise the composite key
|
|
399
|
+
if (fieldSet.has(key)) {
|
|
400
|
+
for (const compositeField of fieldSet) {
|
|
401
|
+
updatedElement[compositeField] =
|
|
402
|
+
originalElement[compositeField];
|
|
403
|
+
}
|
|
358
404
|
}
|
|
359
405
|
}
|
|
360
406
|
}
|
|
361
407
|
}
|
|
362
408
|
});
|
|
363
409
|
|
|
410
|
+
// Exit early when there are no changes introduced in the update mutation
|
|
411
|
+
if (Object.keys(updatedElement).length === 0) {
|
|
412
|
+
return null;
|
|
413
|
+
}
|
|
414
|
+
|
|
364
415
|
// include field(s) from custom PK if one is specified for the model
|
|
365
416
|
if (primaryKey && primaryKey.length) {
|
|
366
417
|
for (const pkField of primaryKey) {
|
|
@@ -368,10 +419,6 @@ class StorageClass implements StorageFacade {
|
|
|
368
419
|
}
|
|
369
420
|
}
|
|
370
421
|
|
|
371
|
-
if (Object.keys(updatedElement).length === 0) {
|
|
372
|
-
return null;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
422
|
const { id, _version, _lastChangedAt, _deleted } = originalElement;
|
|
376
423
|
|
|
377
424
|
// For update mutations we only want to send fields with changes
|
|
@@ -421,7 +468,7 @@ class ExclusiveStorage implements StorageFacade {
|
|
|
421
468
|
patchesTuple?: [Patch[], PersistentModel]
|
|
422
469
|
): Promise<[T, OpType.INSERT | OpType.UPDATE][]> {
|
|
423
470
|
return this.runExclusive<[T, OpType.INSERT | OpType.UPDATE][]>(storage =>
|
|
424
|
-
storage.save
|
|
471
|
+
storage.save(model, condition, mutator, patchesTuple)
|
|
425
472
|
);
|
|
426
473
|
}
|
|
427
474
|
|
|
@@ -489,7 +536,7 @@ class ExclusiveStorage implements StorageFacade {
|
|
|
489
536
|
}
|
|
490
537
|
|
|
491
538
|
batchSave<T extends PersistentModel>(
|
|
492
|
-
modelConstructor: PersistentModelConstructor<
|
|
539
|
+
modelConstructor: PersistentModelConstructor<T>,
|
|
493
540
|
items: ModelInstanceMetadata[]
|
|
494
541
|
): Promise<[T, OpType][]> {
|
|
495
542
|
return this.storage.batchSave(modelConstructor, items);
|
package/src/sync/index.ts
CHANGED
|
@@ -21,6 +21,9 @@ import {
|
|
|
21
21
|
TypeConstructorMap,
|
|
22
22
|
ModelPredicate,
|
|
23
23
|
AuthModeStrategy,
|
|
24
|
+
ManagedIdentifier,
|
|
25
|
+
OptionallyManagedIdentifier,
|
|
26
|
+
__modelMeta__,
|
|
24
27
|
AmplifyContext,
|
|
25
28
|
} from '../types';
|
|
26
29
|
import { exhaustiveCheck, getNow, SYNC, USER } from '../util';
|
|
@@ -32,6 +35,7 @@ import { CONTROL_MSG, SubscriptionProcessor } from './processors/subscription';
|
|
|
32
35
|
import { SyncProcessor } from './processors/sync';
|
|
33
36
|
import {
|
|
34
37
|
createMutationInstanceFromModelOperation,
|
|
38
|
+
getIdentifierValue,
|
|
35
39
|
predicateToGraphQLCondition,
|
|
36
40
|
TransformerMutationType,
|
|
37
41
|
} from './utils';
|
|
@@ -46,25 +50,26 @@ type StartParams = {
|
|
|
46
50
|
};
|
|
47
51
|
|
|
48
52
|
export declare class MutationEvent {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
mutator: (draft: MutableModel<MutationEvent>) => void | MutationEvent
|
|
53
|
-
): MutationEvent;
|
|
53
|
+
readonly [__modelMeta__]: {
|
|
54
|
+
identifier: OptionallyManagedIdentifier<MutationEvent, 'id'>;
|
|
55
|
+
};
|
|
54
56
|
public readonly id: string;
|
|
55
57
|
public readonly model: string;
|
|
56
58
|
public readonly operation: TransformerMutationType;
|
|
57
59
|
public readonly modelId: string;
|
|
58
60
|
public readonly condition: string;
|
|
59
|
-
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;
|
|
60
67
|
}
|
|
61
68
|
|
|
62
|
-
declare class ModelMetadata {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
mutator: (draft: MutableModel<ModelMetadata>) => void | ModelMetadata
|
|
67
|
-
): ModelMetadata;
|
|
69
|
+
export declare class ModelMetadata {
|
|
70
|
+
readonly [__modelMeta__]: {
|
|
71
|
+
identifier: ManagedIdentifier<ModelMetadata, 'id'>;
|
|
72
|
+
};
|
|
68
73
|
public readonly id: string;
|
|
69
74
|
public readonly namespace: string;
|
|
70
75
|
public readonly model: string;
|
|
@@ -72,6 +77,11 @@ declare class ModelMetadata {
|
|
|
72
77
|
public readonly lastSync?: number;
|
|
73
78
|
public readonly lastFullSync?: number;
|
|
74
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;
|
|
75
85
|
}
|
|
76
86
|
|
|
77
87
|
export enum ControlMessage {
|
|
@@ -123,7 +133,7 @@ export class SyncEngine {
|
|
|
123
133
|
) {
|
|
124
134
|
const MutationEvent = this.modelClasses[
|
|
125
135
|
'MutationEvent'
|
|
126
|
-
] as PersistentModelConstructor<
|
|
136
|
+
] as PersistentModelConstructor<MutationEvent>;
|
|
127
137
|
|
|
128
138
|
this.outbox = new MutationEventOutbox(
|
|
129
139
|
this.schema,
|
|
@@ -296,7 +306,7 @@ export class SyncEngine {
|
|
|
296
306
|
);
|
|
297
307
|
|
|
298
308
|
this.storage.runExclusive(storage =>
|
|
299
|
-
this.modelMerger.merge(storage, model)
|
|
309
|
+
this.modelMerger.merge(storage, model, modelDefinition)
|
|
300
310
|
);
|
|
301
311
|
|
|
302
312
|
observer.next({
|
|
@@ -333,7 +343,7 @@ export class SyncEngine {
|
|
|
333
343
|
);
|
|
334
344
|
|
|
335
345
|
this.storage.runExclusive(storage =>
|
|
336
|
-
this.modelMerger.merge(storage, model)
|
|
346
|
+
this.modelMerger.merge(storage, model, modelDefinition)
|
|
337
347
|
);
|
|
338
348
|
}
|
|
339
349
|
)
|
|
@@ -362,7 +372,6 @@ export class SyncEngine {
|
|
|
362
372
|
.observe(null, null, ownSymbol)
|
|
363
373
|
.filter(({ model }) => {
|
|
364
374
|
const modelDefinition = this.getModelDefinition(model);
|
|
365
|
-
|
|
366
375
|
return modelDefinition.syncable === true;
|
|
367
376
|
})
|
|
368
377
|
.subscribe({
|
|
@@ -372,7 +381,11 @@ export class SyncEngine {
|
|
|
372
381
|
const MutationEventConstructor = this.modelClasses[
|
|
373
382
|
'MutationEvent'
|
|
374
383
|
] as PersistentModelConstructor<MutationEvent>;
|
|
375
|
-
const
|
|
384
|
+
const modelDefinition = this.getModelDefinition(model);
|
|
385
|
+
const graphQLCondition = predicateToGraphQLCondition(
|
|
386
|
+
condition,
|
|
387
|
+
modelDefinition
|
|
388
|
+
);
|
|
376
389
|
const mutationEvent = createMutationInstanceFromModelOperation(
|
|
377
390
|
namespace.relationships,
|
|
378
391
|
this.getModelDefinition(model),
|
|
@@ -537,7 +550,9 @@ export class SyncEngine {
|
|
|
537
550
|
|
|
538
551
|
const oneByOne: ModelInstanceMetadata[] = [];
|
|
539
552
|
const page = items.filter(item => {
|
|
540
|
-
|
|
553
|
+
const itemId = getIdentifierValue(modelDefinition, item);
|
|
554
|
+
|
|
555
|
+
if (!idsInOutbox.has(itemId)) {
|
|
541
556
|
return true;
|
|
542
557
|
}
|
|
543
558
|
|
|
@@ -550,7 +565,8 @@ export class SyncEngine {
|
|
|
550
565
|
for (const item of oneByOne) {
|
|
551
566
|
const opType = await this.modelMerger.merge(
|
|
552
567
|
storage,
|
|
553
|
-
item
|
|
568
|
+
item,
|
|
569
|
+
modelDefinition
|
|
554
570
|
);
|
|
555
571
|
|
|
556
572
|
if (opType !== undefined) {
|
|
@@ -562,7 +578,8 @@ export class SyncEngine {
|
|
|
562
578
|
...(await this.modelMerger.mergePage(
|
|
563
579
|
storage,
|
|
564
580
|
modelConstructor,
|
|
565
|
-
page
|
|
581
|
+
page,
|
|
582
|
+
modelDefinition
|
|
566
583
|
))
|
|
567
584
|
);
|
|
568
585
|
|
|
@@ -608,7 +625,7 @@ export class SyncEngine {
|
|
|
608
625
|
|
|
609
626
|
modelMetadata = (
|
|
610
627
|
this.modelClasses
|
|
611
|
-
.ModelMetadata as PersistentModelConstructor<
|
|
628
|
+
.ModelMetadata as PersistentModelConstructor<ModelMetadata>
|
|
612
629
|
).copyOf(modelMetadata, draft => {
|
|
613
630
|
draft.lastSync = startedAt;
|
|
614
631
|
draft.lastFullSync = isFullSync
|
|
@@ -709,7 +726,7 @@ export class SyncEngine {
|
|
|
709
726
|
|
|
710
727
|
private async setupModels(params: StartParams) {
|
|
711
728
|
const { fullSyncInterval } = params;
|
|
712
|
-
const
|
|
729
|
+
const ModelMetadataConstructor = this.modelClasses
|
|
713
730
|
.ModelMetadata as PersistentModelConstructor<ModelMetadata>;
|
|
714
731
|
|
|
715
732
|
const models: [string, SchemaModel][] = [];
|
|
@@ -741,7 +758,7 @@ export class SyncEngine {
|
|
|
741
758
|
|
|
742
759
|
if (modelMetadata === undefined) {
|
|
743
760
|
[[savedModel]] = await this.storage.save(
|
|
744
|
-
this.modelInstanceCreator(
|
|
761
|
+
this.modelInstanceCreator(ModelMetadataConstructor, {
|
|
745
762
|
model: model.name,
|
|
746
763
|
namespace,
|
|
747
764
|
lastSync: null,
|
|
@@ -759,9 +776,7 @@ export class SyncEngine {
|
|
|
759
776
|
const syncPredicateUpdated = prevSyncPredicate !== lastSyncPredicate;
|
|
760
777
|
|
|
761
778
|
[[savedModel]] = await this.storage.save(
|
|
762
|
-
(
|
|
763
|
-
this.modelClasses.ModelMetadata as PersistentModelConstructor<any>
|
|
764
|
-
).copyOf(modelMetadata, draft => {
|
|
779
|
+
ModelMetadataConstructor.copyOf(modelMetadata, draft => {
|
|
765
780
|
draft.fullSyncInterval = fullSyncInterval;
|
|
766
781
|
// perform a base sync if the syncPredicate changed in between calls to DataStore.start
|
|
767
782
|
// ensures that the local store contains all the data specified by the syncExpression
|
package/src/sync/merger.ts
CHANGED
|
@@ -3,8 +3,10 @@ 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';
|
|
8
10
|
|
|
9
11
|
// https://github.com/aws-amplify/amplify-js/blob/datastore-docs/packages/datastore/docs/sync-engine.md#merger
|
|
10
12
|
class ModelMerger {
|
|
@@ -15,10 +17,15 @@ class ModelMerger {
|
|
|
15
17
|
|
|
16
18
|
public async merge<T extends ModelInstanceMetadata>(
|
|
17
19
|
storage: Storage,
|
|
18
|
-
model: T
|
|
20
|
+
model: T,
|
|
21
|
+
modelDefinition: SchemaModel
|
|
19
22
|
): Promise<OpType> {
|
|
20
23
|
let result: OpType;
|
|
21
|
-
const mutationsForModel = await this.outbox.getForModel(
|
|
24
|
+
const mutationsForModel = await this.outbox.getForModel(
|
|
25
|
+
storage,
|
|
26
|
+
model,
|
|
27
|
+
modelDefinition
|
|
28
|
+
);
|
|
22
29
|
|
|
23
30
|
const isDelete = model._deleted;
|
|
24
31
|
|
|
@@ -37,13 +44,16 @@ class ModelMerger {
|
|
|
37
44
|
public async mergePage(
|
|
38
45
|
storage: Storage,
|
|
39
46
|
modelConstructor: PersistentModelConstructor<any>,
|
|
40
|
-
items: ModelInstanceMetadata[]
|
|
47
|
+
items: ModelInstanceMetadata[],
|
|
48
|
+
modelDefinition: SchemaModel
|
|
41
49
|
): Promise<[ModelInstanceMetadata, OpType][]> {
|
|
42
50
|
const itemsMap: Map<string, ModelInstanceMetadata> = new Map();
|
|
43
51
|
|
|
44
52
|
for (const item of items) {
|
|
45
53
|
// merge items by model id. Latest record for a given id remains.
|
|
46
|
-
|
|
54
|
+
const modelId = getIdentifierValue(modelDefinition, item);
|
|
55
|
+
|
|
56
|
+
itemsMap.set(modelId, item);
|
|
47
57
|
}
|
|
48
58
|
|
|
49
59
|
const page = [...itemsMap.values()];
|
package/src/sync/outbox.ts
CHANGED
|
@@ -11,9 +11,10 @@ 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
|
|
19
20
|
// https://github.com/aws-amplify/amplify-js/blob/datastore-docs/packages/datastore/docs/sync-engine.md#outbox
|
|
@@ -35,6 +36,8 @@ class MutationEventOutbox {
|
|
|
35
36
|
const mutationEventModelDefinition =
|
|
36
37
|
this.schema.namespaces[SYNC].models['MutationEvent'];
|
|
37
38
|
|
|
39
|
+
// `id` is the key for the record in the mutationEvent;
|
|
40
|
+
// `modelId` is the key for the actual record that was mutated
|
|
38
41
|
const predicate = ModelPredicateCreator.createFromExisting<MutationEvent>(
|
|
39
42
|
mutationEventModelDefinition,
|
|
40
43
|
c =>
|
|
@@ -43,13 +46,16 @@ class MutationEventOutbox {
|
|
|
43
46
|
.id('ne', this.inProgressMutationEventId)
|
|
44
47
|
);
|
|
45
48
|
|
|
49
|
+
// Check if there are any other records with same id
|
|
46
50
|
const [first] = await s.query(this.MutationEvent, predicate);
|
|
47
51
|
|
|
52
|
+
// No other record with same modelId, so enqueue
|
|
48
53
|
if (first === undefined) {
|
|
49
54
|
await s.save(mutationEvent, undefined, this.ownSymbol);
|
|
50
55
|
return;
|
|
51
56
|
}
|
|
52
57
|
|
|
58
|
+
// There was an enqueued mutation for the modelId, so continue
|
|
53
59
|
const { operation: incomingMutationType } = mutationEvent;
|
|
54
60
|
|
|
55
61
|
if (first.operation === TransformerMutationType.CREATE) {
|
|
@@ -122,16 +128,19 @@ class MutationEventOutbox {
|
|
|
122
128
|
|
|
123
129
|
public async getForModel<T extends PersistentModel>(
|
|
124
130
|
storage: StorageFacade,
|
|
125
|
-
model: T
|
|
131
|
+
model: T,
|
|
132
|
+
userModelDefinition: SchemaModel
|
|
126
133
|
): Promise<MutationEvent[]> {
|
|
127
134
|
const mutationEventModelDefinition =
|
|
128
135
|
this.schema.namespaces[SYNC].models.MutationEvent;
|
|
129
136
|
|
|
137
|
+
const modelId = getIdentifierValue(userModelDefinition, model);
|
|
138
|
+
|
|
130
139
|
const mutationEvents = await storage.query(
|
|
131
140
|
this.MutationEvent,
|
|
132
141
|
ModelPredicateCreator.createFromExisting(
|
|
133
142
|
mutationEventModelDefinition,
|
|
134
|
-
c => c.modelId('eq',
|
|
143
|
+
c => c.modelId('eq', modelId)
|
|
135
144
|
)
|
|
136
145
|
);
|
|
137
146
|
|
|
@@ -187,9 +196,14 @@ class MutationEventOutbox {
|
|
|
187
196
|
const mutationEventModelDefinition =
|
|
188
197
|
this.schema.namespaces[SYNC].models['MutationEvent'];
|
|
189
198
|
|
|
199
|
+
const userModelDefinition =
|
|
200
|
+
this.schema.namespaces['user'].models[head.model];
|
|
201
|
+
|
|
202
|
+
const recordId = getIdentifierValue(userModelDefinition, record);
|
|
203
|
+
|
|
190
204
|
const predicate = ModelPredicateCreator.createFromExisting<MutationEvent>(
|
|
191
205
|
mutationEventModelDefinition,
|
|
192
|
-
c => c.modelId('eq',
|
|
206
|
+
c => c.modelId('eq', recordId).id('ne', this.inProgressMutationEventId)
|
|
193
207
|
);
|
|
194
208
|
|
|
195
209
|
const outdatedMutations = await storage.query(
|
|
@@ -224,11 +238,11 @@ class MutationEventOutbox {
|
|
|
224
238
|
previous: MutationEvent,
|
|
225
239
|
current: MutationEvent
|
|
226
240
|
): MutationEvent {
|
|
227
|
-
const { _version,
|
|
228
|
-
|
|
241
|
+
const { _version, _lastChangedAt, _deleted, ...previousData } = JSON.parse(
|
|
242
|
+
previous.data
|
|
243
|
+
);
|
|
229
244
|
|
|
230
245
|
const {
|
|
231
|
-
id: __id,
|
|
232
246
|
_version: __version,
|
|
233
247
|
_lastChangedAt: __lastChangedAt,
|
|
234
248
|
_deleted: __deleted,
|
|
@@ -236,7 +250,6 @@ class MutationEventOutbox {
|
|
|
236
250
|
} = JSON.parse(current.data);
|
|
237
251
|
|
|
238
252
|
const data = JSON.stringify({
|
|
239
|
-
id,
|
|
240
253
|
_version,
|
|
241
254
|
_lastChangedAt,
|
|
242
255
|
_deleted,
|
|
@@ -27,7 +27,13 @@ import {
|
|
|
27
27
|
ProcessName,
|
|
28
28
|
AmplifyContext,
|
|
29
29
|
} from '../../types';
|
|
30
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
exhaustiveCheck,
|
|
32
|
+
extractTargetNamesFromSrc,
|
|
33
|
+
USER,
|
|
34
|
+
USER_AGENT_SUFFIX_DATASTORE,
|
|
35
|
+
ID,
|
|
36
|
+
} from '../../util';
|
|
31
37
|
import { MutationEventOutbox } from '../outbox';
|
|
32
38
|
import {
|
|
33
39
|
buildGraphQLOperation,
|
|
@@ -452,63 +458,61 @@ class MutationProcessor {
|
|
|
452
458
|
|
|
453
459
|
// include all the fields that comprise a custom PK if one is specified
|
|
454
460
|
const deleteInput = {};
|
|
455
|
-
if (primaryKey
|
|
461
|
+
if (primaryKey?.length) {
|
|
456
462
|
for (const pkField of primaryKey) {
|
|
457
463
|
deleteInput[pkField] = parsedData[pkField];
|
|
458
464
|
}
|
|
459
465
|
} else {
|
|
460
|
-
deleteInput[
|
|
466
|
+
deleteInput[ID] = (<any>parsedData).id;
|
|
461
467
|
}
|
|
462
468
|
|
|
463
|
-
|
|
464
|
-
operation === TransformerMutationType.DELETE
|
|
465
|
-
? <ModelInstanceMetadata>deleteInput // For DELETE mutations, only PK is sent
|
|
466
|
-
: Object.values(modelDefinition.fields)
|
|
467
|
-
.filter(({ name, type, association }) => {
|
|
468
|
-
// connections
|
|
469
|
-
if (isModelFieldType(type)) {
|
|
470
|
-
// BELONGS_TO
|
|
471
|
-
if (
|
|
472
|
-
isTargetNameAssociation(association) &&
|
|
473
|
-
association.connectionType === 'BELONGS_TO'
|
|
474
|
-
) {
|
|
475
|
-
return true;
|
|
476
|
-
}
|
|
469
|
+
let mutationInput;
|
|
477
470
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
471
|
+
if (operation === TransformerMutationType.DELETE) {
|
|
472
|
+
// For DELETE mutations, only the key(s) are included in the input
|
|
473
|
+
mutationInput = <ModelInstanceMetadata>deleteInput;
|
|
474
|
+
} else {
|
|
475
|
+
// Otherwise, we construct the mutation input with the following logic
|
|
476
|
+
mutationInput = {};
|
|
477
|
+
const modelFields = Object.values(modelDefinition.fields);
|
|
478
|
+
|
|
479
|
+
for (const { name, type, association } of modelFields) {
|
|
480
|
+
// model fields should be stripped out from the input
|
|
481
|
+
if (isModelFieldType(type)) {
|
|
482
|
+
// except for belongs to relations - we need to replace them with the correct foreign key(s)
|
|
483
|
+
if (
|
|
484
|
+
isTargetNameAssociation(association) &&
|
|
485
|
+
association.connectionType === 'BELONGS_TO'
|
|
486
|
+
) {
|
|
487
|
+
const targetNames: string[] | undefined =
|
|
488
|
+
extractTargetNamesFromSrc(association);
|
|
489
|
+
|
|
490
|
+
if (targetNames) {
|
|
491
|
+
// instead of including the connected model itself, we add its key(s) to the mutation input
|
|
492
|
+
for (const targetName of targetNames) {
|
|
493
|
+
mutationInput[targetName] = parsedData[targetName];
|
|
485
494
|
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
continue;
|
|
498
|
+
}
|
|
499
|
+
// scalar fields / non-model types
|
|
486
500
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
if (
|
|
495
|
-
isModelFieldType(type) &&
|
|
496
|
-
isTargetNameAssociation(association)
|
|
497
|
-
) {
|
|
498
|
-
fieldName = association.targetName;
|
|
499
|
-
val = parsedData[fieldName];
|
|
500
|
-
}
|
|
501
|
+
if (operation === TransformerMutationType.UPDATE) {
|
|
502
|
+
if (!parsedData.hasOwnProperty(name)) {
|
|
503
|
+
// for update mutations - strip out a field if it's unchanged
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
501
507
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
return acc;
|
|
507
|
-
}, <typeof parsedData>{});
|
|
508
|
+
// all other fields are added to the input object
|
|
509
|
+
mutationInput[name] = parsedData[name];
|
|
510
|
+
}
|
|
511
|
+
}
|
|
508
512
|
|
|
509
513
|
// Build mutation variables input object
|
|
510
514
|
const input: ModelInstanceMetadata = {
|
|
511
|
-
...
|
|
515
|
+
...mutationInput,
|
|
512
516
|
_version,
|
|
513
517
|
};
|
|
514
518
|
|
|
@@ -393,7 +393,6 @@ class SubscriptionProcessor {
|
|
|
393
393
|
Observable<{
|
|
394
394
|
value: GraphQLResult<Record<string, PersistentModel>>;
|
|
395
395
|
}>
|
|
396
|
-
|
|
397
396
|
>(<unknown>this.amplifyContext.API.graphql({ query, variables, ...{ authMode }, authToken, userAgentSuffix }));
|
|
398
397
|
|
|
399
398
|
let subscriptionReadyCallback: () => void;
|
|
@@ -85,9 +85,7 @@ class SyncProcessor {
|
|
|
85
85
|
return predicateToGraphQLFilter(predicatesGroup);
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
private async retrievePage<
|
|
89
|
-
T extends ModelInstanceMetadata = ModelInstanceMetadata
|
|
90
|
-
>(
|
|
88
|
+
private async retrievePage<T extends ModelInstanceMetadata>(
|
|
91
89
|
modelDefinition: SchemaModel,
|
|
92
90
|
lastSync: number,
|
|
93
91
|
nextToken: string,
|