@aws-amplify/datastore 4.0.12 → 4.0.13-push-notification-dryrun.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/lib/datastore/datastore.d.ts +29 -3
  2. package/lib/datastore/datastore.js +308 -147
  3. package/lib/datastore/datastore.js.map +1 -1
  4. package/lib/predicates/index.d.ts +77 -7
  5. package/lib/predicates/index.js +142 -122
  6. package/lib/predicates/index.js.map +1 -1
  7. package/lib/predicates/next.d.ts +51 -10
  8. package/lib/predicates/next.js +111 -91
  9. package/lib/predicates/next.js.map +1 -1
  10. package/lib/storage/adapter/AsyncStorageAdapter.d.ts +28 -30
  11. package/lib/storage/adapter/AsyncStorageAdapter.js +135 -532
  12. package/lib/storage/adapter/AsyncStorageAdapter.js.map +1 -1
  13. package/lib/storage/adapter/AsyncStorageDatabase.js.map +1 -1
  14. package/lib/storage/adapter/IndexedDBAdapter.d.ts +28 -29
  15. package/lib/storage/adapter/IndexedDBAdapter.js +490 -885
  16. package/lib/storage/adapter/IndexedDBAdapter.js.map +1 -1
  17. package/lib/storage/adapter/StorageAdapterBase.d.ts +134 -0
  18. package/lib/storage/adapter/StorageAdapterBase.js +439 -0
  19. package/lib/storage/adapter/StorageAdapterBase.js.map +1 -0
  20. package/lib/storage/relationship.d.ts +9 -0
  21. package/lib/storage/relationship.js +9 -0
  22. package/lib/storage/relationship.js.map +1 -1
  23. package/lib/storage/storage.d.ts +1 -1
  24. package/lib/storage/storage.js +4 -3
  25. package/lib/storage/storage.js.map +1 -1
  26. package/lib/sync/index.d.ts +15 -1
  27. package/lib/sync/index.js +80 -13
  28. package/lib/sync/index.js.map +1 -1
  29. package/lib/sync/outbox.js +14 -7
  30. package/lib/sync/outbox.js.map +1 -1
  31. package/lib/sync/processors/mutation.d.ts +10 -1
  32. package/lib/sync/processors/mutation.js +33 -12
  33. package/lib/sync/processors/mutation.js.map +1 -1
  34. package/lib/sync/processors/subscription.d.ts +7 -1
  35. package/lib/sync/processors/subscription.js +196 -135
  36. package/lib/sync/processors/subscription.js.map +1 -1
  37. package/lib/sync/processors/sync.d.ts +1 -1
  38. package/lib/sync/processors/sync.js.map +1 -1
  39. package/lib/sync/utils.d.ts +66 -2
  40. package/lib/sync/utils.js +264 -16
  41. package/lib/sync/utils.js.map +1 -1
  42. package/lib/types.d.ts +9 -1
  43. package/lib/types.js.map +1 -1
  44. package/lib/util.d.ts +16 -0
  45. package/lib/util.js +31 -2
  46. package/lib/util.js.map +1 -1
  47. package/lib-esm/datastore/datastore.d.ts +29 -3
  48. package/lib-esm/datastore/datastore.js +310 -149
  49. package/lib-esm/datastore/datastore.js.map +1 -1
  50. package/lib-esm/predicates/index.d.ts +77 -7
  51. package/lib-esm/predicates/index.js +143 -123
  52. package/lib-esm/predicates/index.js.map +1 -1
  53. package/lib-esm/predicates/next.d.ts +51 -10
  54. package/lib-esm/predicates/next.js +111 -91
  55. package/lib-esm/predicates/next.js.map +1 -1
  56. package/lib-esm/storage/adapter/AsyncStorageAdapter.d.ts +28 -30
  57. package/lib-esm/storage/adapter/AsyncStorageAdapter.js +138 -535
  58. package/lib-esm/storage/adapter/AsyncStorageAdapter.js.map +1 -1
  59. package/lib-esm/storage/adapter/AsyncStorageDatabase.js.map +1 -1
  60. package/lib-esm/storage/adapter/IndexedDBAdapter.d.ts +28 -29
  61. package/lib-esm/storage/adapter/IndexedDBAdapter.js +489 -884
  62. package/lib-esm/storage/adapter/IndexedDBAdapter.js.map +1 -1
  63. package/lib-esm/storage/adapter/StorageAdapterBase.d.ts +134 -0
  64. package/lib-esm/storage/adapter/StorageAdapterBase.js +437 -0
  65. package/lib-esm/storage/adapter/StorageAdapterBase.js.map +1 -0
  66. package/lib-esm/storage/relationship.d.ts +9 -0
  67. package/lib-esm/storage/relationship.js +9 -0
  68. package/lib-esm/storage/relationship.js.map +1 -1
  69. package/lib-esm/storage/storage.d.ts +1 -1
  70. package/lib-esm/storage/storage.js +4 -3
  71. package/lib-esm/storage/storage.js.map +1 -1
  72. package/lib-esm/sync/index.d.ts +15 -1
  73. package/lib-esm/sync/index.js +82 -15
  74. package/lib-esm/sync/index.js.map +1 -1
  75. package/lib-esm/sync/outbox.js +14 -7
  76. package/lib-esm/sync/outbox.js.map +1 -1
  77. package/lib-esm/sync/processors/mutation.d.ts +10 -1
  78. package/lib-esm/sync/processors/mutation.js +33 -12
  79. package/lib-esm/sync/processors/mutation.js.map +1 -1
  80. package/lib-esm/sync/processors/subscription.d.ts +7 -1
  81. package/lib-esm/sync/processors/subscription.js +197 -136
  82. package/lib-esm/sync/processors/subscription.js.map +1 -1
  83. package/lib-esm/sync/processors/sync.d.ts +1 -1
  84. package/lib-esm/sync/processors/sync.js.map +1 -1
  85. package/lib-esm/sync/utils.d.ts +66 -2
  86. package/lib-esm/sync/utils.js +261 -18
  87. package/lib-esm/sync/utils.js.map +1 -1
  88. package/lib-esm/types.d.ts +9 -1
  89. package/lib-esm/types.js.map +1 -1
  90. package/lib-esm/util.d.ts +16 -0
  91. package/lib-esm/util.js +32 -3
  92. package/lib-esm/util.js.map +1 -1
  93. package/package.json +12 -11
  94. package/src/datastore/datastore.ts +288 -159
  95. package/src/predicates/index.ts +145 -175
  96. package/src/predicates/next.ts +114 -81
  97. package/src/storage/adapter/AsyncStorageAdapter.ts +97 -563
  98. package/src/storage/adapter/AsyncStorageDatabase.ts +2 -2
  99. package/src/storage/adapter/IndexedDBAdapter.ts +318 -770
  100. package/src/storage/adapter/StorageAdapterBase.ts +545 -0
  101. package/src/storage/relationship.ts +9 -0
  102. package/src/storage/storage.ts +12 -9
  103. package/src/sync/index.ts +108 -20
  104. package/src/sync/outbox.ts +17 -11
  105. package/src/sync/processors/mutation.ts +35 -4
  106. package/src/sync/processors/subscription.ts +124 -10
  107. package/src/sync/processors/sync.ts +4 -1
  108. package/src/sync/utils.ts +285 -15
  109. package/src/types.ts +15 -2
  110. package/src/util.ts +40 -1
  111. package/CHANGELOG.md +0 -904
