@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
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import API from '@aws-amplify/api';
|
|
1
2
|
import { Amplify, ConsoleLogger as Logger, Hub, JS } from '@aws-amplify/core';
|
|
3
|
+
import { Auth } from '@aws-amplify/auth';
|
|
4
|
+
import Cache from '@aws-amplify/cache';
|
|
2
5
|
import {
|
|
3
6
|
Draft,
|
|
4
7
|
immerable,
|
|
@@ -54,9 +57,17 @@ import {
|
|
|
54
57
|
isNonModelFieldType,
|
|
55
58
|
isModelFieldType,
|
|
56
59
|
ObserveQueryOptions,
|
|
60
|
+
ManagedIdentifier,
|
|
61
|
+
PersistentModelMetaData,
|
|
62
|
+
IdentifierFieldOrIdentifierObject,
|
|
63
|
+
__modelMeta__,
|
|
64
|
+
isIdentifierObject,
|
|
65
|
+
AmplifyContext,
|
|
66
|
+
isModelAttributePrimaryKey,
|
|
57
67
|
} from '../types';
|
|
58
68
|
import {
|
|
59
69
|
DATASTORE,
|
|
70
|
+
errorMessages,
|
|
60
71
|
establishRelationAndKeys,
|
|
61
72
|
exhaustiveCheck,
|
|
62
73
|
isModelConstructor,
|
|
@@ -69,7 +80,14 @@ import {
|
|
|
69
80
|
registerNonModelClass,
|
|
70
81
|
sortCompareFunction,
|
|
71
82
|
DeferredCallbackResolver,
|
|
83
|
+
extractPrimaryKeyFieldNames,
|
|
84
|
+
extractPrimaryKeysAndValues,
|
|
85
|
+
isIdManaged,
|
|
86
|
+
isIdOptionallyManaged,
|
|
87
|
+
validatePredicate,
|
|
88
|
+
mergePatches,
|
|
72
89
|
} from '../util';
|
|
90
|
+
import { getIdentifierValue } from '../sync/utils';
|
|
73
91
|
|
|
74
92
|
setAutoFreeze(true);
|
|
75
93
|
enablePatches();
|
|
@@ -79,11 +97,16 @@ const logger = new Logger('DataStore');
|
|
|
79
97
|
const ulid = monotonicUlidFactory(Date.now());
|
|
80
98
|
const { isNode } = JS.browserOrNode();
|
|
81
99
|
|
|
100
|
+
type SettingMetaData = {
|
|
101
|
+
identifier: ManagedIdentifier<Setting, 'id'>;
|
|
102
|
+
readOnlyFields: never;
|
|
103
|
+
};
|
|
82
104
|
declare class Setting {
|
|
83
|
-
|
|
105
|
+
public readonly [__modelMeta__]: SettingMetaData;
|
|
106
|
+
constructor(init: ModelInit<Setting, SettingMetaData>);
|
|
84
107
|
static copyOf(
|
|
85
108
|
src: Setting,
|
|
86
|
-
mutator: (draft: MutableModel<Setting>) => void | Setting
|
|
109
|
+
mutator: (draft: MutableModel<Setting, SettingMetaData>) => void | Setting
|
|
87
110
|
): Setting;
|
|
88
111
|
public readonly id: string;
|
|
89
112
|
public readonly key: string;
|
|
@@ -222,6 +245,20 @@ const initSchema = (userSchema: Schema) => {
|
|
|
222
245
|
return userClasses;
|
|
223
246
|
};
|
|
224
247
|
|
|
248
|
+
/* Checks if the schema has been initialized by initSchema().
|
|
249
|
+
*
|
|
250
|
+
* Call this function before accessing schema.
|
|
251
|
+
* Currently this only needs to be called in start() and clear() because all other functions will call start first.
|
|
252
|
+
*/
|
|
253
|
+
const checkSchemaInitialized = () => {
|
|
254
|
+
if (schema === undefined) {
|
|
255
|
+
const message =
|
|
256
|
+
'Schema is not initialized. DataStore will not function as expected. This could happen if you have multiple versions of DataStore installed. Please see https://docs.amplify.aws/lib/troubleshooting/upgrading/q/platform/js/#check-for-duplicate-versions';
|
|
257
|
+
logger.error(message);
|
|
258
|
+
throw new Error(message);
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
|
|
225
262
|
const createTypeClasses: (
|
|
226
263
|
namespace: SchemaNamespace
|
|
227
264
|
) => TypeConstructorMap = namespace => {
|
|
@@ -246,16 +283,19 @@ const createTypeClasses: (
|
|
|
246
283
|
|
|
247
284
|
export declare type ModelInstanceCreator = typeof modelInstanceCreator;
|
|
248
285
|
|
|
249
|
-
const instancesMetadata = new WeakSet<
|
|
250
|
-
|
|
251
|
-
>(
|
|
252
|
-
function modelInstanceCreator<T extends PersistentModel = PersistentModel>(
|
|
286
|
+
const instancesMetadata = new WeakSet<ModelInit<unknown, unknown>>();
|
|
287
|
+
|
|
288
|
+
function modelInstanceCreator<T extends PersistentModel>(
|
|
253
289
|
modelConstructor: PersistentModelConstructor<T>,
|
|
254
|
-
init:
|
|
290
|
+
init: Partial<T>
|
|
255
291
|
): T {
|
|
256
292
|
instancesMetadata.add(init);
|
|
257
293
|
|
|
258
|
-
return
|
|
294
|
+
return new modelConstructor(<ModelInit<T, PersistentModelMetaData<T>>>init);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function isSchemaModel(m: SchemaModel | SchemaNonModel): m is SchemaModel {
|
|
298
|
+
return (m as SchemaModel).attributes !== undefined;
|
|
259
299
|
}
|
|
260
300
|
|
|
261
301
|
const validateModelFields =
|
|
@@ -273,6 +313,14 @@ const validateModelFields =
|
|
|
273
313
|
throw new Error(`Field ${name} is required`);
|
|
274
314
|
}
|
|
275
315
|
|
|
316
|
+
if (isSchemaModel(modelDefinition) && !isIdManaged(modelDefinition)) {
|
|
317
|
+
const keys = extractPrimaryKeyFieldNames(modelDefinition);
|
|
318
|
+
if (keys.includes(k) && v === '') {
|
|
319
|
+
logger.error(errorMessages.idEmptyString, { k, value: v });
|
|
320
|
+
throw new Error(errorMessages.idEmptyString);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
276
324
|
if (isGraphQLScalarType(type)) {
|
|
277
325
|
const jsType = GraphQLScalarType.getJSType(type);
|
|
278
326
|
const validateScalar = GraphQLScalarType.getValidationFunction(type);
|
|
@@ -383,7 +431,7 @@ const castInstanceType = (
|
|
|
383
431
|
return v;
|
|
384
432
|
};
|
|
385
433
|
|
|
386
|
-
const initializeInstance = <T>(
|
|
434
|
+
const initializeInstance = <T extends PersistentModel>(
|
|
387
435
|
init: ModelInit<T>,
|
|
388
436
|
modelDefinition: SchemaModel | SchemaNonModel,
|
|
389
437
|
draft: Draft<T & ModelInstanceMetadata>
|
|
@@ -407,31 +455,39 @@ const createModelClass = <T extends PersistentModel>(
|
|
|
407
455
|
(draft: Draft<T & ModelInstanceMetadata>) => {
|
|
408
456
|
initializeInstance(init, modelDefinition, draft);
|
|
409
457
|
|
|
458
|
+
// model is initialized inside a DataStore component (e.g. by Sync Engine, Storage Engine, etc.)
|
|
459
|
+
const isInternallyInitialized = instancesMetadata.has(init);
|
|
460
|
+
|
|
410
461
|
const modelInstanceMetadata: ModelInstanceMetadata =
|
|
411
|
-
|
|
462
|
+
isInternallyInitialized
|
|
412
463
|
? <ModelInstanceMetadata>(<unknown>init)
|
|
413
464
|
: <ModelInstanceMetadata>{};
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
if (
|
|
465
|
+
|
|
466
|
+
type ModelWithIDIdentifier = { id: string };
|
|
467
|
+
|
|
468
|
+
const { id: _id } =
|
|
469
|
+
modelInstanceMetadata as unknown as ModelWithIDIdentifier;
|
|
470
|
+
|
|
471
|
+
if (isIdManaged(modelDefinition)) {
|
|
472
|
+
const isInternalModel = _id !== null && _id !== undefined;
|
|
473
|
+
|
|
474
|
+
const id = isInternalModel
|
|
475
|
+
? _id
|
|
476
|
+
: modelDefinition.syncable
|
|
477
|
+
? uuid4()
|
|
478
|
+
: ulid();
|
|
479
|
+
|
|
480
|
+
(<ModelWithIDIdentifier>(<unknown>draft)).id = id;
|
|
481
|
+
} else if (isIdOptionallyManaged(modelDefinition)) {
|
|
482
|
+
// only auto-populate if the id was not provided
|
|
483
|
+
(<ModelWithIDIdentifier>(<unknown>draft)).id = draft.id || uuid4();
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
if (!isInternallyInitialized) {
|
|
431
487
|
checkReadOnlyPropertyOnCreate(draft, modelDefinition);
|
|
432
488
|
}
|
|
433
489
|
|
|
434
|
-
|
|
490
|
+
const { _version, _lastChangedAt, _deleted } = modelInstanceMetadata;
|
|
435
491
|
|
|
436
492
|
if (modelDefinition.syncable) {
|
|
437
493
|
draft._version = _version;
|
|
@@ -456,8 +512,13 @@ const createModelClass = <T extends PersistentModel>(
|
|
|
456
512
|
const model = produce(
|
|
457
513
|
source,
|
|
458
514
|
draft => {
|
|
459
|
-
fn(<MutableModel<T>>
|
|
460
|
-
|
|
515
|
+
fn(<MutableModel<T>>draft);
|
|
516
|
+
|
|
517
|
+
const keyNames = extractPrimaryKeyFieldNames(modelDefinition);
|
|
518
|
+
// Keys are immutable
|
|
519
|
+
// @ts-ignore TODO: fix type
|
|
520
|
+
keyNames.forEach(key => (draft[key] = source[key]));
|
|
521
|
+
|
|
461
522
|
const modelValidator = validateModelFields(modelDefinition);
|
|
462
523
|
Object.entries(draft).forEach(([k, v]) => {
|
|
463
524
|
const parsedValue = castInstanceType(modelDefinition, k, v);
|
|
@@ -468,9 +529,21 @@ const createModelClass = <T extends PersistentModel>(
|
|
|
468
529
|
p => (patches = p)
|
|
469
530
|
);
|
|
470
531
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
532
|
+
const hasExistingPatches = modelPatchesMap.has(source);
|
|
533
|
+
if (patches.length || hasExistingPatches) {
|
|
534
|
+
if (hasExistingPatches) {
|
|
535
|
+
const [existingPatches, existingSource] = modelPatchesMap.get(source);
|
|
536
|
+
const mergedPatches = mergePatches(
|
|
537
|
+
existingSource,
|
|
538
|
+
existingPatches,
|
|
539
|
+
patches
|
|
540
|
+
);
|
|
541
|
+
modelPatchesMap.set(model, [mergedPatches, existingSource]);
|
|
542
|
+
checkReadOnlyPropertyOnUpdate(mergedPatches, modelDefinition);
|
|
543
|
+
} else {
|
|
544
|
+
modelPatchesMap.set(model, [patches, source]);
|
|
545
|
+
checkReadOnlyPropertyOnUpdate(patches, modelDefinition);
|
|
546
|
+
}
|
|
474
547
|
}
|
|
475
548
|
|
|
476
549
|
return model;
|
|
@@ -484,6 +557,7 @@ const createModelClass = <T extends PersistentModel>(
|
|
|
484
557
|
}
|
|
485
558
|
|
|
486
559
|
const instance = modelInstanceCreator(clazz, json);
|
|
560
|
+
|
|
487
561
|
const modelValidator = validateModelFields(modelDefinition);
|
|
488
562
|
|
|
489
563
|
Object.entries(instance).forEach(([k, v]) => {
|
|
@@ -531,7 +605,9 @@ const checkReadOnlyPropertyOnUpdate = (
|
|
|
531
605
|
});
|
|
532
606
|
};
|
|
533
607
|
|
|
534
|
-
const createNonModelClass = <T>(
|
|
608
|
+
const createNonModelClass = <T extends PersistentModel>(
|
|
609
|
+
typeDefinition: SchemaNonModel
|
|
610
|
+
) => {
|
|
535
611
|
const clazz = <NonModelTypeConstructor<T>>(<unknown>class Model {
|
|
536
612
|
constructor(init: ModelInit<T>) {
|
|
537
613
|
const instance = produce(
|
|
@@ -564,7 +640,7 @@ function defaultConflictHandler(conflictData: SyncConflict): PersistentModel {
|
|
|
564
640
|
return modelInstanceCreator(modelConstructor, { ...localModel, _version });
|
|
565
641
|
}
|
|
566
642
|
|
|
567
|
-
function defaultErrorHandler(error: SyncError) {
|
|
643
|
+
function defaultErrorHandler(error: SyncError<PersistentModel>): void {
|
|
568
644
|
logger.warn(error);
|
|
569
645
|
}
|
|
570
646
|
|
|
@@ -615,7 +691,6 @@ async function checkSchemaVersion(
|
|
|
615
691
|
const [schemaVersionSetting] = await s.query(
|
|
616
692
|
Setting,
|
|
617
693
|
ModelPredicateCreator.createFromExisting(modelDefinition, c =>
|
|
618
|
-
// @ts-ignore Argument of type '"eq"' is not assignable to parameter of type 'never'.
|
|
619
694
|
c.key('eq', SETTING_SCHEMA_VERSION)
|
|
620
695
|
),
|
|
621
696
|
{ page: 0, limit: 1 }
|
|
@@ -682,23 +757,34 @@ function getNamespace(): SchemaNamespace {
|
|
|
682
757
|
}
|
|
683
758
|
|
|
684
759
|
class DataStore {
|
|
760
|
+
// reference to configured category instances. Used for preserving SSR context
|
|
761
|
+
Auth = Auth;
|
|
762
|
+
API = API;
|
|
763
|
+
Cache = Cache;
|
|
764
|
+
|
|
685
765
|
private amplifyConfig: Record<string, any> = {};
|
|
686
766
|
private authModeStrategy: AuthModeStrategy;
|
|
687
767
|
private conflictHandler: ConflictHandler;
|
|
688
|
-
private errorHandler: (error: SyncError) => void;
|
|
768
|
+
private errorHandler: (error: SyncError<PersistentModel>) => void;
|
|
689
769
|
private fullSyncInterval: number;
|
|
690
|
-
private initialized
|
|
770
|
+
private initialized?: Promise<void>;
|
|
691
771
|
private initReject: Function;
|
|
692
772
|
private initResolve: Function;
|
|
693
773
|
private maxRecordsToSync: number;
|
|
694
|
-
private storage
|
|
695
|
-
private sync
|
|
774
|
+
private storage?: Storage;
|
|
775
|
+
private sync?: SyncEngine;
|
|
696
776
|
private syncPageSize: number;
|
|
697
777
|
private syncExpressions: SyncExpression[];
|
|
698
778
|
private syncPredicates: WeakMap<SchemaModel, ModelPredicate<any>> =
|
|
699
779
|
new WeakMap<SchemaModel, ModelPredicate<any>>();
|
|
700
780
|
private sessionId: string;
|
|
701
781
|
private storageAdapter: Adapter;
|
|
782
|
+
// object that gets passed to descendent classes. Allows us to pass these down by reference
|
|
783
|
+
private amplifyContext: AmplifyContext = {
|
|
784
|
+
Auth: this.Auth,
|
|
785
|
+
API: this.API,
|
|
786
|
+
Cache: this.Cache,
|
|
787
|
+
};
|
|
702
788
|
|
|
703
789
|
getModuleName() {
|
|
704
790
|
return 'DataStore';
|
|
@@ -728,6 +814,7 @@ class DataStore {
|
|
|
728
814
|
|
|
729
815
|
await this.storage.init();
|
|
730
816
|
|
|
817
|
+
checkSchemaInitialized();
|
|
731
818
|
await checkSchemaVersion(this.storage, schema.version);
|
|
732
819
|
|
|
733
820
|
const { aws_appsync_graphqlEndpoint } = this.amplifyConfig;
|
|
@@ -744,13 +831,12 @@ class DataStore {
|
|
|
744
831
|
userClasses,
|
|
745
832
|
this.storage,
|
|
746
833
|
modelInstanceCreator,
|
|
747
|
-
this.maxRecordsToSync,
|
|
748
|
-
this.syncPageSize,
|
|
749
834
|
this.conflictHandler,
|
|
750
835
|
this.errorHandler,
|
|
751
836
|
this.syncPredicates,
|
|
752
837
|
this.amplifyConfig,
|
|
753
|
-
this.authModeStrategy
|
|
838
|
+
this.authModeStrategy,
|
|
839
|
+
this.amplifyContext
|
|
754
840
|
);
|
|
755
841
|
|
|
756
842
|
// tslint:disable-next-line:max-line-length
|
|
@@ -796,7 +882,10 @@ class DataStore {
|
|
|
796
882
|
query: {
|
|
797
883
|
<T extends PersistentModel>(
|
|
798
884
|
modelConstructor: PersistentModelConstructor<T>,
|
|
799
|
-
|
|
885
|
+
identifier: IdentifierFieldOrIdentifierObject<
|
|
886
|
+
T,
|
|
887
|
+
PersistentModelMetaData<T>
|
|
888
|
+
>
|
|
800
889
|
): Promise<T | undefined>;
|
|
801
890
|
<T extends PersistentModel>(
|
|
802
891
|
modelConstructor: PersistentModelConstructor<T>,
|
|
@@ -805,7 +894,10 @@ class DataStore {
|
|
|
805
894
|
): Promise<T[]>;
|
|
806
895
|
} = async <T extends PersistentModel>(
|
|
807
896
|
modelConstructor: PersistentModelConstructor<T>,
|
|
808
|
-
|
|
897
|
+
identifierOrCriteria?:
|
|
898
|
+
| IdentifierFieldOrIdentifierObject<T, PersistentModelMetaData<T>>
|
|
899
|
+
| ProducerModelPredicate<T>
|
|
900
|
+
| typeof PredicateAll,
|
|
809
901
|
paginationProducer?: ProducerPaginationInput<T>
|
|
810
902
|
): Promise<T | T[] | undefined> => {
|
|
811
903
|
await this.start();
|
|
@@ -819,28 +911,37 @@ class DataStore {
|
|
|
819
911
|
throw new Error(msg);
|
|
820
912
|
}
|
|
821
913
|
|
|
822
|
-
if (typeof
|
|
914
|
+
if (typeof identifierOrCriteria === 'string') {
|
|
823
915
|
if (paginationProducer !== undefined) {
|
|
824
916
|
logger.warn('Pagination is ignored when querying by id');
|
|
825
917
|
}
|
|
826
918
|
}
|
|
827
919
|
|
|
828
920
|
const modelDefinition = getModelDefinition(modelConstructor);
|
|
921
|
+
const keyFields = extractPrimaryKeyFieldNames(modelDefinition);
|
|
922
|
+
|
|
829
923
|
let predicate: ModelPredicate<T>;
|
|
830
924
|
|
|
831
|
-
if (isQueryOne(
|
|
832
|
-
predicate = ModelPredicateCreator.
|
|
925
|
+
if (isQueryOne(identifierOrCriteria)) {
|
|
926
|
+
predicate = ModelPredicateCreator.createForSingleField<T>(
|
|
833
927
|
modelDefinition,
|
|
834
|
-
|
|
928
|
+
keyFields[0],
|
|
929
|
+
identifierOrCriteria
|
|
835
930
|
);
|
|
836
931
|
} else {
|
|
837
|
-
|
|
932
|
+
// Object is being queried using object literal syntax
|
|
933
|
+
if (isIdentifierObject(<T>identifierOrCriteria, modelDefinition)) {
|
|
934
|
+
predicate = ModelPredicateCreator.createForPk<T>(
|
|
935
|
+
modelDefinition,
|
|
936
|
+
<T>identifierOrCriteria
|
|
937
|
+
);
|
|
938
|
+
} else if (isPredicatesAll(identifierOrCriteria)) {
|
|
838
939
|
// Predicates.ALL means "all records", so no predicate (undefined)
|
|
839
940
|
predicate = undefined;
|
|
840
941
|
} else {
|
|
841
942
|
predicate = ModelPredicateCreator.createFromExisting(
|
|
842
943
|
modelDefinition,
|
|
843
|
-
|
|
944
|
+
<any>identifierOrCriteria
|
|
844
945
|
);
|
|
845
946
|
}
|
|
846
947
|
}
|
|
@@ -870,7 +971,13 @@ class DataStore {
|
|
|
870
971
|
pagination
|
|
871
972
|
);
|
|
872
973
|
|
|
873
|
-
|
|
974
|
+
const isSingleFieldIdentifier = keyFields.length === 1;
|
|
975
|
+
const returnOne =
|
|
976
|
+
isSingleFieldIdentifier &&
|
|
977
|
+
(isQueryOne(identifierOrCriteria) ||
|
|
978
|
+
isIdentifierObject(identifierOrCriteria, modelDefinition));
|
|
979
|
+
|
|
980
|
+
return returnOne ? result[0] : result;
|
|
874
981
|
};
|
|
875
982
|
|
|
876
983
|
save = async <T extends PersistentModel>(
|
|
@@ -883,7 +990,7 @@ class DataStore {
|
|
|
883
990
|
// Allows us to only include changed fields for updates
|
|
884
991
|
const patchesTuple = modelPatchesMap.get(model);
|
|
885
992
|
|
|
886
|
-
const modelConstructor: PersistentModelConstructor<T> = model
|
|
993
|
+
const modelConstructor: PersistentModelConstructor<T> | undefined = model
|
|
887
994
|
? <PersistentModelConstructor<T>>model.constructor
|
|
888
995
|
: undefined;
|
|
889
996
|
|
|
@@ -898,15 +1005,15 @@ class DataStore {
|
|
|
898
1005
|
|
|
899
1006
|
const producedCondition = ModelPredicateCreator.createFromExisting(
|
|
900
1007
|
modelDefinition,
|
|
901
|
-
condition
|
|
1008
|
+
condition!
|
|
902
1009
|
);
|
|
903
1010
|
|
|
904
1011
|
const [savedModel] = await this.storage.runExclusive(async s => {
|
|
905
1012
|
await s.save(model, producedCondition, undefined, patchesTuple);
|
|
906
1013
|
|
|
907
|
-
return s.query(
|
|
1014
|
+
return s.query<T>(
|
|
908
1015
|
modelConstructor,
|
|
909
|
-
ModelPredicateCreator.
|
|
1016
|
+
ModelPredicateCreator.createForPk(modelDefinition, model)
|
|
910
1017
|
);
|
|
911
1018
|
});
|
|
912
1019
|
|
|
@@ -946,22 +1053,28 @@ class DataStore {
|
|
|
946
1053
|
};
|
|
947
1054
|
|
|
948
1055
|
delete: {
|
|
949
|
-
<T extends PersistentModel>(
|
|
950
|
-
model: T,
|
|
951
|
-
condition?: ProducerModelPredicate<T>
|
|
952
|
-
): Promise<T>;
|
|
953
1056
|
<T extends PersistentModel>(
|
|
954
1057
|
modelConstructor: PersistentModelConstructor<T>,
|
|
955
|
-
|
|
1058
|
+
identifier: IdentifierFieldOrIdentifierObject<
|
|
1059
|
+
T,
|
|
1060
|
+
PersistentModelMetaData<T>
|
|
1061
|
+
>
|
|
956
1062
|
): Promise<T[]>;
|
|
957
1063
|
<T extends PersistentModel>(
|
|
958
1064
|
modelConstructor: PersistentModelConstructor<T>,
|
|
959
1065
|
condition: ProducerModelPredicate<T> | typeof PredicateAll
|
|
960
1066
|
): Promise<T[]>;
|
|
1067
|
+
<T extends PersistentModel>(
|
|
1068
|
+
model: T,
|
|
1069
|
+
condition?: ProducerModelPredicate<T>
|
|
1070
|
+
): Promise<T>;
|
|
961
1071
|
} = async <T extends PersistentModel>(
|
|
962
1072
|
modelOrConstructor: T | PersistentModelConstructor<T>,
|
|
963
|
-
|
|
964
|
-
|
|
1073
|
+
identifierOrCriteria?:
|
|
1074
|
+
| IdentifierFieldOrIdentifierObject<T, PersistentModelMetaData<T>>
|
|
1075
|
+
| ProducerModelPredicate<T>
|
|
1076
|
+
| typeof PredicateAll
|
|
1077
|
+
): Promise<T | T[]> => {
|
|
965
1078
|
await this.start();
|
|
966
1079
|
|
|
967
1080
|
let condition: ModelPredicate<T>;
|
|
@@ -973,31 +1086,43 @@ class DataStore {
|
|
|
973
1086
|
throw new Error(msg);
|
|
974
1087
|
}
|
|
975
1088
|
|
|
976
|
-
if (isValidModelConstructor(modelOrConstructor)) {
|
|
1089
|
+
if (isValidModelConstructor<T>(modelOrConstructor)) {
|
|
977
1090
|
const modelConstructor = modelOrConstructor;
|
|
978
1091
|
|
|
979
|
-
if (!
|
|
1092
|
+
if (!identifierOrCriteria) {
|
|
980
1093
|
const msg =
|
|
981
1094
|
'Id to delete or criteria required. Do you want to delete all? Pass Predicates.ALL';
|
|
982
|
-
logger.error(msg, {
|
|
1095
|
+
logger.error(msg, { identifierOrCriteria });
|
|
983
1096
|
|
|
984
1097
|
throw new Error(msg);
|
|
985
1098
|
}
|
|
986
1099
|
|
|
987
|
-
|
|
988
|
-
|
|
1100
|
+
const modelDefinition = getModelDefinition(modelConstructor);
|
|
1101
|
+
|
|
1102
|
+
if (typeof identifierOrCriteria === 'string') {
|
|
1103
|
+
const [keyField] = extractPrimaryKeyFieldNames(modelDefinition);
|
|
1104
|
+
|
|
1105
|
+
condition = ModelPredicateCreator.createForSingleField<T>(
|
|
989
1106
|
getModelDefinition(modelConstructor),
|
|
990
|
-
|
|
1107
|
+
keyField,
|
|
1108
|
+
identifierOrCriteria
|
|
991
1109
|
);
|
|
992
1110
|
} else {
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1111
|
+
if (isIdentifierObject(identifierOrCriteria, modelDefinition)) {
|
|
1112
|
+
condition = ModelPredicateCreator.createForPk<T>(
|
|
1113
|
+
modelDefinition,
|
|
1114
|
+
<T>identifierOrCriteria
|
|
1115
|
+
);
|
|
1116
|
+
} else {
|
|
1117
|
+
condition = ModelPredicateCreator.createFromExisting(
|
|
1118
|
+
modelDefinition,
|
|
1119
|
+
/**
|
|
1120
|
+
* idOrCriteria is always a ProducerModelPredicate<T>, never a symbol.
|
|
1121
|
+
* The symbol is used only for typing purposes. e.g. see Predicates.ALL
|
|
1122
|
+
*/
|
|
1123
|
+
identifierOrCriteria as ProducerModelPredicate<T>
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
1001
1126
|
|
|
1002
1127
|
if (!condition || !ModelPredicateCreator.isValidPredicate(condition)) {
|
|
1003
1128
|
const msg =
|
|
@@ -1025,22 +1150,24 @@ class DataStore {
|
|
|
1025
1150
|
|
|
1026
1151
|
const modelDefinition = getModelDefinition(modelConstructor);
|
|
1027
1152
|
|
|
1028
|
-
const
|
|
1153
|
+
const pkPredicate = ModelPredicateCreator.createForPk<T>(
|
|
1029
1154
|
modelDefinition,
|
|
1030
|
-
model
|
|
1155
|
+
model
|
|
1031
1156
|
);
|
|
1032
1157
|
|
|
1033
|
-
if (
|
|
1034
|
-
if (typeof
|
|
1158
|
+
if (identifierOrCriteria) {
|
|
1159
|
+
if (typeof identifierOrCriteria !== 'function') {
|
|
1035
1160
|
const msg = 'Invalid criteria';
|
|
1036
|
-
logger.error(msg, {
|
|
1161
|
+
logger.error(msg, { identifierOrCriteria });
|
|
1037
1162
|
|
|
1038
1163
|
throw new Error(msg);
|
|
1039
1164
|
}
|
|
1040
1165
|
|
|
1041
|
-
condition =
|
|
1166
|
+
condition = (<ProducerModelPredicate<T>>identifierOrCriteria)(
|
|
1167
|
+
pkPredicate
|
|
1168
|
+
);
|
|
1042
1169
|
} else {
|
|
1043
|
-
condition =
|
|
1170
|
+
condition = pkPredicate;
|
|
1044
1171
|
}
|
|
1045
1172
|
|
|
1046
1173
|
const [[deleted]] = await this.storage.delete(model, condition);
|
|
@@ -1052,20 +1179,28 @@ class DataStore {
|
|
|
1052
1179
|
observe: {
|
|
1053
1180
|
(): Observable<SubscriptionMessage<PersistentModel>>;
|
|
1054
1181
|
|
|
1055
|
-
<T extends PersistentModel>(
|
|
1182
|
+
<T extends PersistentModel>(
|
|
1183
|
+
modelConstructor: PersistentModelConstructor<T>,
|
|
1184
|
+
identifier: string
|
|
1185
|
+
): Observable<SubscriptionMessage<T>>;
|
|
1056
1186
|
|
|
1057
1187
|
<T extends PersistentModel>(
|
|
1058
1188
|
modelConstructor: PersistentModelConstructor<T>,
|
|
1059
|
-
criteria?:
|
|
1189
|
+
criteria?: ProducerModelPredicate<T> | typeof PredicateAll
|
|
1060
1190
|
): Observable<SubscriptionMessage<T>>;
|
|
1061
|
-
|
|
1191
|
+
|
|
1192
|
+
<T extends PersistentModel>(model: T): Observable<SubscriptionMessage<T>>;
|
|
1193
|
+
} = <T extends PersistentModel>(
|
|
1062
1194
|
modelOrConstructor?: T | PersistentModelConstructor<T>,
|
|
1063
|
-
|
|
1195
|
+
identifierOrCriteria?:
|
|
1196
|
+
| string
|
|
1197
|
+
| ProducerModelPredicate<T>
|
|
1198
|
+
| typeof PredicateAll
|
|
1064
1199
|
): Observable<SubscriptionMessage<T>> => {
|
|
1065
1200
|
let predicate: ModelPredicate<T>;
|
|
1066
1201
|
|
|
1067
|
-
const modelConstructor: PersistentModelConstructor<T> =
|
|
1068
|
-
modelOrConstructor && isValidModelConstructor(modelOrConstructor)
|
|
1202
|
+
const modelConstructor: PersistentModelConstructor<T> | undefined =
|
|
1203
|
+
modelOrConstructor && isValidModelConstructor<T>(modelOrConstructor)
|
|
1069
1204
|
? modelOrConstructor
|
|
1070
1205
|
: undefined;
|
|
1071
1206
|
|
|
@@ -1075,10 +1210,10 @@ class DataStore {
|
|
|
1075
1210
|
model && (<Object>Object.getPrototypeOf(model)).constructor;
|
|
1076
1211
|
|
|
1077
1212
|
if (isValidModelConstructor<T>(modelConstructor)) {
|
|
1078
|
-
if (
|
|
1213
|
+
if (identifierOrCriteria) {
|
|
1079
1214
|
logger.warn('idOrCriteria is ignored when using a model instance', {
|
|
1080
1215
|
model,
|
|
1081
|
-
|
|
1216
|
+
identifierOrCriteria,
|
|
1082
1217
|
});
|
|
1083
1218
|
}
|
|
1084
1219
|
|
|
@@ -1092,9 +1227,9 @@ class DataStore {
|
|
|
1092
1227
|
}
|
|
1093
1228
|
}
|
|
1094
1229
|
|
|
1095
|
-
if (
|
|
1230
|
+
if (identifierOrCriteria !== undefined && modelConstructor === undefined) {
|
|
1096
1231
|
const msg = 'Cannot provide criteria without a modelConstructor';
|
|
1097
|
-
logger.error(msg,
|
|
1232
|
+
logger.error(msg, identifierOrCriteria);
|
|
1098
1233
|
throw new Error(msg);
|
|
1099
1234
|
}
|
|
1100
1235
|
|
|
@@ -1105,18 +1240,26 @@ class DataStore {
|
|
|
1105
1240
|
throw new Error(msg);
|
|
1106
1241
|
}
|
|
1107
1242
|
|
|
1108
|
-
if (typeof
|
|
1109
|
-
|
|
1243
|
+
if (typeof identifierOrCriteria === 'string') {
|
|
1244
|
+
const modelDefinition = getModelDefinition(modelConstructor);
|
|
1245
|
+
const [keyField] = extractPrimaryKeyFieldNames(modelDefinition);
|
|
1246
|
+
|
|
1247
|
+
predicate = ModelPredicateCreator.createForSingleField<T>(
|
|
1110
1248
|
getModelDefinition(modelConstructor),
|
|
1111
|
-
|
|
1249
|
+
keyField,
|
|
1250
|
+
identifierOrCriteria
|
|
1112
1251
|
);
|
|
1113
1252
|
} else {
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1253
|
+
if (isPredicatesAll(identifierOrCriteria)) {
|
|
1254
|
+
predicate = undefined;
|
|
1255
|
+
} else {
|
|
1256
|
+
predicate =
|
|
1257
|
+
modelConstructor &&
|
|
1258
|
+
ModelPredicateCreator.createFromExisting<T>(
|
|
1259
|
+
getModelDefinition(modelConstructor),
|
|
1260
|
+
identifierOrCriteria
|
|
1261
|
+
);
|
|
1262
|
+
}
|
|
1120
1263
|
}
|
|
1121
1264
|
|
|
1122
1265
|
return new Observable<SubscriptionMessage<T>>(observer => {
|
|
@@ -1125,10 +1268,43 @@ class DataStore {
|
|
|
1125
1268
|
(async () => {
|
|
1126
1269
|
await this.start();
|
|
1127
1270
|
|
|
1271
|
+
// Filter the events returned by Storage according to namespace,
|
|
1272
|
+
// append original element data, and subscribe to the observable
|
|
1128
1273
|
handle = this.storage
|
|
1129
1274
|
.observe(modelConstructor, predicate)
|
|
1130
1275
|
.filter(({ model }) => namespaceResolver(model) === USER)
|
|
1131
|
-
.subscribe(
|
|
1276
|
+
.subscribe({
|
|
1277
|
+
next: async item => {
|
|
1278
|
+
// the `element` doesn't necessarily contain all item details or
|
|
1279
|
+
// have related records attached consistently with that of a query()
|
|
1280
|
+
// result item. for consistency, we attach them here.
|
|
1281
|
+
|
|
1282
|
+
let message = item;
|
|
1283
|
+
|
|
1284
|
+
// as long as we're not dealing with a DELETE, we need to fetch a fresh
|
|
1285
|
+
// item from storage to ensure it's fully populated.
|
|
1286
|
+
if (item.opType !== 'DELETE') {
|
|
1287
|
+
const modelDefinition = getModelDefinition(item.model);
|
|
1288
|
+
const keyFields = extractPrimaryKeyFieldNames(modelDefinition);
|
|
1289
|
+
const primaryKeysAndValues = extractPrimaryKeysAndValues(
|
|
1290
|
+
item.element,
|
|
1291
|
+
keyFields
|
|
1292
|
+
);
|
|
1293
|
+
const freshElement = await this.query(
|
|
1294
|
+
item.model,
|
|
1295
|
+
primaryKeysAndValues
|
|
1296
|
+
);
|
|
1297
|
+
message = {
|
|
1298
|
+
...message,
|
|
1299
|
+
element: freshElement as T,
|
|
1300
|
+
};
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
observer.next(message as SubscriptionMessage<T>);
|
|
1304
|
+
},
|
|
1305
|
+
error: err => observer.error(err),
|
|
1306
|
+
complete: () => observer.complete(),
|
|
1307
|
+
});
|
|
1132
1308
|
})();
|
|
1133
1309
|
|
|
1134
1310
|
return () => {
|
|
@@ -1145,7 +1321,7 @@ class DataStore {
|
|
|
1145
1321
|
criteria?: ProducerModelPredicate<T> | typeof PredicateAll,
|
|
1146
1322
|
paginationProducer?: ObserveQueryOptions<T>
|
|
1147
1323
|
): Observable<DataStoreSnapshot<T>>;
|
|
1148
|
-
} = <T extends PersistentModel
|
|
1324
|
+
} = <T extends PersistentModel>(
|
|
1149
1325
|
model: PersistentModelConstructor<T>,
|
|
1150
1326
|
criteria?: ProducerModelPredicate<T> | typeof PredicateAll,
|
|
1151
1327
|
options?: ObserveQueryOptions<T>
|
|
@@ -1155,7 +1331,18 @@ class DataStore {
|
|
|
1155
1331
|
const itemsChanged = new Map<string, T>();
|
|
1156
1332
|
let deletedItemIds: string[] = [];
|
|
1157
1333
|
let handle: ZenObservable.Subscription;
|
|
1158
|
-
|
|
1334
|
+
let predicate: ModelPredicate<T>;
|
|
1335
|
+
|
|
1336
|
+
/**
|
|
1337
|
+
* As the name suggests, this geneates a snapshot in the form of
|
|
1338
|
+
* `{items: T[], isSynced: boolean}`
|
|
1339
|
+
* and sends it to the observer.
|
|
1340
|
+
*
|
|
1341
|
+
* SIDE EFFECT: The underlying generation and emission methods may touch:
|
|
1342
|
+
* `items`, `itemsChanged`, and `deletedItemIds`.
|
|
1343
|
+
*
|
|
1344
|
+
* Refer to `generateSnapshot` and `emitSnapshot` for more details.
|
|
1345
|
+
*/
|
|
1159
1346
|
const generateAndEmitSnapshot = (): void => {
|
|
1160
1347
|
const snapshot = generateSnapshot();
|
|
1161
1348
|
emitSnapshot(snapshot);
|
|
@@ -1172,41 +1359,101 @@ class DataStore {
|
|
|
1172
1359
|
const { sort } = options || {};
|
|
1173
1360
|
const sortOptions = sort ? { sort } : undefined;
|
|
1174
1361
|
|
|
1362
|
+
const modelDefinition = getModelDefinition(model);
|
|
1363
|
+
const keyFields = extractPrimaryKeyFieldNames(modelDefinition);
|
|
1364
|
+
|
|
1365
|
+
if (isQueryOne(criteria)) {
|
|
1366
|
+
predicate = ModelPredicateCreator.createForSingleField<T>(
|
|
1367
|
+
modelDefinition,
|
|
1368
|
+
keyFields[0],
|
|
1369
|
+
criteria
|
|
1370
|
+
);
|
|
1371
|
+
} else {
|
|
1372
|
+
if (isPredicatesAll(criteria)) {
|
|
1373
|
+
// Predicates.ALL means "all records", so no predicate (undefined)
|
|
1374
|
+
predicate = undefined;
|
|
1375
|
+
} else {
|
|
1376
|
+
predicate = ModelPredicateCreator.createFromExisting(
|
|
1377
|
+
modelDefinition,
|
|
1378
|
+
criteria
|
|
1379
|
+
);
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
const { predicates, type: predicateGroupType } =
|
|
1384
|
+
ModelPredicateCreator.getPredicates(predicate, false) || {};
|
|
1385
|
+
const hasPredicate = !!predicates;
|
|
1386
|
+
|
|
1175
1387
|
(async () => {
|
|
1176
1388
|
try {
|
|
1177
1389
|
// first, query and return any locally-available records
|
|
1178
|
-
(await this.query(model, criteria, sortOptions)).forEach(item =>
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
handle = this.observe(
|
|
1184
|
-
model,
|
|
1185
|
-
// @ts-ignore TODO: fix this TSlint error
|
|
1186
|
-
criteria
|
|
1187
|
-
).subscribe(({ element, model, opType }) => {
|
|
1188
|
-
// Flag items which have been recently deleted
|
|
1189
|
-
// NOTE: Merging of separate operations to the same model instance is handled upstream
|
|
1190
|
-
// in the `mergePage` method within src/sync/merger.ts. The final state of a model instance
|
|
1191
|
-
// depends on the LATEST record (for a given id).
|
|
1192
|
-
if (opType === 'DELETE') {
|
|
1193
|
-
deletedItemIds.push(element.id);
|
|
1194
|
-
} else {
|
|
1195
|
-
itemsChanged.set(element.id, element);
|
|
1390
|
+
(await this.query(model, criteria, sortOptions)).forEach(item => {
|
|
1391
|
+
let record = item;
|
|
1392
|
+
// TODO: fix query
|
|
1393
|
+
if (Array.isArray(item)) {
|
|
1394
|
+
record = item[0];
|
|
1196
1395
|
}
|
|
1396
|
+
const itemModelDefinition = getModelDefinition(model);
|
|
1397
|
+
const idOrPk = getIdentifierValue(itemModelDefinition, record);
|
|
1398
|
+
items.set(idOrPk, record);
|
|
1399
|
+
});
|
|
1400
|
+
|
|
1401
|
+
// Observe the model and send a stream of updates (debounced).
|
|
1402
|
+
// We need to post-filter results instead of passing criteria through
|
|
1403
|
+
// to have visibility into items that move from in-set to out-of-set.
|
|
1404
|
+
// We need to explicitly remove those items from the existing snapshot.
|
|
1405
|
+
handle = this.observe(model).subscribe(
|
|
1406
|
+
({ element, model, opType }) => {
|
|
1407
|
+
let record = element;
|
|
1408
|
+
|
|
1409
|
+
// TODO: fix query
|
|
1410
|
+
if (Array.isArray(element)) {
|
|
1411
|
+
record = element[0];
|
|
1412
|
+
}
|
|
1413
|
+
const itemModelDefinition = getModelDefinition(model);
|
|
1414
|
+
const idOrPk = getIdentifierValue(itemModelDefinition, record);
|
|
1415
|
+
if (
|
|
1416
|
+
hasPredicate &&
|
|
1417
|
+
!validatePredicate(record, predicateGroupType, predicates)
|
|
1418
|
+
) {
|
|
1419
|
+
if (
|
|
1420
|
+
opType === 'UPDATE' &&
|
|
1421
|
+
(items.has(idOrPk) || itemsChanged.has(idOrPk))
|
|
1422
|
+
) {
|
|
1423
|
+
// tracking as a "deleted item" will include the item in
|
|
1424
|
+
// page limit calculations and ensure it is removed from the
|
|
1425
|
+
// final items collection, regardless of which collection(s)
|
|
1426
|
+
// it is currently in. (I mean, it could be in both, right!?)
|
|
1427
|
+
deletedItemIds.push(idOrPk);
|
|
1428
|
+
} else {
|
|
1429
|
+
// ignore updates for irrelevant/filtered items.
|
|
1430
|
+
return;
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1197
1433
|
|
|
1198
|
-
|
|
1434
|
+
// Flag items which have been recently deleted
|
|
1435
|
+
// NOTE: Merging of separate operations to the same model instance is handled upstream
|
|
1436
|
+
// in the `mergePage` method within src/sync/merger.ts. The final state of a model instance
|
|
1437
|
+
// depends on the LATEST record (for a given id).
|
|
1438
|
+
if (opType === 'DELETE') {
|
|
1439
|
+
deletedItemIds.push(idOrPk);
|
|
1440
|
+
} else {
|
|
1441
|
+
itemsChanged.set(idOrPk, record);
|
|
1442
|
+
}
|
|
1199
1443
|
|
|
1200
|
-
|
|
1201
|
-
itemsChanged.size - deletedItemIds.length >= this.syncPageSize;
|
|
1444
|
+
const isSynced = this.sync?.getModelSyncedStatus(model) ?? false;
|
|
1202
1445
|
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
}
|
|
1446
|
+
const limit =
|
|
1447
|
+
itemsChanged.size - deletedItemIds.length >= this.syncPageSize;
|
|
1206
1448
|
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1449
|
+
if (limit || isSynced) {
|
|
1450
|
+
limitTimerRace.resolve();
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
// kicks off every subsequent race as results sync down
|
|
1454
|
+
limitTimerRace.start();
|
|
1455
|
+
}
|
|
1456
|
+
);
|
|
1210
1457
|
|
|
1211
1458
|
// returns a set of initial/locally-available results
|
|
1212
1459
|
generateAndEmitSnapshot();
|
|
@@ -1215,7 +1462,12 @@ class DataStore {
|
|
|
1215
1462
|
}
|
|
1216
1463
|
})();
|
|
1217
1464
|
|
|
1218
|
-
|
|
1465
|
+
/**
|
|
1466
|
+
* Combines the `items`, `itemsChanged`, and `deletedItemIds` collections into
|
|
1467
|
+
* a snapshot in the form of `{ items: T[], isSynced: boolean}`.
|
|
1468
|
+
*
|
|
1469
|
+
* SIDE EFFECT: The shared `items` collection is recreated.
|
|
1470
|
+
*/
|
|
1219
1471
|
const generateSnapshot = (): DataStoreSnapshot<T> => {
|
|
1220
1472
|
const isSynced = this.sync?.getModelSyncedStatus(model) ?? false;
|
|
1221
1473
|
const itemsArray = [
|
|
@@ -1228,10 +1480,21 @@ class DataStore {
|
|
|
1228
1480
|
}
|
|
1229
1481
|
|
|
1230
1482
|
items.clear();
|
|
1231
|
-
itemsArray.forEach(item =>
|
|
1483
|
+
itemsArray.forEach(item => {
|
|
1484
|
+
// CPK TODO: fix query
|
|
1485
|
+
let record = item;
|
|
1486
|
+
|
|
1487
|
+
if (Array.isArray(item)) {
|
|
1488
|
+
record = item[0];
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
const itemModelDefinition = getModelDefinition(model);
|
|
1492
|
+
const idOrPk = getIdentifierValue(itemModelDefinition, record);
|
|
1493
|
+
items.set(idOrPk, record);
|
|
1494
|
+
});
|
|
1232
1495
|
|
|
1233
1496
|
// remove deleted items from the final result set
|
|
1234
|
-
deletedItemIds.forEach(
|
|
1497
|
+
deletedItemIds.forEach(idOrPk => items.delete(idOrPk));
|
|
1235
1498
|
|
|
1236
1499
|
return {
|
|
1237
1500
|
items: Array.from(items.values()),
|
|
@@ -1239,6 +1502,14 @@ class DataStore {
|
|
|
1239
1502
|
};
|
|
1240
1503
|
};
|
|
1241
1504
|
|
|
1505
|
+
/**
|
|
1506
|
+
* Emits the list of items to the observer.
|
|
1507
|
+
*
|
|
1508
|
+
* SIDE EFFECT: `itemsChanged` and `deletedItemIds` are cleared to prepare
|
|
1509
|
+
* for the next snapshot.
|
|
1510
|
+
*
|
|
1511
|
+
* @param snapshot The generated items data to emit.
|
|
1512
|
+
*/
|
|
1242
1513
|
const emitSnapshot = (snapshot: DataStoreSnapshot<T>): void => {
|
|
1243
1514
|
// send the generated snapshot to the primary subscription
|
|
1244
1515
|
observer.next(snapshot);
|
|
@@ -1248,6 +1519,12 @@ class DataStore {
|
|
|
1248
1519
|
deletedItemIds = [];
|
|
1249
1520
|
};
|
|
1250
1521
|
|
|
1522
|
+
/**
|
|
1523
|
+
* Sorts an `Array` of `T` according to the sort instructions given in the
|
|
1524
|
+
* original `observeQuery()` call.
|
|
1525
|
+
*
|
|
1526
|
+
* @param itemsToSort A array of model type.
|
|
1527
|
+
*/
|
|
1251
1528
|
const sortItems = (itemsToSort: T[]): void => {
|
|
1252
1529
|
const modelDefinition = getModelDefinition(model);
|
|
1253
1530
|
const pagination = this.processPagination(modelDefinition, options);
|
|
@@ -1262,7 +1539,14 @@ class DataStore {
|
|
|
1262
1539
|
}
|
|
1263
1540
|
};
|
|
1264
1541
|
|
|
1265
|
-
|
|
1542
|
+
/**
|
|
1543
|
+
* Force one last snapshot when the model is fully synced.
|
|
1544
|
+
*
|
|
1545
|
+
* This reduces latency for that last snapshot, which will otherwise
|
|
1546
|
+
* wait for the configured timeout.
|
|
1547
|
+
*
|
|
1548
|
+
* @param payload The payload from the Hub event.
|
|
1549
|
+
*/
|
|
1266
1550
|
const hubCallback = ({ payload }): void => {
|
|
1267
1551
|
const { event, data } = payload;
|
|
1268
1552
|
if (
|
|
@@ -1284,6 +1568,10 @@ class DataStore {
|
|
|
1284
1568
|
};
|
|
1285
1569
|
|
|
1286
1570
|
configure = (config: DataStoreConfig = {}) => {
|
|
1571
|
+
this.amplifyContext.Auth = this.Auth;
|
|
1572
|
+
this.amplifyContext.API = this.API;
|
|
1573
|
+
this.amplifyContext.Cache = this.Cache;
|
|
1574
|
+
|
|
1287
1575
|
const {
|
|
1288
1576
|
DataStore: configDataStore,
|
|
1289
1577
|
authModeStrategyType: configAuthModeStrategyType,
|
|
@@ -1313,7 +1601,7 @@ class DataStore {
|
|
|
1313
1601
|
|
|
1314
1602
|
switch (authModeStrategyType) {
|
|
1315
1603
|
case AuthModeStrategyType.MULTI_AUTH:
|
|
1316
|
-
this.authModeStrategy = multiAuthStrategy;
|
|
1604
|
+
this.authModeStrategy = multiAuthStrategy(this.amplifyContext);
|
|
1317
1605
|
break;
|
|
1318
1606
|
case AuthModeStrategyType.DEFAULT:
|
|
1319
1607
|
this.authModeStrategy = defaultAuthStrategy;
|
|
@@ -1329,12 +1617,13 @@ class DataStore {
|
|
|
1329
1617
|
|
|
1330
1618
|
this.syncExpressions =
|
|
1331
1619
|
(configDataStore && configDataStore.syncExpressions) ||
|
|
1332
|
-
|
|
1333
|
-
|
|
1620
|
+
configSyncExpressions ||
|
|
1621
|
+
this.syncExpressions;
|
|
1334
1622
|
|
|
1335
1623
|
this.maxRecordsToSync =
|
|
1336
1624
|
(configDataStore && configDataStore.maxRecordsToSync) ||
|
|
1337
1625
|
configMaxRecordsToSync ||
|
|
1626
|
+
this.maxRecordsToSync ||
|
|
1338
1627
|
10000;
|
|
1339
1628
|
|
|
1340
1629
|
// store on config object, so that Sync, Subscription, and Mutation processors can have access
|
|
@@ -1343,6 +1632,7 @@ class DataStore {
|
|
|
1343
1632
|
this.syncPageSize =
|
|
1344
1633
|
(configDataStore && configDataStore.syncPageSize) ||
|
|
1345
1634
|
configSyncPageSize ||
|
|
1635
|
+
this.syncPageSize ||
|
|
1346
1636
|
1000;
|
|
1347
1637
|
|
|
1348
1638
|
// store on config object, so that Sync, Subscription, and Mutation processors can have access
|
|
@@ -1350,22 +1640,32 @@ class DataStore {
|
|
|
1350
1640
|
|
|
1351
1641
|
this.fullSyncInterval =
|
|
1352
1642
|
(configDataStore && configDataStore.fullSyncInterval) ||
|
|
1353
|
-
this.fullSyncInterval ||
|
|
1354
1643
|
configFullSyncInterval ||
|
|
1644
|
+
this.fullSyncInterval ||
|
|
1355
1645
|
24 * 60; // 1 day
|
|
1356
1646
|
|
|
1357
1647
|
this.storageAdapter =
|
|
1358
1648
|
(configDataStore && configDataStore.storageAdapter) ||
|
|
1359
|
-
this.storageAdapter ||
|
|
1360
1649
|
configStorageAdapter ||
|
|
1650
|
+
this.storageAdapter ||
|
|
1361
1651
|
undefined;
|
|
1362
1652
|
|
|
1363
|
-
this.sessionId = this.retrieveSessionId()
|
|
1653
|
+
this.sessionId = this.retrieveSessionId()!;
|
|
1364
1654
|
};
|
|
1365
1655
|
|
|
1366
1656
|
clear = async function clear() {
|
|
1657
|
+
checkSchemaInitialized();
|
|
1367
1658
|
if (this.storage === undefined) {
|
|
1368
|
-
|
|
1659
|
+
// connect to storage so that it can be cleared without fully starting DataStore
|
|
1660
|
+
this.storage = new Storage(
|
|
1661
|
+
schema,
|
|
1662
|
+
namespaceResolver,
|
|
1663
|
+
getModelConstructorByModelName,
|
|
1664
|
+
modelInstanceCreator,
|
|
1665
|
+
this.storageAdapter,
|
|
1666
|
+
this.sessionId
|
|
1667
|
+
);
|
|
1668
|
+
await this.storage.init();
|
|
1369
1669
|
}
|
|
1370
1670
|
|
|
1371
1671
|
if (syncSubscription && !syncSubscription.closed) {
|
|
@@ -1384,7 +1684,7 @@ class DataStore {
|
|
|
1384
1684
|
this.syncPredicates = new WeakMap<SchemaModel, ModelPredicate<any>>();
|
|
1385
1685
|
};
|
|
1386
1686
|
|
|
1387
|
-
stop = async function stop() {
|
|
1687
|
+
stop = async function stop(this: InstanceType<typeof DataStore>) {
|
|
1388
1688
|
if (this.initialized !== undefined) {
|
|
1389
1689
|
await this.start();
|
|
1390
1690
|
}
|
|
@@ -1548,9 +1848,9 @@ class DataStore {
|
|
|
1548
1848
|
|
|
1549
1849
|
return `${sessionId}-${appSyncId}`;
|
|
1550
1850
|
}
|
|
1551
|
-
} catch {
|
|
1552
|
-
|
|
1553
|
-
|
|
1851
|
+
} catch {}
|
|
1852
|
+
|
|
1853
|
+
return undefined;
|
|
1554
1854
|
}
|
|
1555
1855
|
}
|
|
1556
1856
|
|