@aws-amplify/datastore 3.12.12 → 3.12.13-unstable.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/aws-amplify-datastore.js +1854 -965
- package/dist/aws-amplify-datastore.js.map +1 -1
- package/dist/aws-amplify-datastore.min.js +7 -7
- package/dist/aws-amplify-datastore.min.js.map +1 -1
- package/lib/datastore/datastore.d.ts +13 -16
- package/lib/datastore/datastore.js +130 -63
- package/lib/datastore/datastore.js.map +1 -1
- package/lib/index.d.ts +3 -19
- package/lib/predicates/index.d.ts +3 -2
- package/lib/predicates/index.js +12 -2
- package/lib/predicates/index.js.map +1 -1
- package/lib/storage/adapter/AsyncStorageAdapter.d.ts +4 -3
- package/lib/storage/adapter/AsyncStorageAdapter.js +354 -203
- package/lib/storage/adapter/AsyncStorageAdapter.js.map +1 -1
- package/lib/storage/adapter/AsyncStorageDatabase.d.ts +14 -4
- package/lib/storage/adapter/AsyncStorageDatabase.js +65 -28
- package/lib/storage/adapter/AsyncStorageDatabase.js.map +1 -1
- package/lib/storage/adapter/IndexedDBAdapter.d.ts +5 -4
- package/lib/storage/adapter/IndexedDBAdapter.js +389 -267
- package/lib/storage/adapter/IndexedDBAdapter.js.map +1 -1
- package/lib/storage/adapter/index.d.ts +1 -1
- package/lib/storage/storage.d.ts +1 -1
- package/lib/storage/storage.js +92 -27
- package/lib/storage/storage.js.map +1 -1
- package/lib/sync/index.d.ts +21 -4
- package/lib/sync/index.js +13 -11
- package/lib/sync/index.js.map +1 -1
- package/lib/sync/merger.d.ts +3 -3
- package/lib/sync/merger.js +7 -6
- package/lib/sync/merger.js.map +1 -1
- package/lib/sync/outbox.d.ts +2 -2
- package/lib/sync/outbox.js +11 -9
- package/lib/sync/outbox.js.map +1 -1
- package/lib/sync/processors/mutation.js +60 -42
- package/lib/sync/processors/mutation.js.map +1 -1
- package/lib/sync/processors/subscription.js.map +1 -1
- package/lib/sync/processors/sync.js.map +1 -1
- package/lib/sync/utils.d.ts +3 -2
- package/lib/sync/utils.js +61 -8
- package/lib/sync/utils.js.map +1 -1
- package/lib/types.d.ts +64 -25
- package/lib/types.js +10 -1
- package/lib/types.js.map +1 -1
- package/lib/util.d.ts +56 -24
- package/lib/util.js +334 -170
- package/lib/util.js.map +1 -1
- package/lib-esm/datastore/datastore.d.ts +13 -16
- package/lib-esm/datastore/datastore.js +132 -65
- package/lib-esm/datastore/datastore.js.map +1 -1
- package/lib-esm/index.d.ts +3 -19
- package/lib-esm/predicates/index.d.ts +3 -2
- package/lib-esm/predicates/index.js +13 -3
- package/lib-esm/predicates/index.js.map +1 -1
- package/lib-esm/storage/adapter/AsyncStorageAdapter.d.ts +4 -3
- package/lib-esm/storage/adapter/AsyncStorageAdapter.js +355 -204
- package/lib-esm/storage/adapter/AsyncStorageAdapter.js.map +1 -1
- package/lib-esm/storage/adapter/AsyncStorageDatabase.d.ts +14 -4
- package/lib-esm/storage/adapter/AsyncStorageDatabase.js +66 -29
- package/lib-esm/storage/adapter/AsyncStorageDatabase.js.map +1 -1
- package/lib-esm/storage/adapter/IndexedDBAdapter.d.ts +5 -4
- package/lib-esm/storage/adapter/IndexedDBAdapter.js +390 -268
- package/lib-esm/storage/adapter/IndexedDBAdapter.js.map +1 -1
- package/lib-esm/storage/adapter/index.d.ts +1 -1
- package/lib-esm/storage/storage.d.ts +1 -1
- package/lib-esm/storage/storage.js +92 -27
- package/lib-esm/storage/storage.js.map +1 -1
- package/lib-esm/sync/index.d.ts +21 -4
- package/lib-esm/sync/index.js +15 -13
- package/lib-esm/sync/index.js.map +1 -1
- package/lib-esm/sync/merger.d.ts +3 -3
- package/lib-esm/sync/merger.js +7 -6
- package/lib-esm/sync/merger.js.map +1 -1
- package/lib-esm/sync/outbox.d.ts +2 -2
- package/lib-esm/sync/outbox.js +12 -10
- package/lib-esm/sync/outbox.js.map +1 -1
- package/lib-esm/sync/processors/mutation.js +61 -43
- package/lib-esm/sync/processors/mutation.js.map +1 -1
- package/lib-esm/sync/processors/subscription.js.map +1 -1
- package/lib-esm/sync/processors/sync.js.map +1 -1
- package/lib-esm/sync/utils.d.ts +3 -2
- package/lib-esm/sync/utils.js +62 -10
- package/lib-esm/sync/utils.js.map +1 -1
- package/lib-esm/types.d.ts +64 -25
- package/lib-esm/types.js +9 -2
- package/lib-esm/types.js.map +1 -1
- package/lib-esm/util.d.ts +56 -24
- package/lib-esm/util.js +334 -170
- package/lib-esm/util.js.map +1 -1
- package/package.json +7 -7
- package/src/datastore/datastore.ts +253 -113
- package/src/predicates/index.ts +32 -10
- package/src/storage/adapter/AsyncStorageAdapter.ts +309 -93
- package/src/storage/adapter/AsyncStorageDatabase.ts +74 -26
- package/src/storage/adapter/IndexedDBAdapter.ts +319 -136
- package/src/storage/adapter/index.ts +1 -1
- package/src/storage/storage.ts +68 -21
- package/src/sync/index.ts +41 -26
- package/src/sync/merger.ts +14 -4
- package/src/sync/outbox.ts +21 -8
- package/src/sync/processors/mutation.ts +49 -45
- package/src/sync/processors/subscription.ts +0 -1
- package/src/sync/processors/sync.ts +1 -3
- package/src/sync/utils.ts +69 -12
- package/src/types.ts +181 -29
- package/src/util.ts +415 -176
|
@@ -29,13 +29,16 @@ import {
|
|
|
29
29
|
traverseModel,
|
|
30
30
|
validatePredicate,
|
|
31
31
|
sortCompareFunction,
|
|
32
|
+
keysEqual,
|
|
33
|
+
getStorename,
|
|
34
|
+
getIndexKeys,
|
|
35
|
+
extractPrimaryKeyValues,
|
|
32
36
|
} from '../../util';
|
|
33
37
|
import { Adapter } from './index';
|
|
34
38
|
|
|
35
39
|
const logger = new Logger('DataStore');
|
|
36
40
|
|
|
37
41
|
const DB_NAME = 'amplify-datastore';
|
|
38
|
-
|
|
39
42
|
class IndexedDBAdapter implements Adapter {
|
|
40
43
|
private schema: InternalSchema;
|
|
41
44
|
private namespaceResolver: NamespaceResolver;
|
|
@@ -50,6 +53,31 @@ class IndexedDBAdapter implements Adapter {
|
|
|
50
53
|
private reject: (value?: any) => void;
|
|
51
54
|
private dbName: string = DB_NAME;
|
|
52
55
|
|
|
56
|
+
private getStorenameForModel(
|
|
57
|
+
modelConstructor: PersistentModelConstructor<any>
|
|
58
|
+
) {
|
|
59
|
+
const namespace = this.namespaceResolver(modelConstructor);
|
|
60
|
+
const { name: modelName } = modelConstructor;
|
|
61
|
+
|
|
62
|
+
return getStorename(namespace, modelName);
|
|
63
|
+
}
|
|
64
|
+
|
|
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>;
|
|
71
|
+
const namespaceName = this.namespaceResolver(modelConstructor);
|
|
72
|
+
|
|
73
|
+
const keys = getIndexKeys(
|
|
74
|
+
this.schema.namespaces[namespaceName],
|
|
75
|
+
modelConstructor.name
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
return extractPrimaryKeyValues(model, keys);
|
|
79
|
+
}
|
|
80
|
+
|
|
53
81
|
private async checkPrivate() {
|
|
54
82
|
const isPrivate = await isPrivateMode().then(isPrivate => {
|
|
55
83
|
return isPrivate;
|
|
@@ -64,19 +92,12 @@ class IndexedDBAdapter implements Adapter {
|
|
|
64
92
|
}
|
|
65
93
|
}
|
|
66
94
|
|
|
67
|
-
private
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return this.getStorename(namespace, modelName);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
private getStorename(namespace: string, modelName: string) {
|
|
77
|
-
const storeName = `${namespace}_${modelName}`;
|
|
78
|
-
|
|
79
|
-
return storeName;
|
|
95
|
+
private getNamespaceAndModelFromStorename(storeName: string) {
|
|
96
|
+
const [namespaceName, ...modelNameArr] = storeName.split('_');
|
|
97
|
+
return {
|
|
98
|
+
namespaceName,
|
|
99
|
+
modelName: modelNameArr.join('_'),
|
|
100
|
+
};
|
|
80
101
|
}
|
|
81
102
|
|
|
82
103
|
async setUp(
|
|
@@ -90,6 +111,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
90
111
|
sessionId?: string
|
|
91
112
|
) {
|
|
92
113
|
await this.checkPrivate();
|
|
114
|
+
|
|
93
115
|
if (!this.initPromise) {
|
|
94
116
|
this.initPromise = new Promise((res, rej) => {
|
|
95
117
|
this.resolve = res;
|
|
@@ -108,7 +130,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
108
130
|
|
|
109
131
|
try {
|
|
110
132
|
if (!this.db) {
|
|
111
|
-
const VERSION =
|
|
133
|
+
const VERSION = 3;
|
|
112
134
|
this.db = await idb.openDB(this.dbName, VERSION, {
|
|
113
135
|
upgrade: async (db, oldVersion, newVersion, txn) => {
|
|
114
136
|
if (oldVersion === 0) {
|
|
@@ -116,7 +138,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
116
138
|
const namespace = theSchema.namespaces[namespaceName];
|
|
117
139
|
|
|
118
140
|
Object.keys(namespace.models).forEach(modelName => {
|
|
119
|
-
const storeName =
|
|
141
|
+
const storeName = getStorename(namespaceName, modelName);
|
|
120
142
|
this.createObjectStoreForModel(
|
|
121
143
|
db,
|
|
122
144
|
namespaceName,
|
|
@@ -129,7 +151,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
129
151
|
return;
|
|
130
152
|
}
|
|
131
153
|
|
|
132
|
-
if (oldVersion === 1 && newVersion ===
|
|
154
|
+
if ((oldVersion === 1 || oldVersion === 2) && newVersion === 3) {
|
|
133
155
|
try {
|
|
134
156
|
for (const storeName of txn.objectStoreNames) {
|
|
135
157
|
const origStore = txn.objectStore(storeName);
|
|
@@ -138,13 +160,15 @@ class IndexedDBAdapter implements Adapter {
|
|
|
138
160
|
const tmpName = `tmp_${storeName}`;
|
|
139
161
|
origStore.name = tmpName;
|
|
140
162
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
keyPath: undefined,
|
|
144
|
-
autoIncrement: true,
|
|
145
|
-
});
|
|
163
|
+
const { namespaceName, modelName } =
|
|
164
|
+
this.getNamespaceAndModelFromStorename(storeName);
|
|
146
165
|
|
|
147
|
-
newStore.
|
|
166
|
+
const newStore = this.createObjectStoreForModel(
|
|
167
|
+
db,
|
|
168
|
+
namespaceName,
|
|
169
|
+
storeName,
|
|
170
|
+
modelName
|
|
171
|
+
);
|
|
148
172
|
|
|
149
173
|
let cursor = await origStore.openCursor();
|
|
150
174
|
let count = 0;
|
|
@@ -175,7 +199,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
175
199
|
.map(modelName => {
|
|
176
200
|
return [
|
|
177
201
|
modelName,
|
|
178
|
-
|
|
202
|
+
getStorename(namespaceName, modelName),
|
|
179
203
|
];
|
|
180
204
|
})
|
|
181
205
|
.filter(([, storeName]) => !objectStoreNames.has(storeName))
|
|
@@ -208,19 +232,19 @@ class IndexedDBAdapter implements Adapter {
|
|
|
208
232
|
|
|
209
233
|
private async _get<T>(
|
|
210
234
|
storeOrStoreName: idb.IDBPObjectStore | string,
|
|
211
|
-
|
|
235
|
+
keyArr: string[]
|
|
212
236
|
): Promise<T> {
|
|
213
237
|
let index: idb.IDBPIndex;
|
|
214
238
|
|
|
215
239
|
if (typeof storeOrStoreName === 'string') {
|
|
216
240
|
const storeName = storeOrStoreName;
|
|
217
|
-
index = this.db.transaction(storeName, 'readonly').store.index('
|
|
241
|
+
index = this.db.transaction(storeName, 'readonly').store.index('byPk');
|
|
218
242
|
} else {
|
|
219
243
|
const store = storeOrStoreName;
|
|
220
|
-
index = store.index('
|
|
244
|
+
index = store.index('byPk');
|
|
221
245
|
}
|
|
222
246
|
|
|
223
|
-
const result = await index.get(
|
|
247
|
+
const result = await index.get(keyArr);
|
|
224
248
|
|
|
225
249
|
return result;
|
|
226
250
|
}
|
|
@@ -233,21 +257,26 @@ class IndexedDBAdapter implements Adapter {
|
|
|
233
257
|
const modelConstructor = Object.getPrototypeOf(model)
|
|
234
258
|
.constructor as PersistentModelConstructor<T>;
|
|
235
259
|
const storeName = this.getStorenameForModel(modelConstructor);
|
|
260
|
+
const namespaceName = this.namespaceResolver(modelConstructor);
|
|
261
|
+
|
|
236
262
|
const connectedModels = traverseModel(
|
|
237
263
|
modelConstructor.name,
|
|
238
264
|
model,
|
|
239
|
-
this.schema.namespaces[
|
|
265
|
+
this.schema.namespaces[namespaceName],
|
|
240
266
|
this.modelInstanceCreator,
|
|
241
267
|
this.getModelConstructorByModelName
|
|
242
268
|
);
|
|
243
|
-
const namespaceName = this.namespaceResolver(modelConstructor);
|
|
244
269
|
|
|
245
270
|
const set = new Set<string>();
|
|
246
271
|
const connectionStoreNames = Object.values(connectedModels).map(
|
|
247
272
|
({ modelName, item, instance }) => {
|
|
248
|
-
const storeName =
|
|
273
|
+
const storeName = getStorename(namespaceName, modelName);
|
|
249
274
|
set.add(storeName);
|
|
250
|
-
|
|
275
|
+
const keys = getIndexKeys(
|
|
276
|
+
this.schema.namespaces[namespaceName],
|
|
277
|
+
modelName
|
|
278
|
+
);
|
|
279
|
+
return { storeName, item, instance, keys };
|
|
251
280
|
}
|
|
252
281
|
);
|
|
253
282
|
|
|
@@ -257,7 +286,9 @@ class IndexedDBAdapter implements Adapter {
|
|
|
257
286
|
);
|
|
258
287
|
const store = tx.objectStore(storeName);
|
|
259
288
|
|
|
260
|
-
const
|
|
289
|
+
const keyValues = this.getIndexKeyValuesFromModel(model);
|
|
290
|
+
|
|
291
|
+
const fromDB = await this._get(store, keyValues);
|
|
261
292
|
|
|
262
293
|
if (condition && fromDB) {
|
|
263
294
|
const predicates = ModelPredicateCreator.getPredicates(condition);
|
|
@@ -276,17 +307,26 @@ class IndexedDBAdapter implements Adapter {
|
|
|
276
307
|
const result: [T, OpType.INSERT | OpType.UPDATE][] = [];
|
|
277
308
|
|
|
278
309
|
for await (const resItem of connectionStoreNames) {
|
|
279
|
-
const { storeName, item, instance } = resItem;
|
|
310
|
+
const { storeName, item, instance, keys } = resItem;
|
|
280
311
|
const store = tx.objectStore(storeName);
|
|
281
|
-
const { id } = item;
|
|
282
312
|
|
|
283
|
-
const
|
|
313
|
+
const itemKeyValues = keys.map(key => {
|
|
314
|
+
const value = item[key];
|
|
315
|
+
return value;
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
const fromDB = <T>await this._get(store, itemKeyValues);
|
|
284
319
|
const opType: OpType =
|
|
285
320
|
fromDB === undefined ? OpType.INSERT : OpType.UPDATE;
|
|
286
321
|
|
|
322
|
+
const modelKeyValues = this.getIndexKeyValuesFromModel(model);
|
|
323
|
+
|
|
287
324
|
// Even if the parent is an INSERT, the child might not be, so we need to get its key
|
|
288
|
-
if (
|
|
289
|
-
|
|
325
|
+
if (
|
|
326
|
+
keysEqual(itemKeyValues, modelKeyValues) ||
|
|
327
|
+
opType === OpType.INSERT
|
|
328
|
+
) {
|
|
329
|
+
const key = await store.index('byPk').getKey(itemKeyValues);
|
|
290
330
|
await store.put(item, key);
|
|
291
331
|
|
|
292
332
|
result.push([instance, opType]);
|
|
@@ -306,7 +346,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
306
346
|
const namespace = this.schema.namespaces[namespaceName];
|
|
307
347
|
const relations = namespace.relationships[srcModelName].relationTypes;
|
|
308
348
|
const connectionStoreNames = relations.map(({ modelName }) => {
|
|
309
|
-
return
|
|
349
|
+
return getStorename(namespaceName, modelName);
|
|
310
350
|
});
|
|
311
351
|
const modelConstructor = this.getModelConstructorByModelName(
|
|
312
352
|
namespaceName,
|
|
@@ -322,8 +362,9 @@ class IndexedDBAdapter implements Adapter {
|
|
|
322
362
|
const tx = this.db.transaction([...connectionStoreNames], 'readonly');
|
|
323
363
|
|
|
324
364
|
for await (const relation of relations) {
|
|
325
|
-
|
|
326
|
-
const
|
|
365
|
+
// target name, metadata, set by init
|
|
366
|
+
const { fieldName, modelName, targetName, targetNames } = relation;
|
|
367
|
+
const storeName = getStorename(namespaceName, modelName);
|
|
327
368
|
const store = tx.objectStore(storeName);
|
|
328
369
|
const modelConstructor = this.getModelConstructorByModelName(
|
|
329
370
|
namespaceName,
|
|
@@ -333,35 +374,91 @@ class IndexedDBAdapter implements Adapter {
|
|
|
333
374
|
switch (relation.relationType) {
|
|
334
375
|
case 'HAS_ONE':
|
|
335
376
|
for await (const recordItem of records) {
|
|
336
|
-
|
|
337
|
-
if (
|
|
377
|
+
// POST CPK codegen changes:
|
|
378
|
+
if (targetNames?.length) {
|
|
379
|
+
let getByFields = [];
|
|
380
|
+
let allPresent;
|
|
381
|
+
// iterate through all targetnames to make sure they are all present in the recordItem
|
|
382
|
+
allPresent = targetNames.every(targetName => {
|
|
383
|
+
return recordItem[targetName] != null;
|
|
384
|
+
});
|
|
338
385
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
);
|
|
386
|
+
if (!allPresent) {
|
|
387
|
+
break;
|
|
388
|
+
}
|
|
343
389
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
390
|
+
getByFields = targetNames as any;
|
|
391
|
+
|
|
392
|
+
// keys are the key values
|
|
393
|
+
const keys = getByFields.map(
|
|
394
|
+
getByField => recordItem[getByField]
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
const connectionRecord = await this._get(store, keys);
|
|
398
|
+
|
|
399
|
+
recordItem[fieldName] =
|
|
400
|
+
connectionRecord &&
|
|
401
|
+
this.modelInstanceCreator(modelConstructor, connectionRecord);
|
|
402
|
+
} else {
|
|
403
|
+
// If single target name, using old codegen
|
|
404
|
+
const getByfield = recordItem[targetName]
|
|
405
|
+
? targetName
|
|
406
|
+
: fieldName;
|
|
407
|
+
|
|
408
|
+
// We break here, because the recordItem does not have 'team', the `getByField`
|
|
409
|
+
// extract the keys on the related model.
|
|
410
|
+
if (!recordItem[getByfield]) break;
|
|
411
|
+
|
|
412
|
+
const key = [recordItem[getByfield]];
|
|
413
|
+
|
|
414
|
+
const connectionRecord = await this._get(store, key);
|
|
348
415
|
|
|
416
|
+
recordItem[fieldName] =
|
|
417
|
+
connectionRecord &&
|
|
418
|
+
this.modelInstanceCreator(modelConstructor, connectionRecord);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
349
421
|
break;
|
|
350
422
|
case 'BELONGS_TO':
|
|
351
423
|
for await (const recordItem of records) {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
424
|
+
// POST CPK codegen changes:
|
|
425
|
+
if (targetNames?.length) {
|
|
426
|
+
let allPresent;
|
|
427
|
+
// iterate through all targetnames to make sure they are all present in the recordItem
|
|
428
|
+
allPresent = targetNames.every(targetName => {
|
|
429
|
+
return recordItem[targetName] != null;
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
// If not present, there is not yet a connected record
|
|
433
|
+
if (!allPresent) {
|
|
434
|
+
break;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const keys = targetNames.map(
|
|
438
|
+
targetName => recordItem[targetName]
|
|
356
439
|
);
|
|
357
440
|
|
|
441
|
+
// Retrieve the connected record
|
|
442
|
+
const connectionRecord = await this._get(store, keys);
|
|
443
|
+
|
|
444
|
+
recordItem[fieldName] =
|
|
445
|
+
connectionRecord &&
|
|
446
|
+
this.modelInstanceCreator(modelConstructor, connectionRecord);
|
|
447
|
+
|
|
448
|
+
targetNames?.map(targetName => {
|
|
449
|
+
delete recordItem[targetName];
|
|
450
|
+
});
|
|
451
|
+
} else if (recordItem[targetName]) {
|
|
452
|
+
const key = [recordItem[targetName]];
|
|
453
|
+
|
|
454
|
+
const connectionRecord = await this._get(store, key);
|
|
455
|
+
|
|
358
456
|
recordItem[fieldName] =
|
|
359
457
|
connectionRecord &&
|
|
360
458
|
this.modelInstanceCreator(modelConstructor, connectionRecord);
|
|
361
459
|
delete recordItem[targetName];
|
|
362
460
|
}
|
|
363
461
|
}
|
|
364
|
-
|
|
365
462
|
break;
|
|
366
463
|
case 'HAS_MANY':
|
|
367
464
|
// TODO: Lazy loading
|
|
@@ -388,13 +485,19 @@ class IndexedDBAdapter implements Adapter {
|
|
|
388
485
|
|
|
389
486
|
const predicates =
|
|
390
487
|
predicate && ModelPredicateCreator.getPredicates(predicate);
|
|
391
|
-
const
|
|
488
|
+
const keyPath = getIndexKeys(
|
|
489
|
+
this.schema.namespaces[namespaceName],
|
|
490
|
+
modelConstructor.name
|
|
491
|
+
);
|
|
492
|
+
const queryByKey =
|
|
493
|
+
predicates && this.keyValueFromPredicate(predicates, keyPath);
|
|
494
|
+
|
|
392
495
|
const hasSort = pagination && pagination.sort;
|
|
393
496
|
const hasPagination = pagination && pagination.limit;
|
|
394
497
|
|
|
395
498
|
const records: T[] = await (async () => {
|
|
396
|
-
if (
|
|
397
|
-
const record = await this.
|
|
499
|
+
if (queryByKey) {
|
|
500
|
+
const record = await this.getByKey(storeName, queryByKey);
|
|
398
501
|
return record ? [record] : [];
|
|
399
502
|
}
|
|
400
503
|
|
|
@@ -418,11 +521,11 @@ class IndexedDBAdapter implements Adapter {
|
|
|
418
521
|
return await this.load(namespaceName, modelConstructor.name, records);
|
|
419
522
|
}
|
|
420
523
|
|
|
421
|
-
private async
|
|
524
|
+
private async getByKey<T extends PersistentModel>(
|
|
422
525
|
storeName: string,
|
|
423
|
-
|
|
526
|
+
keyValue: string[]
|
|
424
527
|
): Promise<T> {
|
|
425
|
-
const record = <T>await this._get(storeName,
|
|
528
|
+
const record = <T>await this._get(storeName, keyValue);
|
|
426
529
|
return record;
|
|
427
530
|
}
|
|
428
531
|
|
|
@@ -432,17 +535,27 @@ class IndexedDBAdapter implements Adapter {
|
|
|
432
535
|
return await this.db.getAll(storeName);
|
|
433
536
|
}
|
|
434
537
|
|
|
435
|
-
private
|
|
436
|
-
predicates: PredicatesGroup<T
|
|
437
|
-
|
|
538
|
+
private keyValueFromPredicate<T extends PersistentModel>(
|
|
539
|
+
predicates: PredicatesGroup<T>,
|
|
540
|
+
keyPath: string[]
|
|
541
|
+
): string[] | undefined {
|
|
438
542
|
const { predicates: predicateObjs } = predicates;
|
|
439
|
-
const idPredicate =
|
|
440
|
-
predicateObjs.length === 1 &&
|
|
441
|
-
(predicateObjs.find(
|
|
442
|
-
p => isPredicateObj(p) && p.field === 'id' && p.operator === 'eq'
|
|
443
|
-
) as PredicateObject<T>);
|
|
444
543
|
|
|
445
|
-
|
|
544
|
+
if (predicateObjs.length !== keyPath.length) {
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
const keyValues = [];
|
|
549
|
+
|
|
550
|
+
for (const key of keyPath) {
|
|
551
|
+
const predicateObj = predicateObjs.find(
|
|
552
|
+
p => isPredicateObj(p) && p.field === key && p.operator === 'eq'
|
|
553
|
+
) as PredicateObject<T>;
|
|
554
|
+
|
|
555
|
+
predicateObj && keyValues.push(predicateObj.operand);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
return keyValues.length === keyPath.length ? keyValues : undefined;
|
|
446
559
|
}
|
|
447
560
|
|
|
448
561
|
private async filterOnPredicate<T extends PersistentModel>(
|
|
@@ -605,15 +718,16 @@ class IndexedDBAdapter implements Adapter {
|
|
|
605
718
|
|
|
606
719
|
const modelConstructor = Object.getPrototypeOf(model)
|
|
607
720
|
.constructor as PersistentModelConstructor<T>;
|
|
608
|
-
const
|
|
721
|
+
const namespaceName = this.namespaceResolver(modelConstructor);
|
|
609
722
|
|
|
610
723
|
const storeName = this.getStorenameForModel(modelConstructor);
|
|
611
724
|
|
|
612
725
|
if (condition) {
|
|
613
726
|
const tx = this.db.transaction([storeName], 'readwrite');
|
|
614
727
|
const store = tx.objectStore(storeName);
|
|
728
|
+
const keyValues = this.getIndexKeyValuesFromModel(model);
|
|
615
729
|
|
|
616
|
-
const fromDB = await this._get(store,
|
|
730
|
+
const fromDB = await this._get(store, keyValues);
|
|
617
731
|
|
|
618
732
|
if (fromDB === undefined) {
|
|
619
733
|
const msg = 'Model instance not found in storage';
|
|
@@ -636,26 +750,28 @@ class IndexedDBAdapter implements Adapter {
|
|
|
636
750
|
await tx.done;
|
|
637
751
|
|
|
638
752
|
const relations =
|
|
639
|
-
this.schema.namespaces[
|
|
640
|
-
.
|
|
753
|
+
this.schema.namespaces[namespaceName].relationships[
|
|
754
|
+
modelConstructor.name
|
|
755
|
+
].relationTypes;
|
|
641
756
|
|
|
642
757
|
await this.deleteTraverse(
|
|
643
758
|
relations,
|
|
644
759
|
[model],
|
|
645
760
|
modelConstructor.name,
|
|
646
|
-
|
|
761
|
+
namespaceName,
|
|
647
762
|
deleteQueue
|
|
648
763
|
);
|
|
649
764
|
} else {
|
|
650
765
|
const relations =
|
|
651
|
-
this.schema.namespaces[
|
|
652
|
-
.
|
|
766
|
+
this.schema.namespaces[namespaceName].relationships[
|
|
767
|
+
modelConstructor.name
|
|
768
|
+
].relationTypes;
|
|
653
769
|
|
|
654
770
|
await this.deleteTraverse(
|
|
655
771
|
relations,
|
|
656
772
|
[model],
|
|
657
773
|
modelConstructor.name,
|
|
658
|
-
|
|
774
|
+
namespaceName,
|
|
659
775
|
deleteQueue
|
|
660
776
|
);
|
|
661
777
|
}
|
|
@@ -672,7 +788,10 @@ class IndexedDBAdapter implements Adapter {
|
|
|
672
788
|
}
|
|
673
789
|
|
|
674
790
|
private async deleteItem<T extends PersistentModel>(
|
|
675
|
-
deleteQueue?: {
|
|
791
|
+
deleteQueue?: {
|
|
792
|
+
storeName: string;
|
|
793
|
+
items: T[] | IDBValidKey[];
|
|
794
|
+
}[]
|
|
676
795
|
) {
|
|
677
796
|
const connectionStoreNames = deleteQueue.map(({ storeName }) => {
|
|
678
797
|
return storeName;
|
|
@@ -688,9 +807,11 @@ class IndexedDBAdapter implements Adapter {
|
|
|
688
807
|
let key: IDBValidKey;
|
|
689
808
|
|
|
690
809
|
if (typeof item === 'object') {
|
|
691
|
-
|
|
810
|
+
const keyValues = this.getIndexKeyValuesFromModel(item as T);
|
|
811
|
+
key = await store.index('byPk').getKey(keyValues);
|
|
692
812
|
} else {
|
|
693
|
-
|
|
813
|
+
const itemKey = [item.toString()];
|
|
814
|
+
key = await store.index('byPk').getKey([itemKey]);
|
|
694
815
|
}
|
|
695
816
|
|
|
696
817
|
if (key !== undefined) {
|
|
@@ -709,57 +830,109 @@ class IndexedDBAdapter implements Adapter {
|
|
|
709
830
|
deleteQueue: { storeName: string; items: T[] }[]
|
|
710
831
|
): Promise<void> {
|
|
711
832
|
for await (const rel of relations) {
|
|
712
|
-
const {
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
// if we were unable to find an index via relationTypes
|
|
722
|
-
// i.e. for keyName connections, attempt to find one by the
|
|
723
|
-
// associatedWith property
|
|
724
|
-
getIndexFromAssociation(
|
|
725
|
-
this.schema.namespaces[nameSpace].relationships[modelName].indexes,
|
|
726
|
-
rel.associatedWith
|
|
727
|
-
);
|
|
833
|
+
const {
|
|
834
|
+
relationType,
|
|
835
|
+
modelName,
|
|
836
|
+
targetName,
|
|
837
|
+
targetNames,
|
|
838
|
+
associatedWith,
|
|
839
|
+
} = rel;
|
|
840
|
+
|
|
841
|
+
const storeName = getStorename(nameSpace, modelName);
|
|
728
842
|
|
|
729
843
|
switch (relationType) {
|
|
730
844
|
case 'HAS_ONE':
|
|
731
845
|
for await (const model of models) {
|
|
732
|
-
const hasOneIndex =
|
|
733
|
-
|
|
734
|
-
const hasOneCustomField = targetName in model;
|
|
735
|
-
const value = hasOneCustomField ? model[targetName] : model.id;
|
|
736
|
-
if (!value) break;
|
|
737
|
-
|
|
738
|
-
const recordToDelete = <T>(
|
|
739
|
-
await this.db
|
|
740
|
-
.transaction(storeName, 'readwrite')
|
|
741
|
-
.objectStore(storeName)
|
|
742
|
-
.index(hasOneIndex)
|
|
743
|
-
.get(value)
|
|
744
|
-
);
|
|
846
|
+
const hasOneIndex = 'byPk';
|
|
745
847
|
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
848
|
+
if (targetNames?.length) {
|
|
849
|
+
// CPK codegen
|
|
850
|
+
const values = targetNames.map(targetName => model[targetName]);
|
|
851
|
+
|
|
852
|
+
if (values.length === 0) break;
|
|
853
|
+
|
|
854
|
+
const recordToDelete = <T>(
|
|
855
|
+
await this.db
|
|
856
|
+
.transaction(storeName, 'readwrite')
|
|
857
|
+
.objectStore(storeName)
|
|
858
|
+
.index(hasOneIndex)
|
|
859
|
+
.get(values)
|
|
860
|
+
);
|
|
861
|
+
|
|
862
|
+
await this.deleteTraverse(
|
|
863
|
+
this.schema.namespaces[nameSpace].relationships[modelName]
|
|
864
|
+
.relationTypes,
|
|
865
|
+
recordToDelete ? [recordToDelete] : [],
|
|
866
|
+
modelName,
|
|
867
|
+
nameSpace,
|
|
868
|
+
deleteQueue
|
|
869
|
+
);
|
|
870
|
+
break;
|
|
871
|
+
} else {
|
|
872
|
+
// PRE-CPK codegen
|
|
873
|
+
let index;
|
|
874
|
+
let values: string[];
|
|
875
|
+
|
|
876
|
+
if (targetName && targetName in model) {
|
|
877
|
+
index = hasOneIndex;
|
|
878
|
+
const value = model[targetName];
|
|
879
|
+
values = [value];
|
|
880
|
+
} else {
|
|
881
|
+
// backwards compatability for older versions of codegen that did not emit targetName for HAS_ONE relations
|
|
882
|
+
// TODO: can we deprecate this? it's been ~2 years since codegen started including targetName for HAS_ONE
|
|
883
|
+
// If we deprecate, we'll need to re-gen the MIPR in __tests__/schema.ts > newSchema
|
|
884
|
+
// otherwise some unit tests will fail
|
|
885
|
+
index = getIndex(
|
|
886
|
+
this.schema.namespaces[nameSpace].relationships[modelName]
|
|
887
|
+
.relationTypes,
|
|
888
|
+
srcModel
|
|
889
|
+
);
|
|
890
|
+
values = this.getIndexKeyValuesFromModel(model);
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
if (!values || !index) break;
|
|
894
|
+
|
|
895
|
+
const recordToDelete = <T>(
|
|
896
|
+
await this.db
|
|
897
|
+
.transaction(storeName, 'readwrite')
|
|
898
|
+
.objectStore(storeName)
|
|
899
|
+
.index(index)
|
|
900
|
+
.get(values)
|
|
901
|
+
);
|
|
902
|
+
|
|
903
|
+
await this.deleteTraverse(
|
|
904
|
+
this.schema.namespaces[nameSpace].relationships[modelName]
|
|
905
|
+
.relationTypes,
|
|
906
|
+
recordToDelete ? [recordToDelete] : [],
|
|
907
|
+
modelName,
|
|
908
|
+
nameSpace,
|
|
909
|
+
deleteQueue
|
|
910
|
+
);
|
|
911
|
+
}
|
|
754
912
|
}
|
|
755
913
|
break;
|
|
756
914
|
case 'HAS_MANY':
|
|
757
915
|
for await (const model of models) {
|
|
916
|
+
const index =
|
|
917
|
+
// explicit bi-directional @hasMany and @manyToMany
|
|
918
|
+
getIndex(
|
|
919
|
+
this.schema.namespaces[nameSpace].relationships[modelName]
|
|
920
|
+
.relationTypes,
|
|
921
|
+
srcModel
|
|
922
|
+
) ||
|
|
923
|
+
// uni and/or implicit @hasMany
|
|
924
|
+
getIndexFromAssociation(
|
|
925
|
+
this.schema.namespaces[nameSpace].relationships[modelName]
|
|
926
|
+
.indexes,
|
|
927
|
+
associatedWith
|
|
928
|
+
);
|
|
929
|
+
const keyValues = this.getIndexKeyValuesFromModel(model);
|
|
930
|
+
|
|
758
931
|
const childrenArray = await this.db
|
|
759
932
|
.transaction(storeName, 'readwrite')
|
|
760
933
|
.objectStore(storeName)
|
|
761
|
-
.index(index)
|
|
762
|
-
.getAll(
|
|
934
|
+
.index(index as string)
|
|
935
|
+
.getAll(keyValues);
|
|
763
936
|
|
|
764
937
|
await this.deleteTraverse(
|
|
765
938
|
this.schema.namespaces[nameSpace].relationships[modelName]
|
|
@@ -781,7 +954,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
781
954
|
}
|
|
782
955
|
|
|
783
956
|
deleteQueue.push({
|
|
784
|
-
storeName:
|
|
957
|
+
storeName: getStorename(nameSpace, srcModel),
|
|
785
958
|
items: models.map(record =>
|
|
786
959
|
this.modelInstanceCreator(
|
|
787
960
|
this.getModelConstructorByModelName(nameSpace, srcModel),
|
|
@@ -820,22 +993,29 @@ class IndexedDBAdapter implements Adapter {
|
|
|
820
993
|
const store = txn.store;
|
|
821
994
|
|
|
822
995
|
for (const item of items) {
|
|
996
|
+
const namespaceName = this.namespaceResolver(modelConstructor);
|
|
997
|
+
const modelName = modelConstructor.name;
|
|
998
|
+
const model = this.modelInstanceCreator(modelConstructor, item);
|
|
999
|
+
|
|
823
1000
|
const connectedModels = traverseModel(
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
this.schema.namespaces[
|
|
1001
|
+
modelName,
|
|
1002
|
+
model,
|
|
1003
|
+
this.schema.namespaces[namespaceName],
|
|
827
1004
|
this.modelInstanceCreator,
|
|
828
1005
|
this.getModelConstructorByModelName
|
|
829
1006
|
);
|
|
830
1007
|
|
|
831
|
-
const
|
|
832
|
-
const
|
|
833
|
-
|
|
1008
|
+
const keyValues = this.getIndexKeyValuesFromModel(model);
|
|
1009
|
+
const { _deleted } = item;
|
|
1010
|
+
|
|
1011
|
+
const index = store.index('byPk');
|
|
1012
|
+
const key = await index.getKey(keyValues);
|
|
834
1013
|
|
|
835
1014
|
if (!_deleted) {
|
|
836
|
-
const { instance } = connectedModels.find(
|
|
837
|
-
|
|
838
|
-
|
|
1015
|
+
const { instance } = connectedModels.find(({ instance }) => {
|
|
1016
|
+
const instanceKeyValues = this.getIndexKeyValuesFromModel(instance);
|
|
1017
|
+
return keysEqual(instanceKeyValues, keyValues);
|
|
1018
|
+
});
|
|
839
1019
|
|
|
840
1020
|
result.push([
|
|
841
1021
|
<T>(<unknown>instance),
|
|
@@ -856,7 +1036,7 @@ class IndexedDBAdapter implements Adapter {
|
|
|
856
1036
|
return result;
|
|
857
1037
|
}
|
|
858
1038
|
|
|
859
|
-
private
|
|
1039
|
+
private createObjectStoreForModel(
|
|
860
1040
|
db: idb.IDBPDatabase,
|
|
861
1041
|
namespaceName: string,
|
|
862
1042
|
storeName: string,
|
|
@@ -866,11 +1046,14 @@ class IndexedDBAdapter implements Adapter {
|
|
|
866
1046
|
autoIncrement: true,
|
|
867
1047
|
});
|
|
868
1048
|
|
|
869
|
-
const indexes =
|
|
870
|
-
this.schema.namespaces[namespaceName].relationships[modelName]
|
|
871
|
-
|
|
1049
|
+
const { indexes } =
|
|
1050
|
+
this.schema.namespaces[namespaceName].relationships[modelName];
|
|
1051
|
+
|
|
1052
|
+
indexes.forEach(([idxName, keyPath, options]) => {
|
|
1053
|
+
store.createIndex(idxName, keyPath, options);
|
|
1054
|
+
});
|
|
872
1055
|
|
|
873
|
-
store
|
|
1056
|
+
return store;
|
|
874
1057
|
}
|
|
875
1058
|
}
|
|
876
1059
|
|