@@ -1,191 +1,129 @@
1
- import { ConsoleLogger as Logger } from '@aws-amplify/core';
2
1
  import AsyncStorageDatabase from './AsyncStorageDatabase';
3
- import { Adapter } from './index';
4
- import { ModelInstanceCreator } from '../../datastore/datastore';
5
- import { ModelPredicateCreator } from '../../predicates';
6
2
  import {
7
- InternalSchema,
8
- isPredicateObj,
9
3
  ModelInstanceMetadata,
10
4
  ModelPredicate,
11
- NamespaceResolver,
12
5
  OpType,
13
6
  PaginationInput,
14
7
  PersistentModel,
15
8
  PersistentModelConstructor,
16
- PredicateObject,
17
9
  PredicatesGroup,
18
10
  QueryOne,
19
- RelationType,
20
11
  } from '../../types';
21
12
  import {
22
13
  DEFAULT_PRIMARY_KEY_VALUE_SEPARATOR,
23
- getIndex,
24
- getIndexFromAssociation,
25
- isModelConstructor,
26
14
  traverseModel,
27
15
  validatePredicate,
28
16
  inMemoryPagination,
29
- NAMESPACES,
30
17
  keysEqual,
31
18
  getStorename,
32
19
  getIndexKeys,
33
- extractPrimaryKeyValues,
34
- IDENTIFIER_KEY_SEPARATOR,
35
20
  } from '../../util';
21
+ import { StorageAdapterBase } from './StorageAdapterBase';
36
22
 
