@aws-amplify/datastore 3.14.0 → 3.14.1-unstable.10
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 +2799 -1459
- package/dist/aws-amplify-datastore.js.map +1 -1
- package/dist/aws-amplify-datastore.min.js +10 -10
- package/dist/aws-amplify-datastore.min.js.map +1 -1
- package/lib/authModeStrategies/multiAuthStrategy.js +11 -0
- package/lib/authModeStrategies/multiAuthStrategy.js.map +1 -1
- package/lib/datastore/datastore.js +524 -323
- package/lib/datastore/datastore.js.map +1 -1
- package/lib/storage/adapter/IndexedDBAdapter.js +76 -25
- package/lib/storage/adapter/IndexedDBAdapter.js.map +1 -1
- package/lib/storage/storage.js +2 -2
- package/lib/storage/storage.js.map +1 -1
- package/lib/sync/datastoreConnectivity.js +45 -0
- package/lib/sync/datastoreConnectivity.js.map +1 -1
- package/lib/sync/index.js +518 -395
- package/lib/sync/index.js.map +1 -1
- package/lib/sync/merger.js +6 -0
- package/lib/sync/merger.js.map +1 -1
- package/lib/sync/outbox.js +66 -62
- package/lib/sync/outbox.js.map +1 -1
- package/lib/sync/processors/mutation.js +207 -165
- package/lib/sync/processors/mutation.js.map +1 -1
- package/lib/sync/processors/subscription.js +210 -175
- package/lib/sync/processors/subscription.js.map +1 -1
- package/lib/sync/processors/sync.js +95 -72
- package/lib/sync/processors/sync.js.map +1 -1
- package/lib/sync/utils.js +1 -3
- package/lib/sync/utils.js.map +1 -1
- package/lib/util.js +89 -0
- package/lib/util.js.map +1 -1
- package/lib-esm/authModeStrategies/multiAuthStrategy.d.ts +11 -0
- package/lib-esm/authModeStrategies/multiAuthStrategy.js +11 -0
- package/lib-esm/authModeStrategies/multiAuthStrategy.js.map +1 -1
- package/lib-esm/datastore/datastore.d.ts +95 -2
- package/lib-esm/datastore/datastore.js +524 -323
- package/lib-esm/datastore/datastore.js.map +1 -1
- package/lib-esm/storage/adapter/IndexedDBAdapter.d.ts +21 -0
- package/lib-esm/storage/adapter/IndexedDBAdapter.js +77 -26
- package/lib-esm/storage/adapter/IndexedDBAdapter.js.map +1 -1
- package/lib-esm/storage/storage.js +2 -2
- package/lib-esm/storage/storage.js.map +1 -1
- package/lib-esm/sync/datastoreConnectivity.d.ts +1 -0
- package/lib-esm/sync/datastoreConnectivity.js +45 -0
- package/lib-esm/sync/datastoreConnectivity.js.map +1 -1
- package/lib-esm/sync/index.d.ts +9 -1
- package/lib-esm/sync/index.js +519 -396
- package/lib-esm/sync/index.js.map +1 -1
- package/lib-esm/sync/merger.d.ts +6 -0
- package/lib-esm/sync/merger.js +6 -0
- package/lib-esm/sync/merger.js.map +1 -1
- package/lib-esm/sync/outbox.js +66 -62
- package/lib-esm/sync/outbox.js.map +1 -1
- package/lib-esm/sync/processors/mutation.d.ts +2 -0
- package/lib-esm/sync/processors/mutation.js +208 -166
- package/lib-esm/sync/processors/mutation.js.map +1 -1
- package/lib-esm/sync/processors/subscription.d.ts +2 -0
- package/lib-esm/sync/processors/subscription.js +211 -176
- package/lib-esm/sync/processors/subscription.js.map +1 -1
- package/lib-esm/sync/processors/sync.d.ts +2 -0
- package/lib-esm/sync/processors/sync.js +96 -73
- package/lib-esm/sync/processors/sync.js.map +1 -1
- package/lib-esm/sync/utils.js +1 -3
- package/lib-esm/sync/utils.js.map +1 -1
- package/lib-esm/util.d.ts +11 -0
- package/lib-esm/util.js +89 -0
- package/lib-esm/util.js.map +1 -1
- package/package.json +7 -7
- package/src/authModeStrategies/multiAuthStrategy.ts +11 -0
- package/src/datastore/datastore.ts +572 -366
- package/src/storage/adapter/IndexedDBAdapter.ts +50 -9
- package/src/storage/storage.ts +2 -2
- package/src/sync/datastoreConnectivity.ts +6 -0
- package/src/sync/index.ts +492 -400
- package/src/sync/merger.ts +6 -0
- package/src/sync/outbox.ts +1 -1
- package/src/sync/processors/mutation.ts +139 -104
- package/src/sync/processors/subscription.ts +287 -250
- package/src/sync/processors/sync.ts +88 -60
- package/src/sync/utils.ts +1 -3
- package/src/util.ts +92 -2
- package/lib/authModeStrategies/defaultAuthStrategy.d.ts +0 -2
- package/lib/authModeStrategies/index.d.ts +0 -2
- package/lib/authModeStrategies/multiAuthStrategy.d.ts +0 -2
- package/lib/datastore/datastore.d.ts +0 -63
- package/lib/index.d.ts +0 -15
- package/lib/predicates/index.d.ts +0 -16
- package/lib/predicates/sort.d.ts +0 -8
- package/lib/ssr/index.d.ts +0 -3
- package/lib/storage/adapter/AsyncStorageAdapter.d.ts +0 -41
- package/lib/storage/adapter/AsyncStorageDatabase.d.ts +0 -39
- package/lib/storage/adapter/InMemoryStore.d.ts +0 -11
- package/lib/storage/adapter/InMemoryStore.native.d.ts +0 -1
- package/lib/storage/adapter/IndexedDBAdapter.d.ts +0 -38
- package/lib/storage/adapter/getDefaultAdapter/index.d.ts +0 -3
- package/lib/storage/adapter/getDefaultAdapter/index.native.d.ts +0 -3
- package/lib/storage/adapter/index.d.ts +0 -9
- package/lib/storage/storage.d.ts +0 -49
- package/lib/sync/datastoreConnectivity.d.ts +0 -15
- package/lib/sync/datastoreReachability/index.d.ts +0 -3
- package/lib/sync/datastoreReachability/index.native.d.ts +0 -3
- package/lib/sync/index.d.ts +0 -81
- package/lib/sync/merger.d.ts +0 -11
- package/lib/sync/outbox.d.ts +0 -27
- package/lib/sync/processors/errorMaps.d.ts +0 -17
- package/lib/sync/processors/mutation.d.ts +0 -56
- package/lib/sync/processors/subscription.d.ts +0 -31
- package/lib/sync/processors/sync.d.ts +0 -26
- package/lib/sync/utils.d.ts +0 -42
- package/lib/types.d.ts +0 -501
- package/lib/util.d.ts +0 -145
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import API from '@aws-amplify/api';
|
|
2
|
-
import { Amplify, ConsoleLogger as Logger, Hub, JS } from '@aws-amplify/core';
|
|
3
2
|
import { Auth } from '@aws-amplify/auth';
|
|
4
3
|
import Cache from '@aws-amplify/cache';
|
|
4
|
+
import {
|
|
5
|
+
Amplify,
|
|
6
|
+
ConsoleLogger as Logger,
|
|
7
|
+
Hub,
|
|
8
|
+
JS,
|
|
9
|
+
BackgroundProcessManager,
|
|
10
|
+
} from '@aws-amplify/core';
|
|
5
11
|
import {
|
|
6
12
|
Draft,
|
|
7
13
|
immerable,
|
|
@@ -90,6 +96,7 @@ import {
|
|
|
90
96
|
mergePatches,
|
|
91
97
|
} from '../util';
|
|
92
98
|
import { getIdentifierValue } from '../sync/utils';
|
|
99
|
+
import DataStoreConnectivity from '../sync/datastoreConnectivity';
|
|
93
100
|
|
|
94
101
|
setAutoFreeze(true);
|
|
95
102
|
enablePatches();
|
|
@@ -247,10 +254,14 @@ const initSchema = (userSchema: Schema) => {
|
|
|
247
254
|
return userClasses;
|
|
248
255
|
};
|
|
249
256
|
|
|
250
|
-
|
|
257
|
+
/**
|
|
258
|
+
* Throws an exception if the schema has *not* been initialized
|
|
259
|
+
* by `initSchema()`.
|
|
260
|
+
*
|
|
261
|
+
* **To be called before trying to access schema.**
|
|
251
262
|
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
263
|
+
* Currently this only needs to be called in `start()` and `clear()` because
|
|
264
|
+
* all other functions will call start first.
|
|
254
265
|
*/
|
|
255
266
|
const checkSchemaInitialized = () => {
|
|
256
267
|
if (schema === undefined) {
|
|
@@ -283,8 +294,22 @@ const createTypeClasses: (
|
|
|
283
294
|
return classes;
|
|
284
295
|
};
|
|
285
296
|
|
|
297
|
+
/**
|
|
298
|
+
* Constructs a model and records it with its metadata in a weakset. Allows for
|
|
299
|
+
* the separate storage of core model fields and Amplify/DataStore metadata
|
|
300
|
+
* fields that the customer app does not want exposed.
|
|
301
|
+
*
|
|
302
|
+
* @param modelConstructor The model constructor.
|
|
303
|
+
* @param init Init data that would normally be passed to the constructor.
|
|
304
|
+
* @returns The initialized model.
|
|
305
|
+
*/
|
|
286
306
|
export declare type ModelInstanceCreator = typeof modelInstanceCreator;
|
|
287
307
|
|
|
308
|
+
/**
|
|
309
|
+
* Collection of instantiated models to allow storage of metadata apart from
|
|
310
|
+
* the model visible to the consuming app -- in case the app doesn't have
|
|
311
|
+
* metadata fields (_version, _deleted, etc.) exposed on the model itself.
|
|
312
|
+
*/
|
|
288
313
|
const instancesMetadata = new WeakSet<ModelInit<unknown, unknown>>();
|
|
289
314
|
|
|
290
315
|
function modelInstanceCreator<T extends PersistentModel>(
|
|
@@ -729,6 +754,18 @@ function getModelConstructorByModelName(
|
|
|
729
754
|
}
|
|
730
755
|
}
|
|
731
756
|
|
|
757
|
+
/**
|
|
758
|
+
* Queries the DataStore metadata tables to see if they are the expected
|
|
759
|
+
* version. If not, clobbers the whole DB. If so, leaves them alone.
|
|
760
|
+
* Otherwise, simply writes the schema version.
|
|
761
|
+
*
|
|
762
|
+
* SIDE EFFECT:
|
|
763
|
+
* 1. Creates a transaction
|
|
764
|
+
* 1. Updates data.
|
|
765
|
+
*
|
|
766
|
+
* @param storage Storage adapter containing the metadata.
|
|
767
|
+
* @param version The expected schema version.
|
|
768
|
+
*/
|
|
732
769
|
async function checkSchemaVersion(
|
|
733
770
|
storage: Storage,
|
|
734
771
|
version: string
|
|
@@ -807,6 +844,14 @@ function getNamespace(): SchemaNamespace {
|
|
|
807
844
|
return namespace;
|
|
808
845
|
}
|
|
809
846
|
|
|
847
|
+
enum DataStoreState {
|
|
848
|
+
NotRunning = 'Not Running',
|
|
849
|
+
Starting = 'Starting',
|
|
850
|
+
Running = 'Running',
|
|
851
|
+
Stopping = 'Stopping',
|
|
852
|
+
Clearing = 'Clearing',
|
|
853
|
+
}
|
|
854
|
+
|
|
810
855
|
class DataStore {
|
|
811
856
|
// reference to configured category instances. Used for preserving SSR context
|
|
812
857
|
private Auth = Auth;
|
|
@@ -836,98 +881,188 @@ class DataStore {
|
|
|
836
881
|
API: this.API,
|
|
837
882
|
Cache: this.Cache,
|
|
838
883
|
};
|
|
884
|
+
private connectivityMonitor?: DataStoreConnectivity;
|
|
885
|
+
|
|
886
|
+
/**
|
|
887
|
+
* **IMPORTANT!**
|
|
888
|
+
*
|
|
889
|
+
* Accumulator for background things that can **and MUST** be called when
|
|
890
|
+
* DataStore stops.
|
|
891
|
+
*
|
|
892
|
+
* These jobs **MUST** be *idempotent promises* that resolve ONLY
|
|
893
|
+
* once the intended jobs are completely finished and/or otherwise destroyed
|
|
894
|
+
* and cleaned up with ZERO outstanding:
|
|
895
|
+
*
|
|
896
|
+
* 1. side effects (e.g., state changes)
|
|
897
|
+
* 1. callbacks
|
|
898
|
+
* 1. subscriptions
|
|
899
|
+
* 1. calls to storage
|
|
900
|
+
* 1. *etc.*
|
|
901
|
+
*
|
|
902
|
+
* Methods that create pending promises, subscriptions, callbacks, or any
|
|
903
|
+
* type of side effect **MUST** be registered with the manager. And, a new
|
|
904
|
+
* manager must be created after each `exit()`.
|
|
905
|
+
*
|
|
906
|
+
* Failure to comply will put DataStore into a highly unpredictable state
|
|
907
|
+
* when it needs to stop or clear -- which occurs when restarting with new
|
|
908
|
+
* sync expressions, during testing, and potentially during app code
|
|
909
|
+
* recovery handling, etc..
|
|
910
|
+
*
|
|
911
|
+
* It is up to the discretion of each disposer whether to wait for job
|
|
912
|
+
* completion or to cancel operations and issue failures *as long as the
|
|
913
|
+
* disposer returns in a reasonable amount of time.*
|
|
914
|
+
*
|
|
915
|
+
* (Reasonable = *seconds*, not minutes.)
|
|
916
|
+
*/
|
|
917
|
+
private runningProcesses = new BackgroundProcessManager();
|
|
918
|
+
|
|
919
|
+
/**
|
|
920
|
+
* Indicates what state DataStore is in.
|
|
921
|
+
*
|
|
922
|
+
* Not [yet?] used for actual state management; but for messaging
|
|
923
|
+
* when errors occur, to help troubleshoot.
|
|
924
|
+
*/
|
|
925
|
+
private state: DataStoreState = DataStoreState.NotRunning;
|
|
839
926
|
|
|
840
927
|
getModuleName() {
|
|
841
928
|
return 'DataStore';
|
|
842
929
|
}
|
|
843
930
|
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
931
|
+
/**
|
|
932
|
+
* Builds a function to capture `BackgroundManagerNotOpenError`'s to produce friendlier,
|
|
933
|
+
* more instructive errors for customers.
|
|
934
|
+
*
|
|
935
|
+
* @param operation The name of the operation (usually a Datastore method) the customer
|
|
936
|
+
* tried to call.
|
|
937
|
+
*/
|
|
938
|
+
handleAddProcError(operation: string) {
|
|
939
|
+
/**
|
|
940
|
+
* If the tested error is a `BackgroundManagerNotOpenError`, it will be captured
|
|
941
|
+
* and replaced with a friendlier message that instructs the App Developer.
|
|
942
|
+
*
|
|
943
|
+
* @param err An error to test.
|
|
944
|
+
*/
|
|
945
|
+
const handler = (err: Error) => {
|
|
946
|
+
if (err.message.startsWith('BackgroundManagerNotOpenError')) {
|
|
947
|
+
throw new Error(
|
|
948
|
+
[
|
|
949
|
+
`DataStoreStateError: Tried to execute \`${operation}\` while DataStore was "${this.state}".`,
|
|
950
|
+
`This can only be done while DataStore is "Started" or "Stopped". To remedy:`,
|
|
951
|
+
'Ensure all calls to `stop()` and `clear()` have completed first.',
|
|
952
|
+
'If this is not possible, retry the operation until it succeeds.',
|
|
953
|
+
].join('\n')
|
|
954
|
+
);
|
|
955
|
+
} else {
|
|
956
|
+
throw err;
|
|
957
|
+
}
|
|
958
|
+
};
|
|
853
959
|
|
|
854
|
-
|
|
855
|
-
|
|
960
|
+
return handler;
|
|
961
|
+
}
|
|
856
962
|
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
963
|
+
/**
|
|
964
|
+
* If not already done:
|
|
965
|
+
* 1. Attaches and initializes storage.
|
|
966
|
+
* 1. Loads the schema and records metadata.
|
|
967
|
+
* 1. If `this.amplifyConfig.aws_appsync_graphqlEndpoint` contains a URL,
|
|
968
|
+
* attaches a sync engine, starts it, and subscribes.
|
|
969
|
+
*/
|
|
970
|
+
start = async (): Promise<void> => {
|
|
971
|
+
return this.runningProcesses
|
|
972
|
+
.add(async () => {
|
|
973
|
+
this.state = DataStoreState.Starting;
|
|
974
|
+
if (this.initialized === undefined) {
|
|
975
|
+
logger.debug('Starting DataStore');
|
|
976
|
+
this.initialized = new Promise((res, rej) => {
|
|
977
|
+
this.initResolve = res;
|
|
978
|
+
this.initReject = rej;
|
|
979
|
+
});
|
|
980
|
+
} else {
|
|
981
|
+
await this.initialized;
|
|
982
|
+
return;
|
|
983
|
+
}
|
|
865
984
|
|
|
866
|
-
|
|
985
|
+
this.storage = new Storage(
|
|
986
|
+
schema,
|
|
987
|
+
namespaceResolver,
|
|
988
|
+
getModelConstructorByModelName,
|
|
989
|
+
modelInstanceCreator,
|
|
990
|
+
this.storageAdapter,
|
|
991
|
+
this.sessionId
|
|
992
|
+
);
|
|
867
993
|
|
|
868
|
-
|
|
869
|
-
await checkSchemaVersion(this.storage, schema.version);
|
|
994
|
+
await this.storage.init();
|
|
870
995
|
|
|
871
|
-
|
|
996
|
+
checkSchemaInitialized();
|
|
997
|
+
await checkSchemaVersion(this.storage, schema.version);
|
|
872
998
|
|
|
873
|
-
|
|
874
|
-
logger.debug('GraphQL endpoint available', aws_appsync_graphqlEndpoint);
|
|
999
|
+
const { aws_appsync_graphqlEndpoint } = this.amplifyConfig;
|
|
875
1000
|
|
|
876
|
-
|
|
1001
|
+
if (aws_appsync_graphqlEndpoint) {
|
|
1002
|
+
logger.debug(
|
|
1003
|
+
'GraphQL endpoint available',
|
|
1004
|
+
aws_appsync_graphqlEndpoint
|
|
1005
|
+
);
|
|
877
1006
|
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
1007
|
+
this.syncPredicates = await this.processSyncExpressions();
|
|
1008
|
+
|
|
1009
|
+
this.sync = new SyncEngine(
|
|
1010
|
+
schema,
|
|
1011
|
+
namespaceResolver,
|
|
1012
|
+
syncClasses,
|
|
1013
|
+
userClasses,
|
|
1014
|
+
this.storage,
|
|
1015
|
+
modelInstanceCreator,
|
|
1016
|
+
this.conflictHandler,
|
|
1017
|
+
this.errorHandler,
|
|
1018
|
+
this.syncPredicates,
|
|
1019
|
+
this.amplifyConfig,
|
|
1020
|
+
this.authModeStrategy,
|
|
1021
|
+
this.amplifyContext,
|
|
1022
|
+
this.connectivityMonitor
|
|
1023
|
+
);
|
|
892
1024
|
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
1025
|
+
const fullSyncIntervalInMilliseconds =
|
|
1026
|
+
this.fullSyncInterval * 1000 * 60; // fullSyncInterval from param is in minutes
|
|
1027
|
+
syncSubscription = this.sync
|
|
1028
|
+
.start({ fullSyncInterval: fullSyncIntervalInMilliseconds })
|
|
1029
|
+
.subscribe({
|
|
1030
|
+
next: ({ type, data }) => {
|
|
1031
|
+
// In Node, we need to wait for queries to be synced to prevent returning empty arrays.
|
|
1032
|
+
// In the Browser, we can begin returning data once subscriptions are in place.
|
|
1033
|
+
const readyType = isNode
|
|
1034
|
+
? ControlMessage.SYNC_ENGINE_SYNC_QUERIES_READY
|
|
1035
|
+
: ControlMessage.SYNC_ENGINE_STORAGE_SUBSCRIBED;
|
|
1036
|
+
|
|
1037
|
+
if (type === readyType) {
|
|
1038
|
+
this.initResolve();
|
|
1039
|
+
}
|
|
908
1040
|
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
1041
|
+
Hub.dispatch('datastore', {
|
|
1042
|
+
event: type,
|
|
1043
|
+
data,
|
|
1044
|
+
});
|
|
1045
|
+
},
|
|
1046
|
+
error: err => {
|
|
1047
|
+
logger.warn('Sync error', err);
|
|
1048
|
+
this.initReject();
|
|
1049
|
+
},
|
|
912
1050
|
});
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
logger.warn(
|
|
921
|
-
"Data won't be synchronized. No GraphQL endpoint configured. Did you forget `Amplify.configure(awsconfig)`?",
|
|
922
|
-
{
|
|
923
|
-
config: this.amplifyConfig,
|
|
924
|
-
}
|
|
925
|
-
);
|
|
1051
|
+
} else {
|
|
1052
|
+
logger.warn(
|
|
1053
|
+
"Data won't be synchronized. No GraphQL endpoint configured. Did you forget `Amplify.configure(awsconfig)`?",
|
|
1054
|
+
{
|
|
1055
|
+
config: this.amplifyConfig,
|
|
1056
|
+
}
|
|
1057
|
+
);
|
|
926
1058
|
|
|
927
|
-
|
|
928
|
-
|
|
1059
|
+
this.initResolve();
|
|
1060
|
+
}
|
|
929
1061
|
|
|
930
|
-
|
|
1062
|
+
await this.initialized;
|
|
1063
|
+
this.state = DataStoreState.Running;
|
|
1064
|
+
}, 'datastore start')
|
|
1065
|
+
.catch(this.handleAddProcError('DataStore.start()'));
|
|
931
1066
|
};
|
|
932
1067
|
|
|
933
1068
|
query: {
|
|
@@ -951,129 +1086,136 @@ class DataStore {
|
|
|
951
1086
|
| typeof PredicateAll,
|
|
952
1087
|
paginationProducer?: ProducerPaginationInput<T>
|
|
953
1088
|
): Promise<T | T[] | undefined> => {
|
|
954
|
-
|
|
1089
|
+
return this.runningProcesses
|
|
1090
|
+
.add(async () => {
|
|
1091
|
+
await this.start();
|
|
955
1092
|
|
|
956
|
-
|
|
1093
|
+
//#region Input validation
|
|
957
1094
|
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
1095
|
+
if (!isValidModelConstructor(modelConstructor)) {
|
|
1096
|
+
const msg = 'Constructor is not for a valid model';
|
|
1097
|
+
logger.error(msg, { modelConstructor });
|
|
961
1098
|
|
|
962
|
-
|
|
963
|
-
|
|
1099
|
+
throw new Error(msg);
|
|
1100
|
+
}
|
|
964
1101
|
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
1102
|
+
if (typeof identifierOrCriteria === 'string') {
|
|
1103
|
+
if (paginationProducer !== undefined) {
|
|
1104
|
+
logger.warn('Pagination is ignored when querying by id');
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
970
1107
|
|
|
971
|
-
|
|
972
|
-
|
|
1108
|
+
const modelDefinition = getModelDefinition(modelConstructor);
|
|
1109
|
+
const keyFields = extractPrimaryKeyFieldNames(modelDefinition);
|
|
973
1110
|
|
|
974
|
-
|
|
1111
|
+
let predicate: ModelPredicate<T>;
|
|
975
1112
|
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
1113
|
+
if (isQueryOne(identifierOrCriteria)) {
|
|
1114
|
+
if (keyFields.length > 1) {
|
|
1115
|
+
const msg = errorMessages.queryByPkWithCompositeKeyPresent;
|
|
1116
|
+
logger.error(msg, { keyFields });
|
|
980
1117
|
|
|
981
|
-
|
|
982
|
-
|
|
1118
|
+
throw new Error(msg);
|
|
1119
|
+
}
|
|
983
1120
|
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1121
|
+
predicate = ModelPredicateCreator.createForSingleField<T>(
|
|
1122
|
+
modelDefinition,
|
|
1123
|
+
keyFields[0],
|
|
1124
|
+
identifierOrCriteria
|
|
1125
|
+
);
|
|
1126
|
+
} else {
|
|
1127
|
+
// Object is being queried using object literal syntax
|
|
1128
|
+
if (isIdentifierObject(<T>identifierOrCriteria, modelDefinition)) {
|
|
1129
|
+
predicate = ModelPredicateCreator.createForPk<T>(
|
|
1130
|
+
modelDefinition,
|
|
1131
|
+
<T>identifierOrCriteria
|
|
1132
|
+
);
|
|
1133
|
+
} else if (isPredicatesAll(identifierOrCriteria)) {
|
|
1134
|
+
// Predicates.ALL means "all records", so no predicate (undefined)
|
|
1135
|
+
predicate = undefined;
|
|
1136
|
+
} else {
|
|
1137
|
+
predicate = ModelPredicateCreator.createFromExisting(
|
|
1138
|
+
modelDefinition,
|
|
1139
|
+
<any>identifierOrCriteria
|
|
1140
|
+
);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
const pagination = this.processPagination(
|
|
1001
1145
|
modelDefinition,
|
|
1002
|
-
|
|
1146
|
+
paginationProducer
|
|
1003
1147
|
);
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
const pagination = this.processPagination(
|
|
1008
|
-
modelDefinition,
|
|
1009
|
-
paginationProducer
|
|
1010
|
-
);
|
|
1011
1148
|
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1149
|
+
//#endregion
|
|
1150
|
+
|
|
1151
|
+
logger.debug('params ready', {
|
|
1152
|
+
modelConstructor,
|
|
1153
|
+
predicate: ModelPredicateCreator.getPredicates(predicate, false),
|
|
1154
|
+
pagination: {
|
|
1155
|
+
...pagination,
|
|
1156
|
+
sort: ModelSortPredicateCreator.getPredicates(
|
|
1157
|
+
pagination && pagination.sort,
|
|
1158
|
+
false
|
|
1159
|
+
),
|
|
1160
|
+
},
|
|
1161
|
+
});
|
|
1025
1162
|
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1163
|
+
const result = await this.storage.query(
|
|
1164
|
+
modelConstructor,
|
|
1165
|
+
predicate,
|
|
1166
|
+
pagination
|
|
1167
|
+
);
|
|
1031
1168
|
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1169
|
+
const returnOne =
|
|
1170
|
+
isQueryOne(identifierOrCriteria) ||
|
|
1171
|
+
isIdentifierObject(identifierOrCriteria, modelDefinition);
|
|
1035
1172
|
|
|
1036
|
-
|
|
1173
|
+
return returnOne ? result[0] : result;
|
|
1174
|
+
}, 'datastore query')
|
|
1175
|
+
.catch(this.handleAddProcError('DataStore.query()'));
|
|
1037
1176
|
};
|
|
1038
1177
|
|
|
1039
1178
|
save = async <T extends PersistentModel>(
|
|
1040
1179
|
model: T,
|
|
1041
1180
|
condition?: ProducerModelPredicate<T>
|
|
1042
1181
|
): Promise<T> => {
|
|
1043
|
-
|
|
1182
|
+
return this.runningProcesses
|
|
1183
|
+
.add(async () => {
|
|
1184
|
+
await this.start();
|
|
1044
1185
|
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1186
|
+
// Immer patches for constructing a correct update mutation input
|
|
1187
|
+
// Allows us to only include changed fields for updates
|
|
1188
|
+
const patchesTuple = modelPatchesMap.get(model);
|
|
1048
1189
|
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
: undefined;
|
|
1190
|
+
const modelConstructor: PersistentModelConstructor<T> | undefined =
|
|
1191
|
+
model ? <PersistentModelConstructor<T>>model.constructor : undefined;
|
|
1052
1192
|
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1193
|
+
if (!isValidModelConstructor(modelConstructor)) {
|
|
1194
|
+
const msg = 'Object is not an instance of a valid model';
|
|
1195
|
+
logger.error(msg, { model });
|
|
1056
1196
|
|
|
1057
|
-
|
|
1058
|
-
|
|
1197
|
+
throw new Error(msg);
|
|
1198
|
+
}
|
|
1059
1199
|
|
|
1060
|
-
|
|
1200
|
+
const modelDefinition = getModelDefinition(modelConstructor);
|
|
1061
1201
|
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1202
|
+
const producedCondition = ModelPredicateCreator.createFromExisting(
|
|
1203
|
+
modelDefinition,
|
|
1204
|
+
condition!
|
|
1205
|
+
);
|
|
1066
1206
|
|
|
1067
|
-
|
|
1068
|
-
|
|
1207
|
+
const [savedModel] = await this.storage.runExclusive(async s => {
|
|
1208
|
+
await s.save(model, producedCondition, undefined, patchesTuple);
|
|
1069
1209
|
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1210
|
+
return s.query<T>(
|
|
1211
|
+
modelConstructor,
|
|
1212
|
+
ModelPredicateCreator.createForPk(modelDefinition, model)
|
|
1213
|
+
);
|
|
1214
|
+
});
|
|
1075
1215
|
|
|
1076
|
-
|
|
1216
|
+
return savedModel;
|
|
1217
|
+
}, 'datastore save')
|
|
1218
|
+
.catch(this.handleAddProcError('DataStore.save()'));
|
|
1077
1219
|
};
|
|
1078
1220
|
|
|
1079
1221
|
setConflictHandler = (config: DataStoreConfig): ConflictHandler => {
|
|
@@ -1131,112 +1273,122 @@ class DataStore {
|
|
|
1131
1273
|
| ProducerModelPredicate<T>
|
|
1132
1274
|
| typeof PredicateAll
|
|
1133
1275
|
): Promise<T | T[]> => {
|
|
1134
|
-
|
|
1276
|
+
return this.runningProcesses
|
|
1277
|
+
.add(async () => {
|
|
1278
|
+
await this.start();
|
|
1135
1279
|
|
|
1136
|
-
|
|
1280
|
+
let condition: ModelPredicate<T>;
|
|
1137
1281
|
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1282
|
+
if (!modelOrConstructor) {
|
|
1283
|
+
const msg = 'Model or Model Constructor required';
|
|
1284
|
+
logger.error(msg, { modelOrConstructor });
|
|
1141
1285
|
|
|
1142
|
-
|
|
1143
|
-
|
|
1286
|
+
throw new Error(msg);
|
|
1287
|
+
}
|
|
1144
1288
|
|
|
1145
|
-
|
|
1146
|
-
|
|
1289
|
+
if (isValidModelConstructor<T>(modelOrConstructor)) {
|
|
1290
|
+
const modelConstructor = modelOrConstructor;
|
|
1147
1291
|
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1292
|
+
if (!identifierOrCriteria) {
|
|
1293
|
+
const msg =
|
|
1294
|
+
'Id to delete or criteria required. Do you want to delete all? Pass Predicates.ALL';
|
|
1295
|
+
logger.error(msg, { identifierOrCriteria });
|
|
1152
1296
|
|
|
1153
|
-
|
|
1154
|
-
|
|
1297
|
+
throw new Error(msg);
|
|
1298
|
+
}
|
|
1155
1299
|
|
|
1156
|
-
|
|
1300
|
+
const modelDefinition = getModelDefinition(modelConstructor);
|
|
1157
1301
|
|
|
1158
|
-
|
|
1159
|
-
|
|
1302
|
+
if (typeof identifierOrCriteria === 'string') {
|
|
1303
|
+
const keyFields = extractPrimaryKeyFieldNames(modelDefinition);
|
|
1160
1304
|
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1305
|
+
if (keyFields.length > 1) {
|
|
1306
|
+
const msg = errorMessages.deleteByPkWithCompositeKeyPresent;
|
|
1307
|
+
logger.error(msg, { keyFields });
|
|
1164
1308
|
|
|
1165
|
-
|
|
1166
|
-
|
|
1309
|
+
throw new Error(msg);
|
|
1310
|
+
}
|
|
1167
1311
|
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1312
|
+
condition = ModelPredicateCreator.createForSingleField<T>(
|
|
1313
|
+
getModelDefinition(modelConstructor),
|
|
1314
|
+
keyFields[0],
|
|
1315
|
+
identifierOrCriteria
|
|
1316
|
+
);
|
|
1317
|
+
} else {
|
|
1318
|
+
if (isIdentifierObject(identifierOrCriteria, modelDefinition)) {
|
|
1319
|
+
condition = ModelPredicateCreator.createForPk<T>(
|
|
1320
|
+
modelDefinition,
|
|
1321
|
+
<T>identifierOrCriteria
|
|
1322
|
+
);
|
|
1323
|
+
} else {
|
|
1324
|
+
condition = ModelPredicateCreator.createFromExisting(
|
|
1325
|
+
modelDefinition,
|
|
1326
|
+
/**
|
|
1327
|
+
* idOrCriteria is always a ProducerModelPredicate<T>, never a symbol.
|
|
1328
|
+
* The symbol is used only for typing purposes. e.g. see Predicates.ALL
|
|
1329
|
+
*/
|
|
1330
|
+
identifierOrCriteria as ProducerModelPredicate<T>
|
|
1331
|
+
);
|
|
1332
|
+
}
|
|
1189
1333
|
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1334
|
+
if (
|
|
1335
|
+
!condition ||
|
|
1336
|
+
!ModelPredicateCreator.isValidPredicate(condition)
|
|
1337
|
+
) {
|
|
1338
|
+
const msg =
|
|
1339
|
+
'Criteria required. Do you want to delete all? Pass Predicates.ALL';
|
|
1340
|
+
logger.error(msg, { condition });
|
|
1194
1341
|
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1342
|
+
throw new Error(msg);
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1198
1345
|
|
|
1199
|
-
|
|
1346
|
+
const [deleted] = await this.storage.delete(
|
|
1347
|
+
modelConstructor,
|
|
1348
|
+
condition
|
|
1349
|
+
);
|
|
1200
1350
|
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1351
|
+
return deleted;
|
|
1352
|
+
} else {
|
|
1353
|
+
const model = modelOrConstructor;
|
|
1354
|
+
const modelConstructor = Object.getPrototypeOf(model || {})
|
|
1355
|
+
.constructor as PersistentModelConstructor<T>;
|
|
1206
1356
|
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1357
|
+
if (!isValidModelConstructor(modelConstructor)) {
|
|
1358
|
+
const msg = 'Object is not an instance of a valid model';
|
|
1359
|
+
logger.error(msg, { model });
|
|
1210
1360
|
|
|
1211
|
-
|
|
1212
|
-
|
|
1361
|
+
throw new Error(msg);
|
|
1362
|
+
}
|
|
1213
1363
|
|
|
1214
|
-
|
|
1364
|
+
const modelDefinition = getModelDefinition(modelConstructor);
|
|
1215
1365
|
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1366
|
+
const pkPredicate = ModelPredicateCreator.createForPk<T>(
|
|
1367
|
+
modelDefinition,
|
|
1368
|
+
model
|
|
1369
|
+
);
|
|
1220
1370
|
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1371
|
+
if (identifierOrCriteria) {
|
|
1372
|
+
if (typeof identifierOrCriteria !== 'function') {
|
|
1373
|
+
const msg = 'Invalid criteria';
|
|
1374
|
+
logger.error(msg, { identifierOrCriteria });
|
|
1225
1375
|
|
|
1226
|
-
|
|
1227
|
-
|
|
1376
|
+
throw new Error(msg);
|
|
1377
|
+
}
|
|
1228
1378
|
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1379
|
+
condition = (<ProducerModelPredicate<T>>identifierOrCriteria)(
|
|
1380
|
+
pkPredicate
|
|
1381
|
+
);
|
|
1382
|
+
} else {
|
|
1383
|
+
condition = pkPredicate;
|
|
1384
|
+
}
|
|
1235
1385
|
|
|
1236
|
-
|
|
1386
|
+
const [[deleted]] = await this.storage.delete(model, condition);
|
|
1237
1387
|
|
|
1238
|
-
|
|
1239
|
-
|
|
1388
|
+
return deleted;
|
|
1389
|
+
}
|
|
1390
|
+
}, 'datastore delete')
|
|
1391
|
+
.catch(this.handleAddProcError('DataStore.delete()'));
|
|
1240
1392
|
};
|
|
1241
1393
|
|
|
1242
1394
|
observe: {
|
|
@@ -1343,53 +1495,64 @@ class DataStore {
|
|
|
1343
1495
|
return new Observable<SubscriptionMessage<T>>(observer => {
|
|
1344
1496
|
let handle: ZenObservable.Subscription;
|
|
1345
1497
|
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
.
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1498
|
+
this.runningProcesses
|
|
1499
|
+
.add(async () => {
|
|
1500
|
+
await this.start();
|
|
1501
|
+
|
|
1502
|
+
// Filter the events returned by Storage according to namespace,
|
|
1503
|
+
// append original element data, and subscribe to the observable
|
|
1504
|
+
handle = this.storage
|
|
1505
|
+
.observe(modelConstructor, predicate)
|
|
1506
|
+
.filter(({ model }) => namespaceResolver(model) === USER)
|
|
1507
|
+
.subscribe({
|
|
1508
|
+
next: item =>
|
|
1509
|
+
this.runningProcesses.isOpen &&
|
|
1510
|
+
this.runningProcesses.add(async () => {
|
|
1511
|
+
// the `element` doesn't necessarily contain all item details or
|
|
1512
|
+
// have related records attached consistently with that of a query()
|
|
1513
|
+
// result item. for consistency, we attach them here.
|
|
1514
|
+
|
|
1515
|
+
let message = item;
|
|
1516
|
+
|
|
1517
|
+
// as long as we're not dealing with a DELETE, we need to fetch a fresh
|
|
1518
|
+
// item from storage to ensure it's fully populated.
|
|
1519
|
+
if (item.opType !== 'DELETE') {
|
|
1520
|
+
const modelDefinition = getModelDefinition(item.model);
|
|
1521
|
+
const keyFields =
|
|
1522
|
+
extractPrimaryKeyFieldNames(modelDefinition);
|
|
1523
|
+
const primaryKeysAndValues = extractPrimaryKeysAndValues(
|
|
1524
|
+
item.element,
|
|
1525
|
+
keyFields
|
|
1526
|
+
);
|
|
1527
|
+
const freshElement = await this.query(
|
|
1528
|
+
item.model,
|
|
1529
|
+
primaryKeysAndValues
|
|
1530
|
+
);
|
|
1531
|
+
message = {
|
|
1532
|
+
...message,
|
|
1533
|
+
element: freshElement as T,
|
|
1534
|
+
};
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
observer.next(message as SubscriptionMessage<T>);
|
|
1538
|
+
}, 'datastore observe message handler'),
|
|
1539
|
+
error: err => observer.error(err),
|
|
1540
|
+
complete: () => observer.complete(),
|
|
1541
|
+
});
|
|
1542
|
+
}, 'datastore observe observable initialization')
|
|
1543
|
+
.catch(this.handleAddProcError('DataStore.observe()'))
|
|
1544
|
+
.catch(error => {
|
|
1545
|
+
observer.error(error);
|
|
1546
|
+
});
|
|
1387
1547
|
|
|
1388
|
-
|
|
1548
|
+
// better than no cleaner, but if the subscriber is handling the
|
|
1549
|
+
// complete() message async and not registering with the context,
|
|
1550
|
+
// this will still be problematic.
|
|
1551
|
+
return this.runningProcesses.addCleaner(async () => {
|
|
1389
1552
|
if (handle) {
|
|
1390
1553
|
handle.unsubscribe();
|
|
1391
1554
|
}
|
|
1392
|
-
};
|
|
1555
|
+
}, 'DataStore.observe() cleanup');
|
|
1393
1556
|
});
|
|
1394
1557
|
};
|
|
1395
1558
|
|
|
@@ -1462,72 +1625,79 @@ class DataStore {
|
|
|
1462
1625
|
ModelPredicateCreator.getPredicates(predicate, false) || {};
|
|
1463
1626
|
const hasPredicate = !!predicates;
|
|
1464
1627
|
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
const idOrPk = getIdentifierValue(itemModelDefinition, item);
|
|
1471
|
-
items.set(idOrPk, item);
|
|
1472
|
-
});
|
|
1473
|
-
|
|
1474
|
-
// Observe the model and send a stream of updates (debounced).
|
|
1475
|
-
// We need to post-filter results instead of passing criteria through
|
|
1476
|
-
// to have visibility into items that move from in-set to out-of-set.
|
|
1477
|
-
// We need to explicitly remove those items from the existing snapshot.
|
|
1478
|
-
handle = this.observe(model).subscribe(
|
|
1479
|
-
({ element, model, opType }) => {
|
|
1628
|
+
this.runningProcesses
|
|
1629
|
+
.add(async () => {
|
|
1630
|
+
try {
|
|
1631
|
+
// first, query and return any locally-available records
|
|
1632
|
+
(await this.query(model, criteria, sortOptions)).forEach(item => {
|
|
1480
1633
|
const itemModelDefinition = getModelDefinition(model);
|
|
1481
|
-
const idOrPk = getIdentifierValue(itemModelDefinition,
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1634
|
+
const idOrPk = getIdentifierValue(itemModelDefinition, item);
|
|
1635
|
+
items.set(idOrPk, item);
|
|
1636
|
+
});
|
|
1637
|
+
|
|
1638
|
+
// Observe the model and send a stream of updates (debounced).
|
|
1639
|
+
// We need to post-filter results instead of passing criteria through
|
|
1640
|
+
// to have visibility into items that move from in-set to out-of-set.
|
|
1641
|
+
// We need to explicitly remove those items from the existing snapshot.
|
|
1642
|
+
handle = this.observe(model).subscribe(
|
|
1643
|
+
({ element, model, opType }) => {
|
|
1644
|
+
const itemModelDefinition = getModelDefinition(model);
|
|
1645
|
+
const idOrPk = getIdentifierValue(itemModelDefinition, element);
|
|
1486
1646
|
if (
|
|
1487
|
-
|
|
1488
|
-
(
|
|
1647
|
+
hasPredicate &&
|
|
1648
|
+
!validatePredicate(element, predicateGroupType, predicates)
|
|
1489
1649
|
) {
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1650
|
+
if (
|
|
1651
|
+
opType === 'UPDATE' &&
|
|
1652
|
+
(items.has(idOrPk) || itemsChanged.has(idOrPk))
|
|
1653
|
+
) {
|
|
1654
|
+
// tracking as a "deleted item" will include the item in
|
|
1655
|
+
// page limit calculations and ensure it is removed from the
|
|
1656
|
+
// final items collection, regardless of which collection(s)
|
|
1657
|
+
// it is currently in. (I mean, it could be in both, right!?)
|
|
1658
|
+
deletedItemIds.push(idOrPk);
|
|
1659
|
+
} else {
|
|
1660
|
+
// ignore updates for irrelevant/filtered items.
|
|
1661
|
+
return;
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
// Flag items which have been recently deleted
|
|
1666
|
+
// NOTE: Merging of separate operations to the same model instance is handled upstream
|
|
1667
|
+
// in the `mergePage` method within src/sync/merger.ts. The final state of a model instance
|
|
1668
|
+
// depends on the LATEST record (for a given id).
|
|
1669
|
+
if (opType === 'DELETE') {
|
|
1494
1670
|
deletedItemIds.push(idOrPk);
|
|
1495
1671
|
} else {
|
|
1496
|
-
|
|
1497
|
-
return;
|
|
1672
|
+
itemsChanged.set(idOrPk, element);
|
|
1498
1673
|
}
|
|
1499
|
-
}
|
|
1500
1674
|
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
// in the `mergePage` method within src/sync/merger.ts. The final state of a model instance
|
|
1504
|
-
// depends on the LATEST record (for a given id).
|
|
1505
|
-
if (opType === 'DELETE') {
|
|
1506
|
-
deletedItemIds.push(idOrPk);
|
|
1507
|
-
} else {
|
|
1508
|
-
itemsChanged.set(idOrPk, element);
|
|
1509
|
-
}
|
|
1675
|
+
const isSynced =
|
|
1676
|
+
this.sync?.getModelSyncedStatus(model) ?? false;
|
|
1510
1677
|
|
|
1511
|
-
|
|
1678
|
+
const limit =
|
|
1679
|
+
itemsChanged.size - deletedItemIds.length >=
|
|
1680
|
+
this.syncPageSize;
|
|
1512
1681
|
|
|
1513
|
-
|
|
1514
|
-
|
|
1682
|
+
if (limit || isSynced) {
|
|
1683
|
+
limitTimerRace.resolve();
|
|
1684
|
+
}
|
|
1515
1685
|
|
|
1516
|
-
|
|
1517
|
-
limitTimerRace.
|
|
1686
|
+
// kicks off every subsequent race as results sync down
|
|
1687
|
+
limitTimerRace.start();
|
|
1518
1688
|
}
|
|
1689
|
+
);
|
|
1519
1690
|
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
observer.error(
|
|
1529
|
-
}
|
|
1530
|
-
})();
|
|
1691
|
+
// returns a set of initial/locally-available results
|
|
1692
|
+
generateAndEmitSnapshot();
|
|
1693
|
+
} catch (err) {
|
|
1694
|
+
observer.error(err);
|
|
1695
|
+
}
|
|
1696
|
+
}, 'datastore observequery startup')
|
|
1697
|
+
.catch(this.handleAddProcError('DataStore.observeQuery()'))
|
|
1698
|
+
.catch(error => {
|
|
1699
|
+
observer.error(error);
|
|
1700
|
+
});
|
|
1531
1701
|
|
|
1532
1702
|
/**
|
|
1533
1703
|
* Combines the `items`, `itemsChanged`, and `deletedItemIds` collections into
|
|
@@ -1571,7 +1741,8 @@ class DataStore {
|
|
|
1571
1741
|
* @param snapshot The generated items data to emit.
|
|
1572
1742
|
*/
|
|
1573
1743
|
const emitSnapshot = (snapshot: DataStoreSnapshot<T>): void => {
|
|
1574
|
-
// send the generated snapshot to the primary subscription
|
|
1744
|
+
// send the generated snapshot to the primary subscription.
|
|
1745
|
+
// NOTE: This observer's handler *could* be async ...
|
|
1575
1746
|
observer.next(snapshot);
|
|
1576
1747
|
|
|
1577
1748
|
// reset the changed items sets
|
|
@@ -1614,16 +1785,16 @@ class DataStore {
|
|
|
1614
1785
|
data?.model?.name === model.name
|
|
1615
1786
|
) {
|
|
1616
1787
|
generateAndEmitSnapshot();
|
|
1617
|
-
Hub.remove('
|
|
1788
|
+
Hub.remove('datastore', hubCallback);
|
|
1618
1789
|
}
|
|
1619
1790
|
};
|
|
1620
1791
|
Hub.listen('datastore', hubCallback);
|
|
1621
1792
|
|
|
1622
|
-
return () => {
|
|
1793
|
+
return this.runningProcesses.addCleaner(async () => {
|
|
1623
1794
|
if (handle) {
|
|
1624
1795
|
handle.unsubscribe();
|
|
1625
1796
|
}
|
|
1626
|
-
};
|
|
1797
|
+
}, 'datastore observequery cleaner');
|
|
1627
1798
|
});
|
|
1628
1799
|
};
|
|
1629
1800
|
|
|
@@ -1713,8 +1884,18 @@ class DataStore {
|
|
|
1713
1884
|
this.sessionId = this.retrieveSessionId()!;
|
|
1714
1885
|
};
|
|
1715
1886
|
|
|
1716
|
-
|
|
1887
|
+
/**
|
|
1888
|
+
* Clears all data from storage and removes all data, schema info, other
|
|
1889
|
+
* initialization details, and then stops DataStore.
|
|
1890
|
+
*
|
|
1891
|
+
* That said, reinitialization is required after clearing. This can be done
|
|
1892
|
+
* by explicitiliy calling `start()` or any method that implicitly starts
|
|
1893
|
+
* DataStore, such as `query()`, `save()`, or `delete()`.
|
|
1894
|
+
*/
|
|
1895
|
+
async clear() {
|
|
1717
1896
|
checkSchemaInitialized();
|
|
1897
|
+
this.state = DataStoreState.Clearing;
|
|
1898
|
+
await this.runningProcesses.close();
|
|
1718
1899
|
if (this.storage === undefined) {
|
|
1719
1900
|
// connect to storage so that it can be cleared without fully starting DataStore
|
|
1720
1901
|
this.storage = new Storage(
|
|
@@ -1732,35 +1913,53 @@ class DataStore {
|
|
|
1732
1913
|
syncSubscription.unsubscribe();
|
|
1733
1914
|
}
|
|
1734
1915
|
|
|
1735
|
-
await this.storage.clear();
|
|
1736
|
-
|
|
1737
1916
|
if (this.sync) {
|
|
1738
|
-
this.sync.
|
|
1917
|
+
await this.sync.stop();
|
|
1739
1918
|
}
|
|
1740
1919
|
|
|
1920
|
+
await this.storage!.clear();
|
|
1921
|
+
|
|
1741
1922
|
this.initialized = undefined; // Should re-initialize when start() is called.
|
|
1742
1923
|
this.storage = undefined;
|
|
1743
1924
|
this.sync = undefined;
|
|
1744
1925
|
this.syncPredicates = new WeakMap<SchemaModel, ModelPredicate<any>>();
|
|
1745
|
-
};
|
|
1746
1926
|
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1927
|
+
await this.runningProcesses.open();
|
|
1928
|
+
this.state = DataStoreState.NotRunning;
|
|
1929
|
+
}
|
|
1930
|
+
|
|
1931
|
+
/**
|
|
1932
|
+
* Stops all DataStore sync activities.
|
|
1933
|
+
*
|
|
1934
|
+
* TODO: "Waits for graceful termination of
|
|
1935
|
+
* running queries and terminates subscriptions."
|
|
1936
|
+
*/
|
|
1937
|
+
async stop(this: InstanceType<typeof DataStore>) {
|
|
1938
|
+
this.state = DataStoreState.Stopping;
|
|
1939
|
+
|
|
1940
|
+
await this.runningProcesses.close();
|
|
1751
1941
|
|
|
1752
1942
|
if (syncSubscription && !syncSubscription.closed) {
|
|
1753
1943
|
syncSubscription.unsubscribe();
|
|
1754
1944
|
}
|
|
1755
1945
|
|
|
1756
1946
|
if (this.sync) {
|
|
1757
|
-
this.sync.
|
|
1947
|
+
await this.sync.stop();
|
|
1758
1948
|
}
|
|
1759
1949
|
|
|
1760
1950
|
this.initialized = undefined; // Should re-initialize when start() is called.
|
|
1761
1951
|
this.sync = undefined;
|
|
1762
|
-
|
|
1952
|
+
await this.runningProcesses.open();
|
|
1953
|
+
this.state = DataStoreState.NotRunning;
|
|
1954
|
+
}
|
|
1763
1955
|
|
|
1956
|
+
/**
|
|
1957
|
+
* Validates given pagination input from a query and creates a pagination
|
|
1958
|
+
* argument for use against the storage layer.
|
|
1959
|
+
*
|
|
1960
|
+
* @param modelDefinition
|
|
1961
|
+
* @param paginationProducer
|
|
1962
|
+
*/
|
|
1764
1963
|
private processPagination<T extends PersistentModel>(
|
|
1765
1964
|
modelDefinition: SchemaModel,
|
|
1766
1965
|
paginationProducer: ProducerPaginationInput<T>
|
|
@@ -1810,6 +2009,10 @@ class DataStore {
|
|
|
1810
2009
|
};
|
|
1811
2010
|
}
|
|
1812
2011
|
|
|
2012
|
+
/**
|
|
2013
|
+
* Examines the configured `syncExpressions` and produces a WeakMap of
|
|
2014
|
+
* SchemaModel -> predicate to use during sync.
|
|
2015
|
+
*/
|
|
1813
2016
|
private async processSyncExpressions(): Promise<
|
|
1814
2017
|
WeakMap<SchemaModel, ModelPredicate<any>>
|
|
1815
2018
|
> {
|
|
@@ -1895,7 +2098,10 @@ class DataStore {
|
|
|
1895
2098
|
}, new WeakMap<SchemaModel, ModelPredicate<any>>());
|
|
1896
2099
|
}
|
|
1897
2100
|
|
|
1898
|
-
|
|
2101
|
+
/**
|
|
2102
|
+
* A session ID to allow CMS to open databases against multiple apps.
|
|
2103
|
+
* This session ID is only expected be set by AWS Amplify Studio.
|
|
2104
|
+
*/
|
|
1899
2105
|
private retrieveSessionId(): string | undefined {
|
|
1900
2106
|
try {
|
|
1901
2107
|
const sessionId = sessionStorage.getItem('datastoreSessionId');
|