@aws-amplify/datastore 3.7.3 → 3.7.4-custom-pk.86
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +144 -17
- package/README.md +154 -0
- package/dist/aws-amplify-datastore.js +3740 -1599
- package/dist/aws-amplify-datastore.js.map +1 -1
- package/dist/aws-amplify-datastore.min.js +25 -95
- package/dist/aws-amplify-datastore.min.js.map +1 -1
- package/lib/authModeStrategies/multiAuthStrategy.d.ts +2 -2
- package/lib/authModeStrategies/multiAuthStrategy.js +32 -26
- package/lib/authModeStrategies/multiAuthStrategy.js.map +1 -1
- package/lib/datastore/datastore.d.ts +17 -16
- package/lib/datastore/datastore.js +291 -83
- package/lib/datastore/datastore.js.map +1 -1
- package/lib/index.d.ts +3 -19
- package/lib/predicates/index.d.ts +3 -2
- package/lib/predicates/index.js +12 -2
- package/lib/predicates/index.js.map +1 -1
- package/lib/storage/adapter/AsyncStorageAdapter.d.ts +6 -2
- package/lib/storage/adapter/AsyncStorageAdapter.js +361 -193
- package/lib/storage/adapter/AsyncStorageAdapter.js.map +1 -1
- package/lib/storage/adapter/AsyncStorageDatabase.d.ts +4 -4
- package/lib/storage/adapter/AsyncStorageDatabase.js +59 -28
- package/lib/storage/adapter/AsyncStorageDatabase.js.map +1 -1
- package/lib/storage/adapter/InMemoryStore.js +2 -2
- package/lib/storage/adapter/InMemoryStore.js.map +1 -1
- package/lib/storage/adapter/IndexedDBAdapter.d.ts +7 -2
- package/lib/storage/adapter/IndexedDBAdapter.js +454 -252
- package/lib/storage/adapter/IndexedDBAdapter.js.map +1 -1
- package/lib/storage/adapter/index.d.ts +1 -1
- package/lib/storage/storage.d.ts +3 -3
- package/lib/storage/storage.js +106 -28
- package/lib/storage/storage.js.map +1 -1
- package/lib/sync/index.d.ts +23 -7
- package/lib/sync/index.js +18 -17
- package/lib/sync/index.js.map +1 -1
- package/lib/sync/merger.d.ts +3 -3
- package/lib/sync/merger.js +8 -6
- package/lib/sync/merger.js.map +1 -1
- package/lib/sync/outbox.d.ts +2 -2
- package/lib/sync/outbox.js +12 -9
- package/lib/sync/outbox.js.map +1 -1
- package/lib/sync/processors/errorMaps.d.ts +17 -0
- package/lib/sync/processors/errorMaps.js +125 -0
- package/lib/sync/processors/errorMaps.js.map +1 -0
- package/lib/sync/processors/mutation.d.ts +22 -4
- package/lib/sync/processors/mutation.js +119 -51
- package/lib/sync/processors/mutation.js.map +1 -1
- package/lib/sync/processors/subscription.d.ts +4 -2
- package/lib/sync/processors/subscription.js +76 -41
- package/lib/sync/processors/subscription.js.map +1 -1
- package/lib/sync/processors/sync.d.ts +4 -2
- package/lib/sync/processors/sync.js +62 -27
- package/lib/sync/processors/sync.js.map +1 -1
- package/lib/sync/utils.d.ts +3 -2
- package/lib/sync/utils.js +58 -7
- package/lib/sync/utils.js.map +1 -1
- package/lib/types.d.ts +90 -34
- package/lib/types.js +31 -2
- package/lib/types.js.map +1 -1
- package/lib/util.d.ts +48 -33
- package/lib/util.js +219 -57
- package/lib/util.js.map +1 -1
- package/lib-esm/authModeStrategies/multiAuthStrategy.d.ts +2 -2
- package/lib-esm/authModeStrategies/multiAuthStrategy.js +32 -26
- package/lib-esm/authModeStrategies/multiAuthStrategy.js.map +1 -1
- package/lib-esm/datastore/datastore.d.ts +17 -16
- package/lib-esm/datastore/datastore.js +293 -85
- package/lib-esm/datastore/datastore.js.map +1 -1
- package/lib-esm/index.d.ts +3 -19
- package/lib-esm/predicates/index.d.ts +3 -2
- package/lib-esm/predicates/index.js +13 -3
- package/lib-esm/predicates/index.js.map +1 -1
- package/lib-esm/storage/adapter/AsyncStorageAdapter.d.ts +6 -2
- package/lib-esm/storage/adapter/AsyncStorageAdapter.js +361 -193
- package/lib-esm/storage/adapter/AsyncStorageAdapter.js.map +1 -1
- package/lib-esm/storage/adapter/AsyncStorageDatabase.d.ts +4 -4
- package/lib-esm/storage/adapter/AsyncStorageDatabase.js +60 -29
- package/lib-esm/storage/adapter/AsyncStorageDatabase.js.map +1 -1
- package/lib-esm/storage/adapter/InMemoryStore.js +2 -2
- package/lib-esm/storage/adapter/InMemoryStore.js.map +1 -1
- package/lib-esm/storage/adapter/IndexedDBAdapter.d.ts +7 -2
- package/lib-esm/storage/adapter/IndexedDBAdapter.js +454 -252
- package/lib-esm/storage/adapter/IndexedDBAdapter.js.map +1 -1
- package/lib-esm/storage/adapter/index.d.ts +1 -1
- package/lib-esm/storage/storage.d.ts +3 -3
- package/lib-esm/storage/storage.js +106 -28
- package/lib-esm/storage/storage.js.map +1 -1
- package/lib-esm/sync/index.d.ts +23 -7
- package/lib-esm/sync/index.js +20 -19
- package/lib-esm/sync/index.js.map +1 -1
- package/lib-esm/sync/merger.d.ts +3 -3
- package/lib-esm/sync/merger.js +8 -6
- package/lib-esm/sync/merger.js.map +1 -1
- package/lib-esm/sync/outbox.d.ts +2 -2
- package/lib-esm/sync/outbox.js +13 -10
- package/lib-esm/sync/outbox.js.map +1 -1
- package/lib-esm/sync/processors/errorMaps.d.ts +17 -0
- package/lib-esm/sync/processors/errorMaps.js +119 -0
- package/lib-esm/sync/processors/errorMaps.js.map +1 -0
- package/lib-esm/sync/processors/mutation.d.ts +22 -4
- package/lib-esm/sync/processors/mutation.js +122 -54
- package/lib-esm/sync/processors/mutation.js.map +1 -1
- package/lib-esm/sync/processors/subscription.d.ts +4 -2
- package/lib-esm/sync/processors/subscription.js +77 -42
- package/lib-esm/sync/processors/subscription.js.map +1 -1
- package/lib-esm/sync/processors/sync.d.ts +4 -2
- package/lib-esm/sync/processors/sync.js +62 -27
- package/lib-esm/sync/processors/sync.js.map +1 -1
- package/lib-esm/sync/utils.d.ts +3 -2
- package/lib-esm/sync/utils.js +58 -8
- package/lib-esm/sync/utils.js.map +1 -1
- package/lib-esm/types.d.ts +90 -34
- package/lib-esm/types.js +29 -3
- package/lib-esm/types.js.map +1 -1
- package/lib-esm/util.d.ts +48 -33
- package/lib-esm/util.js +207 -54
- package/lib-esm/util.js.map +1 -1
- package/package.json +10 -9
- package/src/authModeStrategies/multiAuthStrategy.ts +24 -21
- package/src/datastore/datastore.ts +448 -148
- package/src/predicates/index.ts +32 -10
- package/src/storage/adapter/AsyncStorageAdapter.ts +305 -78
- package/src/storage/adapter/AsyncStorageDatabase.ts +65 -26
- package/src/storage/adapter/InMemoryStore.ts +2 -2
- package/src/storage/adapter/IndexedDBAdapter.ts +344 -101
- package/src/storage/adapter/index.ts +1 -1
- package/src/storage/storage.ts +85 -24
- package/src/sync/index.ts +74 -58
- package/src/sync/merger.ts +16 -4
- package/src/sync/outbox.ts +22 -8
- package/src/sync/processors/errorMaps.ts +93 -0
- package/src/sync/processors/mutation.ts +143 -84
- package/src/sync/processors/subscription.ts +67 -36
- package/src/sync/processors/sync.ts +37 -12
- package/src/sync/utils.ts +67 -16
- package/src/types.ts +252 -50
- package/src/util.ts +256 -42
- package/ssr/package.json +1 -1
package/src/predicates/index.ts
CHANGED
|
@@ -8,7 +8,11 @@ import {
|
|
|
8
8
|
ProducerModelPredicate,
|
|
9
9
|
SchemaModel,
|
|
10
10
|
} from '../types';
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
exhaustiveCheck,
|
|
13
|
+
extractPrimaryKeyFieldNames,
|
|
14
|
+
extractPrimaryKeyValues,
|
|
15
|
+
} from '../util';
|
|
12
16
|
|
|
13
17
|
export { ModelSortPredicateCreator } from './sort';
|
|
14
18
|
|
|
@@ -85,7 +89,7 @@ export class ModelPredicateCreator {
|
|
|
85
89
|
|
|
86
90
|
// Push the group to the top-level recorder
|
|
87
91
|
ModelPredicateCreator.predicateGroupsMap
|
|
88
|
-
.get(receiver)
|
|
92
|
+
.get(receiver)!
|
|
89
93
|
.predicates.push(group);
|
|
90
94
|
|
|
91
95
|
return receiver;
|
|
@@ -109,7 +113,7 @@ export class ModelPredicateCreator {
|
|
|
109
113
|
operand: any
|
|
110
114
|
) => {
|
|
111
115
|
ModelPredicateCreator.predicateGroupsMap
|
|
112
|
-
.get(receiver)
|
|
116
|
+
.get(receiver)!
|
|
113
117
|
.predicates.push({ field, operator, operand });
|
|
114
118
|
return receiver;
|
|
115
119
|
};
|
|
@@ -147,7 +151,7 @@ export class ModelPredicateCreator {
|
|
|
147
151
|
// transforms cb-style predicate into Proxy
|
|
148
152
|
static createFromExisting<T extends PersistentModel>(
|
|
149
153
|
modelDefinition: SchemaModel,
|
|
150
|
-
existing
|
|
154
|
+
existing?: ProducerModelPredicate<T>
|
|
151
155
|
) {
|
|
152
156
|
if (!existing || !modelDefinition) {
|
|
153
157
|
return undefined;
|
|
@@ -158,13 +162,31 @@ export class ModelPredicateCreator {
|
|
|
158
162
|
);
|
|
159
163
|
}
|
|
160
164
|
|
|
161
|
-
static
|
|
165
|
+
static createForSingleField<T extends PersistentModel>(
|
|
162
166
|
modelDefinition: SchemaModel,
|
|
163
|
-
|
|
167
|
+
fieldName: string,
|
|
168
|
+
value: string
|
|
164
169
|
) {
|
|
165
|
-
return ModelPredicateCreator.createPredicateBuilder<T>(modelDefinition)
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
170
|
+
return ModelPredicateCreator.createPredicateBuilder<T>(modelDefinition)[
|
|
171
|
+
fieldName
|
|
172
|
+
](<any>'eq', <any>value);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
static createForPk<T extends PersistentModel>(
|
|
176
|
+
modelDefinition: SchemaModel,
|
|
177
|
+
model: T
|
|
178
|
+
) {
|
|
179
|
+
const keyFields = extractPrimaryKeyFieldNames(modelDefinition);
|
|
180
|
+
const keyValues = extractPrimaryKeyValues(model, keyFields);
|
|
181
|
+
|
|
182
|
+
let modelPredicate =
|
|
183
|
+
ModelPredicateCreator.createPredicateBuilder<T>(modelDefinition);
|
|
184
|
+
|
|
185
|
+
keyFields.forEach((field, idx) => {
|
|
186
|
+
const operand = keyValues[idx];
|
|
187
|
+
modelPredicate = modelPredicate[field](<any>'eq', <any>operand);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
return modelPredicate;
|
|
169
191
|
}
|
|
170
192
|
}
|
|
@@ -33,6 +33,8 @@ import {
|
|
|
33
33
|
|
|
34
34
|
const logger = new Logger('DataStore');
|
|
35
35
|
|
|
36
|
+
const DEFAULT_PRIMARY_KEY_SEPARATOR = '#';
|
|
37
|
+
|
|
36
38
|
export class AsyncStorageAdapter implements Adapter {
|
|
37
39
|
private schema: InternalSchema;
|
|
38
40
|
private namespaceResolver: NamespaceResolver;
|
|
@@ -61,6 +63,56 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
61
63
|
return storeName;
|
|
62
64
|
}
|
|
63
65
|
|
|
66
|
+
// Returns primary keys for a model
|
|
67
|
+
private getIndexKeys(namespaceName: string, modelName: string): string[] {
|
|
68
|
+
const namespace = this.schema.namespaces[namespaceName];
|
|
69
|
+
|
|
70
|
+
const keyPath = namespace?.keys[modelName]?.primaryKey;
|
|
71
|
+
|
|
72
|
+
if (keyPath) {
|
|
73
|
+
return keyPath;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return ['id'];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Retrieves concatenated primary key values from a model
|
|
80
|
+
private getIndexKeyValuesPath<T extends PersistentModel>(model: T): string {
|
|
81
|
+
const modelConstructor = Object.getPrototypeOf(model)
|
|
82
|
+
.constructor as PersistentModelConstructor<T>;
|
|
83
|
+
const namespaceName = this.namespaceResolver(modelConstructor);
|
|
84
|
+
const keys = this.getIndexKeys(namespaceName, modelConstructor.name);
|
|
85
|
+
|
|
86
|
+
// Retrieve key values from model
|
|
87
|
+
const keyValues = keys.map(field => model[field]);
|
|
88
|
+
|
|
89
|
+
// Return concatenated key values
|
|
90
|
+
return keyValues.join(DEFAULT_PRIMARY_KEY_SEPARATOR);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Retrieves concatenated primary key values from a model
|
|
94
|
+
private getIndexKeyValues<T extends PersistentModel>(model: T): string[] {
|
|
95
|
+
const modelConstructor = Object.getPrototypeOf(model)
|
|
96
|
+
.constructor as PersistentModelConstructor<T>;
|
|
97
|
+
const namespaceName = this.namespaceResolver(modelConstructor);
|
|
98
|
+
const keys = this.getIndexKeys(namespaceName, modelConstructor.name);
|
|
99
|
+
|
|
100
|
+
// Retrieve key values from model
|
|
101
|
+
return keys.map(field => model[field]);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private keysEqual(keysA, keysB): boolean {
|
|
105
|
+
if (keysA.length !== keysB.length) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (keysA.length === 1) {
|
|
110
|
+
return keysA[0] === keysB[0];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return keysA.every((key, idx) => key === keysB[idx]);
|
|
114
|
+
}
|
|
115
|
+
|
|
64
116
|
async setUp(
|
|
65
117
|
theSchema: InternalSchema,
|
|
66
118
|
namespaceResolver: NamespaceResolver,
|
|
@@ -101,23 +153,29 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
101
153
|
const modelConstructor = Object.getPrototypeOf(model)
|
|
102
154
|
.constructor as PersistentModelConstructor<T>;
|
|
103
155
|
const storeName = this.getStorenameForModel(modelConstructor);
|
|
156
|
+
|
|
157
|
+
const namespaceName = this.namespaceResolver(modelConstructor);
|
|
158
|
+
|
|
104
159
|
const connectedModels = traverseModel(
|
|
105
160
|
modelConstructor.name,
|
|
106
161
|
model,
|
|
107
|
-
this.schema.namespaces[
|
|
162
|
+
this.schema.namespaces[namespaceName],
|
|
108
163
|
this.modelInstanceCreator,
|
|
109
164
|
this.getModelConstructorByModelName
|
|
110
165
|
);
|
|
111
|
-
|
|
166
|
+
|
|
112
167
|
const set = new Set<string>();
|
|
113
168
|
const connectionStoreNames = Object.values(connectedModels).map(
|
|
114
169
|
({ modelName, item, instance }) => {
|
|
115
170
|
const storeName = this.getStorename(namespaceName, modelName);
|
|
116
171
|
set.add(storeName);
|
|
117
|
-
|
|
172
|
+
const keys = this.getIndexKeys(namespaceName, modelName);
|
|
173
|
+
return { storeName, item, instance, keys };
|
|
118
174
|
}
|
|
119
175
|
);
|
|
120
|
-
const
|
|
176
|
+
const keyValuesPath = this.getIndexKeyValuesPath(model);
|
|
177
|
+
|
|
178
|
+
const fromDB = await this.db.get(keyValuesPath, storeName);
|
|
121
179
|
|
|
122
180
|
if (condition && fromDB) {
|
|
123
181
|
const predicates = ModelPredicateCreator.getPredicates(condition);
|
|
@@ -136,14 +194,22 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
136
194
|
const result: [T, OpType.INSERT | OpType.UPDATE][] = [];
|
|
137
195
|
|
|
138
196
|
for await (const resItem of connectionStoreNames) {
|
|
139
|
-
const { storeName, item, instance } = resItem;
|
|
140
|
-
const { id } = item;
|
|
197
|
+
const { storeName, item, instance, keys } = resItem;
|
|
141
198
|
|
|
142
|
-
|
|
199
|
+
/* Find the key values in the item, and concatenate them */
|
|
200
|
+
const itemKeyValues: string[] = keys.map(key => item[key]);
|
|
201
|
+
const itemKeyValuesPath: string = itemKeyValues.join(
|
|
202
|
+
DEFAULT_PRIMARY_KEY_SEPARATOR
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
const fromDB = <T>await this.db.get(itemKeyValuesPath, storeName);
|
|
143
206
|
const opType: OpType = fromDB ? OpType.UPDATE : OpType.INSERT;
|
|
207
|
+
const modelKeyValues = this.getIndexKeyValues(model);
|
|
208
|
+
const keysEqual = this.keysEqual(itemKeyValues, modelKeyValues);
|
|
144
209
|
|
|
145
|
-
|
|
146
|
-
|
|
210
|
+
// If item key values and model key values are equal, save to db
|
|
211
|
+
if (keysEqual || opType === OpType.INSERT) {
|
|
212
|
+
await this.db.save(item, storeName, keys, itemKeyValuesPath);
|
|
147
213
|
|
|
148
214
|
result.push([instance, opType]);
|
|
149
215
|
}
|
|
@@ -174,7 +240,8 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
174
240
|
}
|
|
175
241
|
|
|
176
242
|
for await (const relation of relations) {
|
|
177
|
-
const { fieldName, modelName, targetName, relationType } =
|
|
243
|
+
const { fieldName, modelName, targetName, targetNames, relationType } =
|
|
244
|
+
relation;
|
|
178
245
|
const storeName = this.getStorename(namespaceName, modelName);
|
|
179
246
|
const modelConstructor = this.getModelConstructorByModelName(
|
|
180
247
|
namespaceName,
|
|
@@ -184,28 +251,88 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
184
251
|
switch (relationType) {
|
|
185
252
|
case 'HAS_ONE':
|
|
186
253
|
for await (const recordItem of records) {
|
|
187
|
-
|
|
188
|
-
if (
|
|
254
|
+
// ASYNC CPK TODO: make this cleaner
|
|
255
|
+
if (targetNames?.length) {
|
|
256
|
+
let getByFields = [];
|
|
257
|
+
let allPresent;
|
|
258
|
+
// iterate through all targetnames to make sure they are all present in the recordItem
|
|
259
|
+
allPresent = targetNames.every(targetName => {
|
|
260
|
+
return recordItem[targetName] != null;
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
if (!allPresent) {
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
getByFields = targetNames as any;
|
|
268
|
+
|
|
269
|
+
// keys are the key values
|
|
270
|
+
const keys = getByFields
|
|
271
|
+
.map(getByField => recordItem[getByField])
|
|
272
|
+
.join(DEFAULT_PRIMARY_KEY_SEPARATOR);
|
|
189
273
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
274
|
+
const connectionRecord = await this.db.get(
|
|
275
|
+
keys as any,
|
|
276
|
+
storeName
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
recordItem[fieldName] =
|
|
280
|
+
connectionRecord &&
|
|
281
|
+
this.modelInstanceCreator(modelConstructor, connectionRecord);
|
|
282
|
+
} else {
|
|
283
|
+
const getByfield = recordItem[targetName]
|
|
284
|
+
? targetName
|
|
285
|
+
: fieldName;
|
|
286
|
+
if (!recordItem[getByfield]) break;
|
|
287
|
+
|
|
288
|
+
const key = recordItem[getByfield];
|
|
289
|
+
|
|
290
|
+
const connectionRecord = await this.db.get(key, storeName);
|
|
194
291
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
292
|
+
recordItem[fieldName] =
|
|
293
|
+
connectionRecord &&
|
|
294
|
+
this.modelInstanceCreator(modelConstructor, connectionRecord);
|
|
295
|
+
}
|
|
198
296
|
}
|
|
199
297
|
|
|
200
298
|
break;
|
|
201
299
|
case 'BELONGS_TO':
|
|
202
300
|
for await (const recordItem of records) {
|
|
203
|
-
|
|
301
|
+
// ASYNC CPK TODO: make this cleaner
|
|
302
|
+
if (targetNames?.length) {
|
|
303
|
+
let allPresent;
|
|
304
|
+
// iterate through all targetnames to make sure they are all present in the recordItem
|
|
305
|
+
allPresent = targetNames.every(targetName => {
|
|
306
|
+
return recordItem[targetName] != null;
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// If not present, there is not yet a connected record
|
|
310
|
+
if (!allPresent) {
|
|
311
|
+
break;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const keys = targetNames
|
|
315
|
+
.map(targetName => recordItem[targetName])
|
|
316
|
+
.join(DEFAULT_PRIMARY_KEY_SEPARATOR);
|
|
317
|
+
|
|
318
|
+
// Retrieve the connected record
|
|
204
319
|
const connectionRecord = await this.db.get(
|
|
205
|
-
|
|
320
|
+
keys as any,
|
|
206
321
|
storeName
|
|
207
322
|
);
|
|
208
323
|
|
|
324
|
+
recordItem[fieldName] =
|
|
325
|
+
connectionRecord &&
|
|
326
|
+
this.modelInstanceCreator(modelConstructor, connectionRecord);
|
|
327
|
+
|
|
328
|
+
targetNames?.map(targetName => {
|
|
329
|
+
delete recordItem[targetName];
|
|
330
|
+
});
|
|
331
|
+
} else if (recordItem[targetName as any]) {
|
|
332
|
+
const key = recordItem[targetName];
|
|
333
|
+
|
|
334
|
+
const connectionRecord = await this.db.get(key, storeName);
|
|
335
|
+
|
|
209
336
|
recordItem[fieldName] =
|
|
210
337
|
connectionRecord &&
|
|
211
338
|
this.modelInstanceCreator(modelConstructor, connectionRecord);
|
|
@@ -238,13 +365,16 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
238
365
|
|
|
239
366
|
const predicates =
|
|
240
367
|
predicate && ModelPredicateCreator.getPredicates(predicate);
|
|
241
|
-
const
|
|
368
|
+
const keys = this.getIndexKeys(namespaceName, modelConstructor.name);
|
|
369
|
+
const queryByKey =
|
|
370
|
+
predicates && this.keyValueFromPredicate(predicates, keys);
|
|
371
|
+
|
|
242
372
|
const hasSort = pagination && pagination.sort;
|
|
243
373
|
const hasPagination = pagination && pagination.limit;
|
|
244
374
|
|
|
245
375
|
const records: T[] = await (async () => {
|
|
246
|
-
if (
|
|
247
|
-
const record = await this.
|
|
376
|
+
if (queryByKey) {
|
|
377
|
+
const record = await this.getByKey(storeName, queryByKey);
|
|
248
378
|
return record ? [record] : [];
|
|
249
379
|
}
|
|
250
380
|
|
|
@@ -264,11 +394,11 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
264
394
|
return await this.load(namespaceName, modelConstructor.name, records);
|
|
265
395
|
}
|
|
266
396
|
|
|
267
|
-
private async
|
|
397
|
+
private async getByKey<T extends PersistentModel>(
|
|
268
398
|
storeName: string,
|
|
269
|
-
|
|
399
|
+
keyValuePath: string
|
|
270
400
|
): Promise<T> {
|
|
271
|
-
const record = <T>await this.db.get(
|
|
401
|
+
const record = <T>await this.db.get(keyValuePath, storeName);
|
|
272
402
|
return record;
|
|
273
403
|
}
|
|
274
404
|
|
|
@@ -278,17 +408,29 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
278
408
|
return await this.db.getAll(storeName);
|
|
279
409
|
}
|
|
280
410
|
|
|
281
|
-
private
|
|
282
|
-
predicates: PredicatesGroup<T
|
|
283
|
-
|
|
411
|
+
private keyValueFromPredicate<T extends PersistentModel>(
|
|
412
|
+
predicates: PredicatesGroup<T>,
|
|
413
|
+
keys: string[]
|
|
414
|
+
): string | undefined {
|
|
284
415
|
const { predicates: predicateObjs } = predicates;
|
|
285
|
-
const idPredicate =
|
|
286
|
-
predicateObjs.length === 1 &&
|
|
287
|
-
(predicateObjs.find(
|
|
288
|
-
p => isPredicateObj(p) && p.field === 'id' && p.operator === 'eq'
|
|
289
|
-
) as PredicateObject<T>);
|
|
290
416
|
|
|
291
|
-
|
|
417
|
+
if (predicateObjs.length !== keys.length) {
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const keyValues = [];
|
|
422
|
+
|
|
423
|
+
for (const key of keys) {
|
|
424
|
+
const predicateObj = predicateObjs.find(
|
|
425
|
+
p => isPredicateObj(p) && p.field === key && p.operator === 'eq'
|
|
426
|
+
) as PredicateObject<T>;
|
|
427
|
+
|
|
428
|
+
predicateObj && keyValues.push(predicateObj.operand);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
return keyValues.length === keys.length
|
|
432
|
+
? keyValues.join(DEFAULT_PRIMARY_KEY_SEPARATOR)
|
|
433
|
+
: undefined;
|
|
292
434
|
}
|
|
293
435
|
|
|
294
436
|
private async filterOnPredicate<T extends PersistentModel>(
|
|
@@ -328,6 +470,7 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
328
470
|
|
|
329
471
|
return records.slice(start, end);
|
|
330
472
|
}
|
|
473
|
+
|
|
331
474
|
return records;
|
|
332
475
|
}
|
|
333
476
|
|
|
@@ -337,6 +480,7 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
337
480
|
): Promise<T | undefined> {
|
|
338
481
|
const storeName = this.getStorenameForModel(modelConstructor);
|
|
339
482
|
const result = <T>await this.db.getOne(firstOrLast, storeName);
|
|
483
|
+
|
|
340
484
|
return result && this.modelInstanceCreator(modelConstructor, result);
|
|
341
485
|
}
|
|
342
486
|
|
|
@@ -372,6 +516,7 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
372
516
|
(acc, { items }) => acc.concat(items),
|
|
373
517
|
<T[]>[]
|
|
374
518
|
);
|
|
519
|
+
|
|
375
520
|
return [models, deletedModels];
|
|
376
521
|
} else {
|
|
377
522
|
await this.deleteTraverse(
|
|
@@ -396,11 +541,14 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
396
541
|
|
|
397
542
|
const modelConstructor = Object.getPrototypeOf(model)
|
|
398
543
|
.constructor as PersistentModelConstructor<T>;
|
|
399
|
-
const
|
|
544
|
+
const namespaceName = this.namespaceResolver(modelConstructor);
|
|
400
545
|
|
|
401
546
|
const storeName = this.getStorenameForModel(modelConstructor);
|
|
547
|
+
|
|
402
548
|
if (condition) {
|
|
403
|
-
const
|
|
549
|
+
const keyValuePath = this.getIndexKeyValuesPath(model);
|
|
550
|
+
|
|
551
|
+
const fromDB = await this.db.get(keyValuePath, storeName);
|
|
404
552
|
|
|
405
553
|
if (fromDB === undefined) {
|
|
406
554
|
const msg = 'Model instance not found in storage';
|
|
@@ -421,25 +569,28 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
421
569
|
}
|
|
422
570
|
|
|
423
571
|
const relations =
|
|
424
|
-
this.schema.namespaces[
|
|
425
|
-
.
|
|
572
|
+
this.schema.namespaces[namespaceName].relationships[
|
|
573
|
+
modelConstructor.name
|
|
574
|
+
].relationTypes;
|
|
575
|
+
|
|
426
576
|
await this.deleteTraverse(
|
|
427
577
|
relations,
|
|
428
578
|
[model],
|
|
429
579
|
modelConstructor.name,
|
|
430
|
-
|
|
580
|
+
namespaceName,
|
|
431
581
|
deleteQueue
|
|
432
582
|
);
|
|
433
583
|
} else {
|
|
434
584
|
const relations =
|
|
435
|
-
this.schema.namespaces[
|
|
436
|
-
.
|
|
585
|
+
this.schema.namespaces[namespaceName].relationships[
|
|
586
|
+
modelConstructor.name
|
|
587
|
+
].relationTypes;
|
|
437
588
|
|
|
438
589
|
await this.deleteTraverse(
|
|
439
590
|
relations,
|
|
440
591
|
[model],
|
|
441
592
|
modelConstructor.name,
|
|
442
|
-
|
|
593
|
+
namespaceName,
|
|
443
594
|
deleteQueue
|
|
444
595
|
);
|
|
445
596
|
}
|
|
@@ -464,8 +615,8 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
464
615
|
for await (const item of items) {
|
|
465
616
|
if (item) {
|
|
466
617
|
if (typeof item === 'object') {
|
|
467
|
-
const
|
|
468
|
-
await this.db.delete(
|
|
618
|
+
const keyValuesPath: string = this.getIndexKeyValuesPath(item as T);
|
|
619
|
+
await this.db.delete(keyValuesPath, storeName);
|
|
469
620
|
}
|
|
470
621
|
}
|
|
471
622
|
}
|
|
@@ -487,10 +638,16 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
487
638
|
deleteQueue: { storeName: string; items: T[] }[]
|
|
488
639
|
): Promise<void> {
|
|
489
640
|
for await (const rel of relations) {
|
|
490
|
-
const {
|
|
641
|
+
const {
|
|
642
|
+
relationType,
|
|
643
|
+
modelName,
|
|
644
|
+
targetName,
|
|
645
|
+
targetNames,
|
|
646
|
+
associatedWith,
|
|
647
|
+
} = rel;
|
|
491
648
|
const storeName = this.getStorename(nameSpace, modelName);
|
|
492
649
|
|
|
493
|
-
const index:
|
|
650
|
+
const index: any =
|
|
494
651
|
getIndex(
|
|
495
652
|
this.schema.namespaces[nameSpace].relationships[modelName]
|
|
496
653
|
.relationTypes,
|
|
@@ -507,35 +664,102 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
507
664
|
switch (relationType) {
|
|
508
665
|
case 'HAS_ONE':
|
|
509
666
|
for await (const model of models) {
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
const hasOneCustomField = targetName in model;
|
|
513
|
-
const value = hasOneCustomField ? model[targetName] : model.id;
|
|
514
|
-
if (!value) break;
|
|
667
|
+
if (targetNames && targetNames?.length) {
|
|
668
|
+
const hasOneIndex = index || associatedWith;
|
|
515
669
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
670
|
+
// iterate over targetNames array and see if each item is present in model object
|
|
671
|
+
// targetNames here being the keys for the CHILD model
|
|
672
|
+
const hasConnectedModelFields = targetNames.every(targetName =>
|
|
673
|
+
model.hasOwnProperty(targetName)
|
|
674
|
+
);
|
|
520
675
|
|
|
521
|
-
|
|
522
|
-
this.
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
676
|
+
// PK / Composite key for the parent model
|
|
677
|
+
const keyValuesPath: string = this.getIndexKeyValuesPath(model);
|
|
678
|
+
|
|
679
|
+
let values;
|
|
680
|
+
|
|
681
|
+
if (hasConnectedModelFields) {
|
|
682
|
+
// Values will be that of the child model
|
|
683
|
+
values = targetNames.map(
|
|
684
|
+
targetName => model[targetName]
|
|
685
|
+
) as any;
|
|
686
|
+
} else {
|
|
687
|
+
// values will be that of the parent model
|
|
688
|
+
values = keyValuesPath;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
if (values.length === 0) break;
|
|
692
|
+
|
|
693
|
+
const allRecords = await this.db.getAll(storeName);
|
|
694
|
+
|
|
695
|
+
let recordToDelete;
|
|
696
|
+
|
|
697
|
+
// values === targetNames
|
|
698
|
+
if (hasConnectedModelFields) {
|
|
699
|
+
/**
|
|
700
|
+
* Retrieve record by finding the record where all
|
|
701
|
+
* targetNames are present on the connected model
|
|
702
|
+
*/
|
|
703
|
+
recordToDelete = allRecords.filter(childItem =>
|
|
704
|
+
values.every(value => childItem[value] != null)
|
|
705
|
+
) as T[];
|
|
706
|
+
} else {
|
|
707
|
+
// values === keyValuePath
|
|
708
|
+
recordToDelete = allRecords.filter(
|
|
709
|
+
childItem => childItem[hasOneIndex] === values
|
|
710
|
+
) as T[];
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
await this.deleteTraverse<T>(
|
|
714
|
+
this.schema.namespaces[nameSpace].relationships[modelName]
|
|
715
|
+
.relationTypes,
|
|
716
|
+
recordToDelete,
|
|
717
|
+
modelName,
|
|
718
|
+
nameSpace,
|
|
719
|
+
deleteQueue
|
|
720
|
+
);
|
|
721
|
+
} else {
|
|
722
|
+
const hasOneIndex = index || associatedWith;
|
|
723
|
+
const hasOneCustomField = targetName in model;
|
|
724
|
+
const keyValuesPath: string = this.getIndexKeyValuesPath(model);
|
|
725
|
+
const value = hasOneCustomField
|
|
726
|
+
? model[targetName]
|
|
727
|
+
: keyValuesPath;
|
|
728
|
+
|
|
729
|
+
if (!value) break;
|
|
730
|
+
|
|
731
|
+
const allRecords = await this.db.getAll(storeName);
|
|
732
|
+
|
|
733
|
+
const recordToDelete = allRecords.filter(
|
|
734
|
+
childItem => childItem[hasOneIndex] === value
|
|
735
|
+
) as T[];
|
|
736
|
+
|
|
737
|
+
await this.deleteTraverse<T>(
|
|
738
|
+
this.schema.namespaces[nameSpace].relationships[modelName]
|
|
739
|
+
.relationTypes,
|
|
740
|
+
recordToDelete,
|
|
741
|
+
modelName,
|
|
742
|
+
nameSpace,
|
|
743
|
+
deleteQueue
|
|
744
|
+
);
|
|
745
|
+
}
|
|
529
746
|
}
|
|
530
747
|
break;
|
|
531
748
|
case 'HAS_MANY':
|
|
532
749
|
for await (const model of models) {
|
|
750
|
+
// Key values for the parent model:
|
|
751
|
+
const keyValues: string[] = this.getIndexKeyValues(model);
|
|
752
|
+
|
|
533
753
|
const allRecords = await this.db.getAll(storeName);
|
|
534
|
-
const childrenArray = allRecords.filter(
|
|
535
|
-
childItem => childItem[index] === model.id
|
|
536
|
-
);
|
|
537
754
|
|
|
538
|
-
|
|
755
|
+
// Use constant if we go with this approach
|
|
756
|
+
const indices = index.split('-');
|
|
757
|
+
|
|
758
|
+
const childrenArray = allRecords.filter(childItem =>
|
|
759
|
+
indices.every(index => keyValues.includes(childItem[index]))
|
|
760
|
+
) as T[];
|
|
761
|
+
|
|
762
|
+
await this.deleteTraverse<T>(
|
|
539
763
|
this.schema.namespaces[nameSpace].relationships[modelName]
|
|
540
764
|
.relationTypes,
|
|
541
765
|
childrenArray,
|
|
@@ -579,28 +803,31 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
579
803
|
const { name: modelName } = modelConstructor;
|
|
580
804
|
const namespaceName = this.namespaceResolver(modelConstructor);
|
|
581
805
|
const storeName = this.getStorename(namespaceName, modelName);
|
|
582
|
-
|
|
806
|
+
const keys = this.getIndexKeys(namespaceName, modelName);
|
|
583
807
|
const batch: ModelInstanceMetadata[] = [];
|
|
584
808
|
|
|
585
809
|
for (const item of items) {
|
|
586
|
-
const
|
|
810
|
+
const model = this.modelInstanceCreator(modelConstructor, item);
|
|
587
811
|
|
|
588
812
|
const connectedModels = traverseModel(
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
this.schema.namespaces[
|
|
813
|
+
modelName,
|
|
814
|
+
model,
|
|
815
|
+
this.schema.namespaces[namespaceName],
|
|
592
816
|
this.modelInstanceCreator,
|
|
593
817
|
this.getModelConstructorByModelName
|
|
594
818
|
);
|
|
595
819
|
|
|
596
|
-
const
|
|
597
|
-
|
|
598
|
-
)
|
|
820
|
+
const keyValuesPath = this.getIndexKeyValuesPath(model);
|
|
821
|
+
|
|
822
|
+
const { instance } = connectedModels.find(({ instance }) => {
|
|
823
|
+
const instanceKeyValuesPath = this.getIndexKeyValuesPath(instance);
|
|
824
|
+
return this.keysEqual([instanceKeyValuesPath], [keyValuesPath]);
|
|
825
|
+
});
|
|
599
826
|
|
|
600
827
|
batch.push(instance);
|
|
601
828
|
}
|
|
602
829
|
|
|
603
|
-
return await this.db.batchSave(storeName, batch);
|
|
830
|
+
return await this.db.batchSave(storeName, batch, keys);
|
|
604
831
|
}
|
|
605
832
|
}
|
|
606
833
|
|