37
- const logger = new Logger('DataStore');
38
-
39
- export class AsyncStorageAdapter implements Adapter {
40
- // Non-null assertions (bang operators) added to most properties to make TS happy.
41
- // For now, we can be reasonably sure they're available when they're needed, because
42
- // the adapter is not used directly outside the library boundary.
43
- // TODO: rejigger for DI?
44
- private schema!: InternalSchema;
45
- private namespaceResolver!: NamespaceResolver;
46
- private modelInstanceCreator!: ModelInstanceCreator;
47
- private getModelConstructorByModelName!: (
48
- namsespaceName: NAMESPACES,
49
- modelName: string
50
- ) => PersistentModelConstructor<any>;
51
- private db!: AsyncStorageDatabase;
52
- private initPromise!: Promise<void>;
53
- private resolve!: (value?: any) => void;
54
- private reject!: (value?: any) => void;
55
-
56
- private getStorenameForModel(
57
- modelConstructor: PersistentModelConstructor<any>
58
- ) {
59
- const namespace = this.namespaceResolver(modelConstructor);
60
- const { name: modelName } = modelConstructor;
23
+ export class AsyncStorageAdapter extends StorageAdapterBase {
24
+ protected db!: AsyncStorageDatabase;
25
+
26
+ // no-ops for this adapter
27
+ protected async preSetUpChecks() {}
28
+ protected async preOpCheck() {}
29
+
30
+ /**
31
+ * Open AsyncStorage database
32
+ * Create new DB if one doesn't exist
33
+ *
34
+ * Called by `StorageAdapterBase.setUp()`
35
+ *
36
+ * @returns AsyncStorageDatabase instance
37
+ */
38
+ protected async initDb(): Promise<AsyncStorageDatabase> {
39
+ const db = new AsyncStorageDatabase();
40
+ await db.init();
41
+ return db;
42
+ }
61
43
 
62
- return getStorename(namespace, modelName);
44
+ async clear(): Promise<void> {
45
+ await this.db.clear();
46
+
47
+ this.db = undefined!;
48
+ this.initPromise = undefined!;
63
49
  }
64
50
 
65
- // Retrieves primary key values from a model
66
- private getIndexKeyValuesFromModel<T extends PersistentModel>(
67
- model: T
68
- ): string[] {
69
- const modelConstructor = Object.getPrototypeOf(model)
70
- .constructor as PersistentModelConstructor<T>;
51
+ async batchSave<T extends PersistentModel>(
52
+ modelConstructor: PersistentModelConstructor<any>,
53
+ items: ModelInstanceMetadata[]
54
+ ): Promise<[T, OpType][]> {
55
+ if (items.length === 0) {
56
+ return [];
57
+ }
58
+
59
+ const modelName = modelConstructor.name;
71
60
  const namespaceName = this.namespaceResolver(modelConstructor);
72
- const keys = getIndexKeys(
73
- this.schema.namespaces[namespaceName],
74
- modelConstructor.name
75
- );
61
+ const storeName = getStorename(namespaceName, modelName);
62
+ const keys = getIndexKeys(this.schema.namespaces[namespaceName], modelName);
63
+ const batch: ModelInstanceMetadata[] = [];
64
+
65
+ for (const item of items) {
66
+ const model = this.modelInstanceCreator(modelConstructor, item);
67
+
68
+ const connectedModels = traverseModel(
69
+ modelName,
70
+ model,
71
+ this.schema.namespaces[namespaceName],
72
+ this.modelInstanceCreator,
73
+ this.getModelConstructorByModelName
74
+ );
75
+
76
+ const keyValuesPath = this.getIndexKeyValuesPath(model);
77
+
78
+ const { instance } = connectedModels.find(({ instance }) => {
79
+ const instanceKeyValuesPath = this.getIndexKeyValuesPath(instance);
80
+ return keysEqual([instanceKeyValuesPath], [keyValuesPath]);
81
+ })!;
76
82
 
77
- return extractPrimaryKeyValues(model, keys);
83
+ batch.push(instance);
84
+ }
85
+
86
+ return await this.db.batchSave(storeName, batch, keys);
78
87
  }
79
88
 
80
- // Retrieves concatenated primary key values from a model
81
- private getIndexKeyValuesPath<T extends PersistentModel>(model: T): string {
82
- return this.getIndexKeyValuesFromModel(model).join(
89
+ protected async _get<T>(storeName: string, keyArr: string[]): Promise<T> {
90
+ const itemKeyValuesPath: string = keyArr.join(
83
91
  DEFAULT_PRIMARY_KEY_VALUE_SEPARATOR
84
92
  );
85
- }
86
93
 
87
- async setUp(
88
- theSchema: InternalSchema,
89
- namespaceResolver: NamespaceResolver,
90
- modelInstanceCreator: ModelInstanceCreator,
91
- getModelConstructorByModelName: (
92
- namsespaceName: NAMESPACES,
93
- modelName: string
94
- ) => PersistentModelConstructor<any>
95
- ) {
96
- if (!this.initPromise) {
97
- this.initPromise = new Promise((res, rej) => {
98
- this.resolve = res;
99
- this.reject = rej;
100
- });
101
- } else {
102
- await this.initPromise;
103
- return;
104
- }
105
- this.schema = theSchema;
106
- this.namespaceResolver = namespaceResolver;
107
- this.modelInstanceCreator = modelInstanceCreator;
108
- this.getModelConstructorByModelName = getModelConstructorByModelName;
109
- try {
110
- if (!this.db) {
111
- this.db = new AsyncStorageDatabase();
112
- await this.db.init();
113
- this.resolve();
114
- }
115
- } catch (error) {
116
- this.reject(error);
117
- }
94
+ return <T>await this.db.get(itemKeyValuesPath, storeName);
118
95
  }
119
96
 
120
97
  async save<T extends PersistentModel>(
121
98
  model: T,
122
99
  condition?: ModelPredicate<T>
123
100
  ): Promise<[T, OpType.INSERT | OpType.UPDATE][]> {
124
- const modelConstructor = Object.getPrototypeOf(model)
125
- .constructor as PersistentModelConstructor<T>;
126
- const storeName = this.getStorenameForModel(modelConstructor);
127
-
128
- const namespaceName = this.namespaceResolver(modelConstructor);
129
-
130
- const connectedModels = traverseModel(
131
- modelConstructor.name,
132
- model,
133
- this.schema.namespaces[namespaceName],
134
- this.modelInstanceCreator,
135
- this.getModelConstructorByModelName as any
136
- );
101
+ const { storeName, connectionStoreNames, modelKeyValues } =
102
+ this.saveMetadata(model);
137
103
 
138
- const set = new Set<string>();
139
- const connectionStoreNames = Object.values(connectedModels).map(
140
- ({ modelName, item, instance }) => {
141
- const storeName = getStorename(namespaceName, modelName);
142
- set.add(storeName);
143
- const keys = getIndexKeys(
144
- this.schema.namespaces[namespaceName],
145
- modelName
146
- );
147
- return { storeName, item, instance, keys };
148
- }
149
- );
150
- const keyValuesPath = this.getIndexKeyValuesPath(model);
151
-
152
- const fromDB = await this.db.get(keyValuesPath, storeName);
153
-
154
- if (condition && fromDB) {
155
- const predicates = ModelPredicateCreator.getPredicates(condition);
156
- const { predicates: predicateObjs, type } = predicates!;
157
-
158
- const isValid = validatePredicate(fromDB, type, predicateObjs);
104
+ const fromDB = await this._get(storeName, modelKeyValues);
159
105
 
160
- if (!isValid) {
161
- const msg = 'Conditional update failed';
162
- logger.error(msg, { model: fromDB, condition: predicateObjs });
163
-
164
- throw new Error(msg);
165
- }
166
- }
106
+ this.validateSaveCondition(condition, fromDB);
167
107
 
168
108
  const result: [T, OpType.INSERT | OpType.UPDATE][] = [];
169
-
170
109
  for await (const resItem of connectionStoreNames) {
171
110
  const { storeName, item, instance, keys } = resItem;
172
111
 
173
- /* Find the key values in the item, and concatenate them */
174
112
  const itemKeyValues: string[] = keys.map(key => item[key]);
175
- const itemKeyValuesPath: string = itemKeyValues.join(
176
- DEFAULT_PRIMARY_KEY_VALUE_SEPARATOR
177
- );
178
113
 
179
- const fromDB = <T>await this.db.get(itemKeyValuesPath, storeName);
114
+ const fromDB = <T>await this._get(storeName, itemKeyValues);
180
115
  const opType: OpType = fromDB ? OpType.UPDATE : OpType.INSERT;
181
- const modelKeyValues = this.getIndexKeyValuesFromModel(model);
182
116
 
183
- // If item key values and model key values are equal, save to db
184
117
  if (
185
118
  keysEqual(itemKeyValues, modelKeyValues) ||
186
119
  opType === OpType.INSERT
187
120
  ) {
188
- await this.db.save(item, storeName, keys, itemKeyValuesPath);
121
+ await this.db.save(
122
+ item,
123
+ storeName,
124
+ keys,
125
+ itemKeyValues.join(DEFAULT_PRIMARY_KEY_VALUE_SEPARATOR)
126
+ );
189
127
 
190
128
  result.push([instance, opType]);
191
129
  }
@@ -193,57 +131,24 @@ export class AsyncStorageAdapter implements Adapter {
193
131
  return result;
194
132
  }
195
133
 
196
- private async load<T>(
197
- namespaceName: NAMESPACES,
198
- srcModelName: string,
199
- records: T[]
200
- ): Promise<T[]> {
201
- const namespace = this.schema.namespaces[namespaceName];
202
- const relations = namespace.relationships![srcModelName].relationTypes;
203
- const connectionStoreNames = relations.map(({ modelName }) => {
204
- return getStorename(namespaceName, modelName);
205
- });
206
- const modelConstructor = this.getModelConstructorByModelName(
207
- namespaceName,
208
- srcModelName
209
- );
210
-
211
- if (connectionStoreNames.length === 0) {
212
- return records.map(record =>
213
- this.modelInstanceCreator(modelConstructor, record)
214
- );
215
- }
216
-
217
- return records.map(record =>
218
- this.modelInstanceCreator(modelConstructor, record)
219
- );
220
- }
221
-
222
134
  async query<T extends PersistentModel>(
223
135
  modelConstructor: PersistentModelConstructor<T>,
224
136
  predicate?: ModelPredicate<T>,
225
137
  pagination?: PaginationInput<T>
226
138
  ): Promise<T[]> {
227
- const storeName = this.getStorenameForModel(modelConstructor);
228
- const namespaceName = this.namespaceResolver(
229
- modelConstructor
230
- ) as NAMESPACES;
231
-
232
- const predicates =
233
- predicate && ModelPredicateCreator.getPredicates(predicate);
234
- const keys = getIndexKeys(
235
- this.schema.namespaces[namespaceName],
236
- modelConstructor.name
237
- );
238
- const queryByKey =
239
- predicates && this.keyValueFromPredicate(predicates, keys);
240
-
241
- const hasSort = pagination && pagination.sort;
242
- const hasPagination = pagination && pagination.limit;
139
+ const {
140
+ storeName,
141
+ namespaceName,
142
+ queryByKey,
143
+ predicates,
144
+ hasSort,
145
+ hasPagination,
146
+ } = this.queryMetadata(modelConstructor, predicate, pagination);
243
147
 
244
148
  const records: T[] = (await (async () => {
245
149
  if (queryByKey) {
246
- const record = await this.getByKey(storeName, queryByKey);
150
+ const keyValues = queryByKey.join(DEFAULT_PRIMARY_KEY_VALUE_SEPARATOR);
151
+ const record = await this.getByKey(storeName, keyValues);
247
152
  return record ? [record] : [];
248
153
  }
249
154
 
@@ -267,8 +172,7 @@ export class AsyncStorageAdapter implements Adapter {
267
172
  storeName: string,
268
173
  keyValuePath: string
269
174
  ): Promise<T> {
270
- const record = <T>await this.db.get(keyValuePath, storeName);
271
- return record;
175
+ return <T>await this.db.get(keyValuePath, storeName);
272
176
  }
273
177
 
274
178
  private async getAll<T extends PersistentModel>(
@@ -277,31 +181,6 @@ export class AsyncStorageAdapter implements Adapter {
277
181
  return await this.db.getAll(storeName);
278
182
  }
279
183
 
280
- private keyValueFromPredicate<T extends PersistentModel>(
281
- predicates: PredicatesGroup<T>,
282
- keys: string[]
283
- ): string | undefined {
284
- const { predicates: predicateObjs } = predicates;
285
-
286
- if (predicateObjs.length !== keys.length) {
287
- return;
288
- }
289
-
290
- const keyValues = [] as any[];
291
-
292
- for (const key of keys) {
293
- const predicateObj = predicateObjs.find(
294
- p => isPredicateObj(p) && p.field === key && p.operator === 'eq'
295
- ) as PredicateObject<T>;
296
-
297
- predicateObj && keyValues.push(predicateObj.operand);
298
- }
299
-
300
- return keyValues.length === keys.length
301
- ? keyValues.join(DEFAULT_PRIMARY_KEY_VALUE_SEPARATOR)
302
- : undefined;
303
- }
304
-
305
184
  private async filterOnPredicate<T extends PersistentModel>(
306
185
  storeName: string,
307
186
  predicates: PredicatesGroup<T>
@@ -334,129 +213,7 @@ export class AsyncStorageAdapter implements Adapter {
334
213
  return result && this.modelInstanceCreator(modelConstructor, result);
335
214
  }
336
215
 
337
- async delete<T extends PersistentModel>(
338
- modelOrModelConstructor: T | PersistentModelConstructor<T>,
339
- condition?: ModelPredicate<T>
340
- ): Promise<[T[], T[]]> {
341
- const deleteQueue: { storeName: string; items: T[] }[] = [];
342
-
343
- if (isModelConstructor(modelOrModelConstructor)) {
344
- const modelConstructor =
345
- modelOrModelConstructor as PersistentModelConstructor<T>;
346
- const nameSpace = this.namespaceResolver(modelConstructor) as NAMESPACES;
347
-
348
- // models to be deleted.
349
- const models = await this.query(modelConstructor, condition!);
350
- // TODO: refactor this to use a function like getRelations()
351
- const relations =
352
- this.schema.namespaces[nameSpace].relationships![modelConstructor.name]
353
- .relationTypes;
354
-
355
- if (condition !== undefined) {
356
- await this.deleteTraverse(
357
- relations,
358
- models,
359
- modelConstructor.name,
360
- nameSpace,
361
- deleteQueue
362
- );
363
-
364
- await this.deleteItem(deleteQueue);
365
-
366
- const deletedModels = deleteQueue.reduce(
367
- (acc, { items }) => acc.concat(items),
368
- <T[]>[]
369
- );
370
-
371
- return [models, deletedModels];
372
- } else {
373
- await this.deleteTraverse(
374
- relations,
375
- models,
376
- modelConstructor.name,
377
- nameSpace,
378
- deleteQueue
379
- );
380
-
381
- await this.deleteItem(deleteQueue);
382
-
383
- const deletedModels = deleteQueue.reduce(
384
- (acc, { items }) => acc.concat(items),
385
- <T[]>[]
386
- );
387
-
388
- return [models, deletedModels];
389
- }
390
- } else {
391
- const model = modelOrModelConstructor as T;
392
-
393
- const modelConstructor = Object.getPrototypeOf(model)
394
- .constructor as PersistentModelConstructor<T>;
395
- const nameSpace = this.namespaceResolver(modelConstructor) as NAMESPACES;
396
-
397
- const storeName = this.getStorenameForModel(modelConstructor);
398
-
399
- if (condition) {
400
- const keyValuePath = this.getIndexKeyValuesPath(model);
401
-
402
- const fromDB = await this.db.get(keyValuePath, storeName);
403
-
404
- if (fromDB === undefined) {
405
- const msg = 'Model instance not found in storage';
406
- logger.warn(msg, { model });
407
-
408
- return [[model], []];
409
- }
410
-
411
- const predicates = ModelPredicateCreator.getPredicates(condition);
412
- const { predicates: predicateObjs, type } = predicates!;
413
-
414
- const isValid = validatePredicate(fromDB, type, predicateObjs);
415
- if (!isValid) {
416
- const msg = 'Conditional update failed';
417
- logger.error(msg, { model: fromDB, condition: predicateObjs });
418
-
419
- throw new Error(msg);
420
- }
421
-
422
- const relations =
423
- this.schema.namespaces[nameSpace].relationships![
424
- modelConstructor.name
425
- ].relationTypes;
426
- await this.deleteTraverse(
427
- relations,
428
- [model],
429
- modelConstructor.name,
430
- nameSpace,
431
- deleteQueue
432
- );
433
- } else {
434
- const relations =
435
- this.schema.namespaces[nameSpace].relationships![
436
- modelConstructor.name
437
- ].relationTypes;
438
-
439
- await this.deleteTraverse(
440
- relations,
441
- [model],
442
- modelConstructor.name,
443
- nameSpace,
444
- deleteQueue
445
- );
446
- }
447
-
448
- await this.deleteItem(deleteQueue);
449
-
450
- const deletedModels = deleteQueue.reduce(
451
- (acc, { items }) => acc.concat(items),
452
- <T[]>[]
453
- );
454
-
455
- return [[model], deletedModels];
456
- }
457
- }
458
-
459
- private async deleteItem<T extends PersistentModel>(
216
+ protected async deleteItem<T extends PersistentModel>(
460
217
  deleteQueue?: { storeName: string; items: T[] | IDBValidKey[] }[]
461
218
  ) {
462
219
  for await (const deleteItem of deleteQueue!) {
@@ -473,244 +230,21 @@ export class AsyncStorageAdapter implements Adapter {
473
230
  }
474
231
  }
475
232
 
233
+ //#region platform-specific helper methods
234
+
476
235
  /**
477
- * Populates the delete Queue with all the items to delete
478
- * @param relations
479
- * @param models
480
- * @param srcModel
481
- * @param nameSpace
482
- * @param deleteQueue
236
+ * Retrieves concatenated primary key values from a model
237
+ *
238
+ * @param model
239
+ * @returns
483
240
  */
484
- private async deleteTraverse<T extends PersistentModel>(
485
- relations: RelationType[],
486
- models: T[],
487
- srcModel: string,
488
- nameSpace: NAMESPACES,
489
- deleteQueue: { storeName: string; items: T[] }[]
490
- ): Promise<void> {
491
- for await (const rel of relations) {
492
- const {
493
- relationType,
494
- modelName,
495
- targetName,
496
- targetNames,
497
- associatedWith,
498
- } = rel;
499
- const storeName = getStorename(nameSpace, modelName);
500
-
501
- const index: string | undefined =
502
- getIndex(
503
- this.schema.namespaces[nameSpace].relationships![modelName]
504
- .relationTypes,
505
- srcModel
506
- ) ||
507
- // if we were unable to find an index via relationTypes
508
- // i.e. for keyName connections, attempt to find one by the
509
- // associatedWith property
510
- getIndexFromAssociation(
511
- this.schema.namespaces[nameSpace].relationships![modelName].indexes,
512
- rel.associatedWith!
513
- );
514
-
515
- switch (relationType) {
516
- case 'HAS_ONE':
517
- for await (const model of models) {
518
- if (targetNames && targetNames?.length) {
519
- let hasOneIndex;
520
-
521
- if (index) {
522
- hasOneIndex = index.split(IDENTIFIER_KEY_SEPARATOR);
523
- } else if (associatedWith) {
524
- if (Array.isArray(associatedWith)) {
525
- hasOneIndex = associatedWith;
526
- } else {
527
- hasOneIndex = [associatedWith];
528
- }
529
- }
530
-
531
- // iterate over targetNames array and see if each key is present in model object
532
- // targetNames here being the keys for the CHILD model
533
- const hasConnectedModelFields = targetNames.every(targetName =>
534
- model.hasOwnProperty(targetName)
535
- );
536
-
537
- // PK / Composite key for the parent model
538
- const keyValuesPath: string = this.getIndexKeyValuesPath(model);
539
-
540
- let values;
541
-
542
- const isUnidirectionalConnection = hasOneIndex === associatedWith;
543
-
544
- if (hasConnectedModelFields && isUnidirectionalConnection) {
545
- // Values will be that of the child model
546
- values = targetNames
547
- .filter(targetName => model[targetName] ?? false)
548
- .map(targetName => model[targetName]) as any;
549
- } else {
550
- // values will be that of the parent model
551
- values = keyValuesPath.split(
552
- DEFAULT_PRIMARY_KEY_VALUE_SEPARATOR
553
- );
554
- }
555
-
556
- if (values.length === 0) break;
557
-
558
- const allRecords = await this.db.getAll(storeName);
559
-
560
- let recordToDelete;
561
-
562
- // values === targetNames
563
- if (hasConnectedModelFields) {
564
- /**
565
- * Retrieve record by finding the record where all
566
- * targetNames are present on the connected model.
567
- *
568
- */
569
- // recordToDelete = allRecords.filter(childItem =>
570
- // values.every(value => childItem[value] != null)
571
- // ) as T[];
572
-
573
- recordToDelete = allRecords.filter(childItem =>
574
- hasOneIndex.every(index => values.includes(childItem[index]))
575
- );
576
- } else {
577
- // values === keyValuePath
578
- recordToDelete = allRecords.filter(
579
- childItem => childItem[hasOneIndex] === values
580
- ) as T[];
581
- }
582
-
583
- await this.deleteTraverse<T>(
584
- this.schema.namespaces[nameSpace].relationships![modelName]
585
- .relationTypes,
586
- recordToDelete,
587
- modelName,
588
- nameSpace,
589
- deleteQueue
590
- );
591
- } else {
592
- const hasOneIndex = index || associatedWith;
593
- const hasOneCustomField = targetName! in model;
594
- const keyValuesPath: string = this.getIndexKeyValuesPath(model);
595
- const value = hasOneCustomField
596
- ? model[targetName!]
597
- : keyValuesPath;
598
-
599
- if (!value) break;
600
-
601
- const allRecords = await this.db.getAll(storeName);
602
-
603
- const recordsToDelete = allRecords.filter(
604
- childItem => childItem[hasOneIndex as string] === value
605
- ) as T[];
606
-
607
- // instantiate models before passing to deleteTraverse
608
- // necessary for extracting PK values via getIndexKeyValuesFromModel
609
- const modelsToDelete = recordsToDelete.length
610
- ? await this.load(nameSpace, modelName, recordsToDelete)
611
- : [];
612
-
613
- await this.deleteTraverse<T>(
614
- this.schema.namespaces[nameSpace].relationships![modelName]
615
- .relationTypes,
616
- modelsToDelete,
617
- modelName,
618
- nameSpace,
619
- deleteQueue
620
- );
621
- }
622
- }
623
- break;
624
- case 'HAS_MANY':
625
- for await (const model of models) {
626
- // Key values for the parent model:
627
- const keyValues: string[] = this.getIndexKeyValuesFromModel(model);
628
-
629
- const allRecords = await this.db.getAll(storeName);
630
-
631
- const indices = index!.split(IDENTIFIER_KEY_SEPARATOR);
632
-
633
- const childRecords = allRecords.filter(childItem =>
634
- indices.every(index => keyValues.includes(childItem[index]))
635
- ) as T[];
636
-
637
- // instantiate models before passing to deleteTraverse
638
- // necessary for extracting PK values via getIndexKeyValuesFromModel
639
- const childModels = await this.load(
640
- nameSpace,
641
- modelName,
642
- childRecords
643
- );
644
-
645
- await this.deleteTraverse<T>(
646
- this.schema.namespaces[nameSpace].relationships![modelName]
647
- .relationTypes,
648
- childModels,
649
- modelName,
650
- nameSpace,
651
- deleteQueue
652
- );
653
- }
654
- break;
655
- case 'BELONGS_TO':
656
- // Intentionally blank
657
- break;
658
- default:
659
- throw new Error(`Invalid relationType ${relationType}`);
660
- }
661
- }
662
-
663
- deleteQueue.push({
664
- storeName: getStorename(nameSpace, srcModel),
665
- items: models.map(record =>
666
- this.modelInstanceCreator(
667
- this.getModelConstructorByModelName(nameSpace, srcModel),
668
- record
669
- )
670
- ),
671
- });
672
- }
673
-
674
- async clear(): Promise<void> {
675
- await this.db.clear();
676
-
677
- this.db = undefined!;
678
- this.initPromise = undefined!;
241
+ private getIndexKeyValuesPath<T extends PersistentModel>(model: T): string {
242
+ return this.getIndexKeyValuesFromModel(model).join(
243
+ DEFAULT_PRIMARY_KEY_VALUE_SEPARATOR
244
+ );
679
245
  }
680
246
 
681
- async batchSave<T extends PersistentModel>(
682
- modelConstructor: PersistentModelConstructor<any>,
683
- items: ModelInstanceMetadata[]
684
- ): Promise<[T, OpType][]> {
685
- const { name: modelName } = modelConstructor;
686
- const namespaceName = this.namespaceResolver(modelConstructor);
687
- const storeName = getStorename(namespaceName, modelName);
688
- const keys = getIndexKeys(this.schema.namespaces[namespaceName], modelName);
689
- const batch: ModelInstanceMetadata[] = [];
690
-
691
- for (const item of items) {
692
- const model = this.modelInstanceCreator(modelConstructor, item);
693
-
694
- const connectedModels = traverseModel(
695
- modelName,
696
- model,
697
- this.schema.namespaces[namespaceName],
698
- this.modelInstanceCreator,
699
- this.getModelConstructorByModelName
700
- );
701
-
702
- const keyValuesPath = this.getIndexKeyValuesPath(model);
703
-
704
- const { instance } = connectedModels.find(({ instance }) => {
705
- const instanceKeyValuesPath = this.getIndexKeyValuesPath(instance);
706
- return keysEqual([instanceKeyValuesPath], [keyValuesPath]);
707
- })!;
708
-
709
- batch.push(instance);
710
- }
711
-
712
- return await this.db.batchSave(storeName, batch, keys);
713
- }
247
+ //#endregion
714
248
  }
715
249
 
716
250
  export default new AsyncStorageAdapter();