@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.
- package/CHANGELOG.md +144 -17
- package/README.md +154 -0
- package/dist/aws-amplify-datastore.js +3740 -1599
- package/dist/aws-amplify-datastore.js.map +1 -1
- package/dist/aws-amplify-datastore.min.js +25 -95
- package/dist/aws-amplify-datastore.min.js.map +1 -1
- package/lib/authModeStrategies/multiAuthStrategy.d.ts +2 -2
- package/lib/authModeStrategies/multiAuthStrategy.js +32 -26
- package/lib/authModeStrategies/multiAuthStrategy.js.map +1 -1
- package/lib/datastore/datastore.d.ts +17 -16
- package/lib/datastore/datastore.js +291 -83
- 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 +6 -2
- package/lib/storage/adapter/AsyncStorageAdapter.js +361 -193
- package/lib/storage/adapter/AsyncStorageAdapter.js.map +1 -1
- package/lib/storage/adapter/AsyncStorageDatabase.d.ts +4 -4
- package/lib/storage/adapter/AsyncStorageDatabase.js +59 -28
- package/lib/storage/adapter/AsyncStorageDatabase.js.map +1 -1
- package/lib/storage/adapter/InMemoryStore.js +2 -2
- package/lib/storage/adapter/InMemoryStore.js.map +1 -1
- package/lib/storage/adapter/IndexedDBAdapter.d.ts +7 -2
- package/lib/storage/adapter/IndexedDBAdapter.js +454 -252
- package/lib/storage/adapter/IndexedDBAdapter.js.map +1 -1
- package/lib/storage/adapter/index.d.ts +1 -1
- package/lib/storage/storage.d.ts +3 -3
- package/lib/storage/storage.js +106 -28
- package/lib/storage/storage.js.map +1 -1
- package/lib/sync/index.d.ts +23 -7
- package/lib/sync/index.js +18 -17
- package/lib/sync/index.js.map +1 -1
- package/lib/sync/merger.d.ts +3 -3
- package/lib/sync/merger.js +8 -6
- package/lib/sync/merger.js.map +1 -1
- package/lib/sync/outbox.d.ts +2 -2
- package/lib/sync/outbox.js +12 -9
- package/lib/sync/outbox.js.map +1 -1
- package/lib/sync/processors/errorMaps.d.ts +17 -0
- package/lib/sync/processors/errorMaps.js +125 -0
- package/lib/sync/processors/errorMaps.js.map +1 -0
- package/lib/sync/processors/mutation.d.ts +22 -4
- package/lib/sync/processors/mutation.js +119 -51
- package/lib/sync/processors/mutation.js.map +1 -1
- package/lib/sync/processors/subscription.d.ts +4 -2
- package/lib/sync/processors/subscription.js +76 -41
- package/lib/sync/processors/subscription.js.map +1 -1
- package/lib/sync/processors/sync.d.ts +4 -2
- package/lib/sync/processors/sync.js +62 -27
- package/lib/sync/processors/sync.js.map +1 -1
- package/lib/sync/utils.d.ts +3 -2
- package/lib/sync/utils.js +58 -7
- package/lib/sync/utils.js.map +1 -1
- package/lib/types.d.ts +90 -34
- package/lib/types.js +31 -2
- package/lib/types.js.map +1 -1
- package/lib/util.d.ts +48 -33
- package/lib/util.js +219 -57
- package/lib/util.js.map +1 -1
- package/lib-esm/authModeStrategies/multiAuthStrategy.d.ts +2 -2
- package/lib-esm/authModeStrategies/multiAuthStrategy.js +32 -26
- package/lib-esm/authModeStrategies/multiAuthStrategy.js.map +1 -1
- package/lib-esm/datastore/datastore.d.ts +17 -16
- package/lib-esm/datastore/datastore.js +293 -85
- 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 +6 -2
- package/lib-esm/storage/adapter/AsyncStorageAdapter.js +361 -193
- package/lib-esm/storage/adapter/AsyncStorageAdapter.js.map +1 -1
- package/lib-esm/storage/adapter/AsyncStorageDatabase.d.ts +4 -4
- package/lib-esm/storage/adapter/AsyncStorageDatabase.js +60 -29
- package/lib-esm/storage/adapter/AsyncStorageDatabase.js.map +1 -1
- package/lib-esm/storage/adapter/InMemoryStore.js +2 -2
- package/lib-esm/storage/adapter/InMemoryStore.js.map +1 -1
- package/lib-esm/storage/adapter/IndexedDBAdapter.d.ts +7 -2
- package/lib-esm/storage/adapter/IndexedDBAdapter.js +454 -252
- 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 +3 -3
- package/lib-esm/storage/storage.js +106 -28
- package/lib-esm/storage/storage.js.map +1 -1
- package/lib-esm/sync/index.d.ts +23 -7
- package/lib-esm/sync/index.js +20 -19
- package/lib-esm/sync/index.js.map +1 -1
- package/lib-esm/sync/merger.d.ts +3 -3
- package/lib-esm/sync/merger.js +8 -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 +13 -10
- package/lib-esm/sync/outbox.js.map +1 -1
- package/lib-esm/sync/processors/errorMaps.d.ts +17 -0
- package/lib-esm/sync/processors/errorMaps.js +119 -0
- package/lib-esm/sync/processors/errorMaps.js.map +1 -0
- package/lib-esm/sync/processors/mutation.d.ts +22 -4
- package/lib-esm/sync/processors/mutation.js +122 -54
- package/lib-esm/sync/processors/mutation.js.map +1 -1
- package/lib-esm/sync/processors/subscription.d.ts +4 -2
- package/lib-esm/sync/processors/subscription.js +77 -42
- package/lib-esm/sync/processors/subscription.js.map +1 -1
- package/lib-esm/sync/processors/sync.d.ts +4 -2
- package/lib-esm/sync/processors/sync.js +62 -27
- 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 +58 -8
- package/lib-esm/sync/utils.js.map +1 -1
- package/lib-esm/types.d.ts +90 -34
- package/lib-esm/types.js +29 -3
- package/lib-esm/types.js.map +1 -1
- package/lib-esm/util.d.ts +48 -33
- package/lib-esm/util.js +207 -54
- package/lib-esm/util.js.map +1 -1
- package/package.json +10 -9
- package/src/authModeStrategies/multiAuthStrategy.ts +24 -21
- package/src/datastore/datastore.ts +448 -148
- package/src/predicates/index.ts +32 -10
- package/src/storage/adapter/AsyncStorageAdapter.ts +305 -78
- package/src/storage/adapter/AsyncStorageDatabase.ts +65 -26
- package/src/storage/adapter/InMemoryStore.ts +2 -2
- package/src/storage/adapter/IndexedDBAdapter.ts +344 -101
- package/src/storage/adapter/index.ts +1 -1
- package/src/storage/storage.ts +85 -24
- package/src/sync/index.ts +74 -58
- package/src/sync/merger.ts +16 -4
- package/src/sync/outbox.ts +22 -8
- package/src/sync/processors/errorMaps.ts +93 -0
- package/src/sync/processors/mutation.ts +143 -84
- package/src/sync/processors/subscription.ts +67 -36
- package/src/sync/processors/sync.ts +37 -12
- package/src/sync/utils.ts +67 -16
- package/src/types.ts +252 -50
- package/src/util.ts +256 -42
- 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<
|
|
32
|
+
modelConstructor: PersistentModelConstructor<T>,
|
|
33
33
|
items: ModelInstanceMetadata[]
|
|
34
34
|
): Promise<[T, OpType][]>;
|
|
35
35
|
}
|
package/src/storage/storage.ts
CHANGED
|
@@ -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
|
-
|
|
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 [
|
|
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
|
-
|
|
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 ||
|
|
150
|
+
const element = updateMutationInput || savedElement;
|
|
136
151
|
|
|
137
|
-
const modelConstructor = (
|
|
138
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
357
|
+
// CPK TODO: rename:
|
|
358
|
+
const targetNames: any = isTargetNameAssociation(
|
|
328
359
|
fields[field]?.association
|
|
329
360
|
);
|
|
330
361
|
|
|
331
|
-
|
|
332
|
-
|
|
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
|
-
|
|
335
|
-
|
|
336
|
-
|
|
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
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
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
|
|
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<
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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<
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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 = (
|
|
607
|
-
.
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
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
|
|
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(
|
|
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
|
-
(
|
|
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 =
|
|
823
|
-
modelConstructor.name
|
|
824
|
-
];
|
|
839
|
+
const modelDefinition =
|
|
840
|
+
this.schema.namespaces[namespaceName].models[modelConstructor.name];
|
|
825
841
|
|
|
826
842
|
return modelDefinition;
|
|
827
843
|
}
|
package/src/sync/merger.ts
CHANGED
|
@@ -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(
|
|
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
|
-
|
|
54
|
+
const modelId = getIdentifierValue(modelDefinition, item);
|
|
55
|
+
|
|
56
|
+
itemsMap.set(modelId, item);
|
|
45
57
|
}
|
|
46
58
|
|
|
47
59
|
const page = [...itemsMap.values()];
|
package/src/sync/outbox.ts
CHANGED
|
@@ -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',
|
|
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',
|
|
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,
|
|
227
|
-
|
|
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,
|