@aws-amplify/datastore 3.12.12 → 3.12.13-ds-allow-applicable-data.6
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 +19 -114
- package/dist/aws-amplify-datastore.js +4815 -2572
- package/dist/aws-amplify-datastore.js.map +1 -1
- package/dist/aws-amplify-datastore.min.js +9 -9
- package/dist/aws-amplify-datastore.min.js.map +1 -1
- package/lib/authModeStrategies/multiAuthStrategy.d.ts +11 -0
- package/lib/authModeStrategies/multiAuthStrategy.js +11 -0
- package/lib/authModeStrategies/multiAuthStrategy.js.map +1 -1
- package/lib/datastore/datastore.d.ts +91 -17
- package/lib/datastore/datastore.js +535 -332
- 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 +93 -28
- package/lib/storage/storage.js.map +1 -1
- package/lib/sync/datastoreConnectivity.d.ts +1 -0
- package/lib/sync/datastoreConnectivity.js +45 -0
- package/lib/sync/datastoreConnectivity.js.map +1 -1
- package/lib/sync/index.d.ts +27 -4
- package/lib/sync/index.js +354 -210
- package/lib/sync/index.js.map +1 -1
- package/lib/sync/merger.d.ts +9 -3
- package/lib/sync/merger.js +13 -6
- package/lib/sync/merger.js.map +1 -1
- package/lib/sync/outbox.d.ts +2 -2
- package/lib/sync/outbox.js +77 -71
- package/lib/sync/outbox.js.map +1 -1
- package/lib/sync/processors/mutation.d.ts +2 -0
- package/lib/sync/processors/mutation.js +256 -200
- package/lib/sync/processors/mutation.js.map +1 -1
- package/lib/sync/processors/subscription.d.ts +2 -0
- package/lib/sync/processors/subscription.js +212 -171
- package/lib/sync/processors/subscription.js.map +1 -1
- package/lib/sync/processors/sync.d.ts +2 -1
- package/lib/sync/processors/sync.js +88 -67
- package/lib/sync/processors/sync.js.map +1 -1
- package/lib/sync/utils.d.ts +3 -2
- package/lib/sync/utils.js +59 -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/authModeStrategies/multiAuthStrategy.d.ts +11 -0
- package/lib-esm/authModeStrategies/multiAuthStrategy.js +11 -0
- package/lib-esm/authModeStrategies/multiAuthStrategy.js.map +1 -1
- package/lib-esm/datastore/datastore.d.ts +91 -17
- package/lib-esm/datastore/datastore.js +537 -334
- 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 +93 -28
- package/lib-esm/storage/storage.js.map +1 -1
- package/lib-esm/sync/datastoreConnectivity.d.ts +1 -0
- package/lib-esm/sync/datastoreConnectivity.js +45 -0
- package/lib-esm/sync/datastoreConnectivity.js.map +1 -1
- package/lib-esm/sync/index.d.ts +27 -4
- package/lib-esm/sync/index.js +357 -213
- package/lib-esm/sync/index.js.map +1 -1
- package/lib-esm/sync/merger.d.ts +9 -3
- package/lib-esm/sync/merger.js +13 -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 +78 -72
- package/lib-esm/sync/outbox.js.map +1 -1
- package/lib-esm/sync/processors/mutation.d.ts +2 -0
- package/lib-esm/sync/processors/mutation.js +258 -202
- package/lib-esm/sync/processors/mutation.js.map +1 -1
- package/lib-esm/sync/processors/subscription.d.ts +2 -0
- package/lib-esm/sync/processors/subscription.js +213 -172
- package/lib-esm/sync/processors/subscription.js.map +1 -1
- package/lib-esm/sync/processors/sync.d.ts +2 -1
- package/lib-esm/sync/processors/sync.js +89 -68
- 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 +60 -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/not-tsconfig.json +32 -0
- package/package.json +7 -7
- package/src/authModeStrategies/multiAuthStrategy.ts +11 -0
- package/src/datastore/datastore.ts +672 -391
- 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 +69 -22
- package/src/sync/datastoreConnectivity.ts +6 -0
- package/src/sync/index.ts +473 -353
- package/src/sync/merger.ts +20 -4
- package/src/sync/outbox.ts +22 -9
- package/src/sync/processors/mutation.ts +194 -149
- package/src/sync/processors/subscription.ts +279 -246
- package/src/sync/processors/sync.ts +162 -138
- package/src/sync/utils.ts +67 -12
- package/src/types.ts +181 -29
- package/src/util.ts +415 -176
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
|
}
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
RelationType,
|
|
23
23
|
} from '../../types';
|
|
24
24
|
import {
|
|
25
|
+
DEFAULT_PRIMARY_KEY_VALUE_SEPARATOR,
|
|
25
26
|
exhaustiveCheck,
|
|
26
27
|
getIndex,
|
|
27
28
|
getIndexFromAssociation,
|
|
@@ -29,6 +30,11 @@ import {
|
|
|
29
30
|
traverseModel,
|
|
30
31
|
validatePredicate,
|
|
31
32
|
sortCompareFunction,
|
|
33
|
+
keysEqual,
|
|
34
|
+
getStorename,
|
|
35
|
+
getIndexKeys,
|
|
36
|
+
extractPrimaryKeyValues,
|
|
37
|
+
IDENTIFIER_KEY_SEPARATOR,
|
|
32
38
|
} from '../../util';
|
|
33
39
|
|
|
34
40
|
const logger = new Logger('DataStore');
|
|
@@ -52,13 +58,29 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
52
58
|
const namespace = this.namespaceResolver(modelConstructor);
|
|
53
59
|
const { name: modelName } = modelConstructor;
|
|
54
60
|
|
|
55
|
-
return
|
|
61
|
+
return getStorename(namespace, modelName);
|
|
56
62
|
}
|
|
57
63
|
|
|
58
|
-
|
|
59
|
-
|
|
64
|
+
// Retrieves primary key values from a model
|
|
65
|
+
private getIndexKeyValuesFromModel<T extends PersistentModel>(
|
|
66
|
+
model: T
|
|
67
|
+
): string[] {
|
|
68
|
+
const modelConstructor = Object.getPrototypeOf(model)
|
|
69
|
+
.constructor as PersistentModelConstructor<T>;
|
|
70
|
+
const namespaceName = this.namespaceResolver(modelConstructor);
|
|
71
|
+
const keys = getIndexKeys(
|
|
72
|
+
this.schema.namespaces[namespaceName],
|
|
73
|
+
modelConstructor.name
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
return extractPrimaryKeyValues(model, keys);
|
|
77
|
+
}
|
|
60
78
|
|
|
61
|
-
|
|
79
|
+
// Retrieves concatenated primary key values from a model
|
|
80
|
+
private getIndexKeyValuesPath<T extends PersistentModel>(model: T): string {
|
|
81
|
+
return this.getIndexKeyValuesFromModel(model).join(
|
|
82
|
+
DEFAULT_PRIMARY_KEY_VALUE_SEPARATOR
|
|
83
|
+
);
|
|
62
84
|
}
|
|
63
85
|
|
|
64
86
|
async setUp(
|
|
@@ -101,23 +123,32 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
101
123
|
const modelConstructor = Object.getPrototypeOf(model)
|
|
102
124
|
.constructor as PersistentModelConstructor<T>;
|
|
103
125
|
const storeName = this.getStorenameForModel(modelConstructor);
|
|
126
|
+
|
|
127
|
+
const namespaceName = this.namespaceResolver(modelConstructor);
|
|
128
|
+
|
|
104
129
|
const connectedModels = traverseModel(
|
|
105
130
|
modelConstructor.name,
|
|
106
131
|
model,
|
|
107
|
-
this.schema.namespaces[
|
|
132
|
+
this.schema.namespaces[namespaceName],
|
|
108
133
|
this.modelInstanceCreator,
|
|
109
134
|
this.getModelConstructorByModelName
|
|
110
135
|
);
|
|
111
|
-
|
|
136
|
+
|
|
112
137
|
const set = new Set<string>();
|
|
113
138
|
const connectionStoreNames = Object.values(connectedModels).map(
|
|
114
139
|
({ modelName, item, instance }) => {
|
|
115
|
-
const storeName =
|
|
140
|
+
const storeName = getStorename(namespaceName, modelName);
|
|
116
141
|
set.add(storeName);
|
|
117
|
-
|
|
142
|
+
const keys = getIndexKeys(
|
|
143
|
+
this.schema.namespaces[namespaceName],
|
|
144
|
+
modelName
|
|
145
|
+
);
|
|
146
|
+
return { storeName, item, instance, keys };
|
|
118
147
|
}
|
|
119
148
|
);
|
|
120
|
-
const
|
|
149
|
+
const keyValuesPath = this.getIndexKeyValuesPath(model);
|
|
150
|
+
|
|
151
|
+
const fromDB = await this.db.get(keyValuesPath, storeName);
|
|
121
152
|
|
|
122
153
|
if (condition && fromDB) {
|
|
123
154
|
const predicates = ModelPredicateCreator.getPredicates(condition);
|
|
@@ -136,14 +167,24 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
136
167
|
const result: [T, OpType.INSERT | OpType.UPDATE][] = [];
|
|
137
168
|
|
|
138
169
|
for await (const resItem of connectionStoreNames) {
|
|
139
|
-
const { storeName, item, instance } = resItem;
|
|
140
|
-
|
|
170
|
+
const { storeName, item, instance, keys } = resItem;
|
|
171
|
+
|
|
172
|
+
/* Find the key values in the item, and concatenate them */
|
|
173
|
+
const itemKeyValues: string[] = keys.map(key => item[key]);
|
|
174
|
+
const itemKeyValuesPath: string = itemKeyValues.join(
|
|
175
|
+
DEFAULT_PRIMARY_KEY_VALUE_SEPARATOR
|
|
176
|
+
);
|
|
141
177
|
|
|
142
|
-
const fromDB = <T>await this.db.get(
|
|
178
|
+
const fromDB = <T>await this.db.get(itemKeyValuesPath, storeName);
|
|
143
179
|
const opType: OpType = fromDB ? OpType.UPDATE : OpType.INSERT;
|
|
180
|
+
const modelKeyValues = this.getIndexKeyValuesFromModel(model);
|
|
144
181
|
|
|
145
|
-
|
|
146
|
-
|
|
182
|
+
// If item key values and model key values are equal, save to db
|
|
183
|
+
if (
|
|
184
|
+
keysEqual(itemKeyValues, modelKeyValues) ||
|
|
185
|
+
opType === OpType.INSERT
|
|
186
|
+
) {
|
|
187
|
+
await this.db.save(item, storeName, keys, itemKeyValuesPath);
|
|
147
188
|
|
|
148
189
|
result.push([instance, opType]);
|
|
149
190
|
}
|
|
@@ -160,7 +201,7 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
160
201
|
const namespace = this.schema.namespaces[namespaceName];
|
|
161
202
|
const relations = namespace.relationships[srcModelName].relationTypes;
|
|
162
203
|
const connectionStoreNames = relations.map(({ modelName }) => {
|
|
163
|
-
return
|
|
204
|
+
return getStorename(namespaceName, modelName);
|
|
164
205
|
});
|
|
165
206
|
const modelConstructor = this.getModelConstructorByModelName(
|
|
166
207
|
namespaceName,
|
|
@@ -174,8 +215,9 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
174
215
|
}
|
|
175
216
|
|
|
176
217
|
for await (const relation of relations) {
|
|
177
|
-
const { fieldName, modelName, targetName, relationType } =
|
|
178
|
-
|
|
218
|
+
const { fieldName, modelName, targetName, targetNames, relationType } =
|
|
219
|
+
relation;
|
|
220
|
+
const storeName = getStorename(namespaceName, modelName);
|
|
179
221
|
const modelConstructor = this.getModelConstructorByModelName(
|
|
180
222
|
namespaceName,
|
|
181
223
|
modelName
|
|
@@ -184,27 +226,81 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
184
226
|
switch (relationType) {
|
|
185
227
|
case 'HAS_ONE':
|
|
186
228
|
for await (const recordItem of records) {
|
|
187
|
-
|
|
188
|
-
if (
|
|
229
|
+
// ASYNC CPK TODO: make this cleaner
|
|
230
|
+
if (targetNames?.length) {
|
|
231
|
+
let getByFields = [];
|
|
232
|
+
let allPresent;
|
|
233
|
+
// iterate through all targetnames to make sure they are all present in the recordItem
|
|
234
|
+
allPresent = targetNames.every(targetName => {
|
|
235
|
+
return recordItem[targetName] != null;
|
|
236
|
+
});
|
|
189
237
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
238
|
+
if (!allPresent) {
|
|
239
|
+
break;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
getByFields = targetNames as any;
|
|
243
|
+
|
|
244
|
+
// keys are the key values
|
|
245
|
+
const keys = getByFields
|
|
246
|
+
.map(getByField => recordItem[getByField])
|
|
247
|
+
.join(DEFAULT_PRIMARY_KEY_VALUE_SEPARATOR);
|
|
248
|
+
|
|
249
|
+
const connectionRecord = await this.db.get(keys, storeName);
|
|
194
250
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
251
|
+
recordItem[fieldName] =
|
|
252
|
+
connectionRecord &&
|
|
253
|
+
this.modelInstanceCreator(modelConstructor, connectionRecord);
|
|
254
|
+
} else {
|
|
255
|
+
const getByfield = recordItem[targetName]
|
|
256
|
+
? targetName
|
|
257
|
+
: fieldName;
|
|
258
|
+
if (!recordItem[getByfield]) break;
|
|
259
|
+
|
|
260
|
+
const key = recordItem[getByfield];
|
|
261
|
+
|
|
262
|
+
const connectionRecord = await this.db.get(key, storeName);
|
|
263
|
+
|
|
264
|
+
recordItem[fieldName] =
|
|
265
|
+
connectionRecord &&
|
|
266
|
+
this.modelInstanceCreator(modelConstructor, connectionRecord);
|
|
267
|
+
}
|
|
198
268
|
}
|
|
199
269
|
|
|
200
270
|
break;
|
|
201
271
|
case 'BELONGS_TO':
|
|
202
272
|
for await (const recordItem of records) {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
273
|
+
// ASYNC CPK TODO: make this cleaner
|
|
274
|
+
if (targetNames?.length) {
|
|
275
|
+
let allPresent;
|
|
276
|
+
// iterate through all targetnames to make sure they are all present in the recordItem
|
|
277
|
+
allPresent = targetNames.every(targetName => {
|
|
278
|
+
return recordItem[targetName] != null;
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// If not present, there is not yet a connected record
|
|
282
|
+
if (!allPresent) {
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const keys = targetNames
|
|
287
|
+
.map(targetName => recordItem[targetName])
|
|
288
|
+
.join(DEFAULT_PRIMARY_KEY_VALUE_SEPARATOR);
|
|
289
|
+
|
|
290
|
+
// Retrieve the connected record
|
|
291
|
+
const connectionRecord = await this.db.get(keys, storeName);
|
|
292
|
+
|
|
293
|
+
recordItem[fieldName] =
|
|
294
|
+
connectionRecord &&
|
|
295
|
+
this.modelInstanceCreator(modelConstructor, connectionRecord);
|
|
296
|
+
|
|
297
|
+
targetNames?.map(targetName => {
|
|
298
|
+
delete recordItem[targetName];
|
|
299
|
+
});
|
|
300
|
+
} else if (recordItem[targetName as any]) {
|
|
301
|
+
const key = recordItem[targetName];
|
|
302
|
+
|
|
303
|
+
const connectionRecord = await this.db.get(key, storeName);
|
|
208
304
|
|
|
209
305
|
recordItem[fieldName] =
|
|
210
306
|
connectionRecord &&
|
|
@@ -238,13 +334,19 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
238
334
|
|
|
239
335
|
const predicates =
|
|
240
336
|
predicate && ModelPredicateCreator.getPredicates(predicate);
|
|
241
|
-
const
|
|
337
|
+
const keys = getIndexKeys(
|
|
338
|
+
this.schema.namespaces[namespaceName],
|
|
339
|
+
modelConstructor.name
|
|
340
|
+
);
|
|
341
|
+
const queryByKey =
|
|
342
|
+
predicates && this.keyValueFromPredicate(predicates, keys);
|
|
343
|
+
|
|
242
344
|
const hasSort = pagination && pagination.sort;
|
|
243
345
|
const hasPagination = pagination && pagination.limit;
|
|
244
346
|
|
|
245
347
|
const records: T[] = await (async () => {
|
|
246
|
-
if (
|
|
247
|
-
const record = await this.
|
|
348
|
+
if (queryByKey) {
|
|
349
|
+
const record = await this.getByKey(storeName, queryByKey);
|
|
248
350
|
return record ? [record] : [];
|
|
249
351
|
}
|
|
250
352
|
|
|
@@ -264,11 +366,11 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
264
366
|
return await this.load(namespaceName, modelConstructor.name, records);
|
|
265
367
|
}
|
|
266
368
|
|
|
267
|
-
private async
|
|
369
|
+
private async getByKey<T extends PersistentModel>(
|
|
268
370
|
storeName: string,
|
|
269
|
-
|
|
371
|
+
keyValuePath: string
|
|
270
372
|
): Promise<T> {
|
|
271
|
-
const record = <T>await this.db.get(
|
|
373
|
+
const record = <T>await this.db.get(keyValuePath, storeName);
|
|
272
374
|
return record;
|
|
273
375
|
}
|
|
274
376
|
|
|
@@ -278,17 +380,29 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
278
380
|
return await this.db.getAll(storeName);
|
|
279
381
|
}
|
|
280
382
|
|
|
281
|
-
private
|
|
282
|
-
predicates: PredicatesGroup<T
|
|
283
|
-
|
|
383
|
+
private keyValueFromPredicate<T extends PersistentModel>(
|
|
384
|
+
predicates: PredicatesGroup<T>,
|
|
385
|
+
keys: string[]
|
|
386
|
+
): string | undefined {
|
|
284
387
|
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
388
|
|
|
291
|
-
|
|
389
|
+
if (predicateObjs.length !== keys.length) {
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const keyValues = [];
|
|
394
|
+
|
|
395
|
+
for (const key of keys) {
|
|
396
|
+
const predicateObj = predicateObjs.find(
|
|
397
|
+
p => isPredicateObj(p) && p.field === key && p.operator === 'eq'
|
|
398
|
+
) as PredicateObject<T>;
|
|
399
|
+
|
|
400
|
+
predicateObj && keyValues.push(predicateObj.operand);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
return keyValues.length === keys.length
|
|
404
|
+
? keyValues.join(DEFAULT_PRIMARY_KEY_VALUE_SEPARATOR)
|
|
405
|
+
: undefined;
|
|
292
406
|
}
|
|
293
407
|
|
|
294
408
|
private async filterOnPredicate<T extends PersistentModel>(
|
|
@@ -328,6 +442,7 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
328
442
|
|
|
329
443
|
return records.slice(start, end);
|
|
330
444
|
}
|
|
445
|
+
|
|
331
446
|
return records;
|
|
332
447
|
}
|
|
333
448
|
|
|
@@ -337,6 +452,7 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
337
452
|
): Promise<T | undefined> {
|
|
338
453
|
const storeName = this.getStorenameForModel(modelConstructor);
|
|
339
454
|
const result = <T>await this.db.getOne(firstOrLast, storeName);
|
|
455
|
+
|
|
340
456
|
return result && this.modelInstanceCreator(modelConstructor, result);
|
|
341
457
|
}
|
|
342
458
|
|
|
@@ -372,6 +488,7 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
372
488
|
(acc, { items }) => acc.concat(items),
|
|
373
489
|
<T[]>[]
|
|
374
490
|
);
|
|
491
|
+
|
|
375
492
|
return [models, deletedModels];
|
|
376
493
|
} else {
|
|
377
494
|
await this.deleteTraverse(
|
|
@@ -396,12 +513,14 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
396
513
|
|
|
397
514
|
const modelConstructor = Object.getPrototypeOf(model)
|
|
398
515
|
.constructor as PersistentModelConstructor<T>;
|
|
399
|
-
const
|
|
516
|
+
const namespaceName = this.namespaceResolver(modelConstructor);
|
|
400
517
|
|
|
401
518
|
const storeName = this.getStorenameForModel(modelConstructor);
|
|
402
519
|
|
|
403
520
|
if (condition) {
|
|
404
|
-
const
|
|
521
|
+
const keyValuePath = this.getIndexKeyValuesPath(model);
|
|
522
|
+
|
|
523
|
+
const fromDB = await this.db.get(keyValuePath, storeName);
|
|
405
524
|
|
|
406
525
|
if (fromDB === undefined) {
|
|
407
526
|
const msg = 'Model instance not found in storage';
|
|
@@ -422,25 +541,28 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
422
541
|
}
|
|
423
542
|
|
|
424
543
|
const relations =
|
|
425
|
-
this.schema.namespaces[
|
|
426
|
-
.
|
|
544
|
+
this.schema.namespaces[namespaceName].relationships[
|
|
545
|
+
modelConstructor.name
|
|
546
|
+
].relationTypes;
|
|
547
|
+
|
|
427
548
|
await this.deleteTraverse(
|
|
428
549
|
relations,
|
|
429
550
|
[model],
|
|
430
551
|
modelConstructor.name,
|
|
431
|
-
|
|
552
|
+
namespaceName,
|
|
432
553
|
deleteQueue
|
|
433
554
|
);
|
|
434
555
|
} else {
|
|
435
556
|
const relations =
|
|
436
|
-
this.schema.namespaces[
|
|
437
|
-
.
|
|
557
|
+
this.schema.namespaces[namespaceName].relationships[
|
|
558
|
+
modelConstructor.name
|
|
559
|
+
].relationTypes;
|
|
438
560
|
|
|
439
561
|
await this.deleteTraverse(
|
|
440
562
|
relations,
|
|
441
563
|
[model],
|
|
442
564
|
modelConstructor.name,
|
|
443
|
-
|
|
565
|
+
namespaceName,
|
|
444
566
|
deleteQueue
|
|
445
567
|
);
|
|
446
568
|
}
|
|
@@ -465,8 +587,8 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
465
587
|
for await (const item of items) {
|
|
466
588
|
if (item) {
|
|
467
589
|
if (typeof item === 'object') {
|
|
468
|
-
const
|
|
469
|
-
await this.db.delete(
|
|
590
|
+
const keyValuesPath: string = this.getIndexKeyValuesPath(item as T);
|
|
591
|
+
await this.db.delete(keyValuesPath, storeName);
|
|
470
592
|
}
|
|
471
593
|
}
|
|
472
594
|
}
|
|
@@ -488,10 +610,16 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
488
610
|
deleteQueue: { storeName: string; items: T[] }[]
|
|
489
611
|
): Promise<void> {
|
|
490
612
|
for await (const rel of relations) {
|
|
491
|
-
const {
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
613
|
+
const {
|
|
614
|
+
relationType,
|
|
615
|
+
modelName,
|
|
616
|
+
targetName,
|
|
617
|
+
targetNames,
|
|
618
|
+
associatedWith,
|
|
619
|
+
} = rel;
|
|
620
|
+
const storeName = getStorename(nameSpace, modelName);
|
|
621
|
+
|
|
622
|
+
const index: string | undefined =
|
|
495
623
|
getIndex(
|
|
496
624
|
this.schema.namespaces[nameSpace].relationships[modelName]
|
|
497
625
|
.relationTypes,
|
|
@@ -508,35 +636,120 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
508
636
|
switch (relationType) {
|
|
509
637
|
case 'HAS_ONE':
|
|
510
638
|
for await (const model of models) {
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
639
|
+
if (targetNames && targetNames?.length) {
|
|
640
|
+
let hasOneIndex;
|
|
641
|
+
|
|
642
|
+
if (index) {
|
|
643
|
+
hasOneIndex = index.split(IDENTIFIER_KEY_SEPARATOR);
|
|
644
|
+
} else if (associatedWith) {
|
|
645
|
+
if (Array.isArray(associatedWith)) {
|
|
646
|
+
hasOneIndex = associatedWith;
|
|
647
|
+
} else {
|
|
648
|
+
hasOneIndex = [associatedWith];
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// iterate over targetNames array and see if each key is present in model object
|
|
653
|
+
// targetNames here being the keys for the CHILD model
|
|
654
|
+
const hasConnectedModelFields = targetNames.every(targetName =>
|
|
655
|
+
model.hasOwnProperty(targetName)
|
|
656
|
+
);
|
|
521
657
|
|
|
522
|
-
|
|
523
|
-
this.
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
658
|
+
// PK / Composite key for the parent model
|
|
659
|
+
const keyValuesPath: string = this.getIndexKeyValuesPath(model);
|
|
660
|
+
|
|
661
|
+
let values;
|
|
662
|
+
|
|
663
|
+
const isUnidirectionalConnection = hasOneIndex === associatedWith;
|
|
664
|
+
|
|
665
|
+
if (hasConnectedModelFields && isUnidirectionalConnection) {
|
|
666
|
+
// Values will be that of the child model
|
|
667
|
+
values = targetNames.map(
|
|
668
|
+
targetName => model[targetName]
|
|
669
|
+
) as any;
|
|
670
|
+
} else {
|
|
671
|
+
// values will be that of the parent model
|
|
672
|
+
values = keyValuesPath.split(
|
|
673
|
+
DEFAULT_PRIMARY_KEY_VALUE_SEPARATOR
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
if (values.length === 0) break;
|
|
678
|
+
|
|
679
|
+
const allRecords = await this.db.getAll(storeName);
|
|
680
|
+
|
|
681
|
+
let recordToDelete;
|
|
682
|
+
|
|
683
|
+
// values === targetNames
|
|
684
|
+
if (hasConnectedModelFields) {
|
|
685
|
+
/**
|
|
686
|
+
* Retrieve record by finding the record where all
|
|
687
|
+
* targetNames are present on the connected model.
|
|
688
|
+
*
|
|
689
|
+
*/
|
|
690
|
+
// recordToDelete = allRecords.filter(childItem =>
|
|
691
|
+
// values.every(value => childItem[value] != null)
|
|
692
|
+
// ) as T[];
|
|
693
|
+
|
|
694
|
+
recordToDelete = allRecords.filter(childItem =>
|
|
695
|
+
hasOneIndex.every(index => values.includes(childItem[index]))
|
|
696
|
+
);
|
|
697
|
+
} else {
|
|
698
|
+
// values === keyValuePath
|
|
699
|
+
recordToDelete = allRecords.filter(
|
|
700
|
+
childItem => childItem[hasOneIndex] === values
|
|
701
|
+
) as T[];
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
await this.deleteTraverse<T>(
|
|
705
|
+
this.schema.namespaces[nameSpace].relationships[modelName]
|
|
706
|
+
.relationTypes,
|
|
707
|
+
recordToDelete,
|
|
708
|
+
modelName,
|
|
709
|
+
nameSpace,
|
|
710
|
+
deleteQueue
|
|
711
|
+
);
|
|
712
|
+
} else {
|
|
713
|
+
const hasOneIndex = index || associatedWith;
|
|
714
|
+
const hasOneCustomField = targetName in model;
|
|
715
|
+
const keyValuesPath: string = this.getIndexKeyValuesPath(model);
|
|
716
|
+
const value = hasOneCustomField
|
|
717
|
+
? model[targetName]
|
|
718
|
+
: keyValuesPath;
|
|
719
|
+
|
|
720
|
+
if (!value) break;
|
|
721
|
+
|
|
722
|
+
const allRecords = await this.db.getAll(storeName);
|
|
723
|
+
|
|
724
|
+
const recordToDelete = allRecords.filter(
|
|
725
|
+
childItem => childItem[hasOneIndex as string] === value
|
|
726
|
+
) as T[];
|
|
727
|
+
|
|
728
|
+
await this.deleteTraverse<T>(
|
|
729
|
+
this.schema.namespaces[nameSpace].relationships[modelName]
|
|
730
|
+
.relationTypes,
|
|
731
|
+
recordToDelete,
|
|
732
|
+
modelName,
|
|
733
|
+
nameSpace,
|
|
734
|
+
deleteQueue
|
|
735
|
+
);
|
|
736
|
+
}
|
|
530
737
|
}
|
|
531
738
|
break;
|
|
532
739
|
case 'HAS_MANY':
|
|
533
740
|
for await (const model of models) {
|
|
741
|
+
// Key values for the parent model:
|
|
742
|
+
const keyValues: string[] = this.getIndexKeyValuesFromModel(model);
|
|
743
|
+
|
|
534
744
|
const allRecords = await this.db.getAll(storeName);
|
|
535
|
-
const childrenArray = allRecords.filter(
|
|
536
|
-
childItem => childItem[index] === model.id
|
|
537
|
-
);
|
|
538
745
|
|
|
539
|
-
|
|
746
|
+
const indices = index.split(IDENTIFIER_KEY_SEPARATOR);
|
|
747
|
+
|
|
748
|
+
const childrenArray = allRecords.filter(childItem =>
|
|
749
|
+
indices.every(index => keyValues.includes(childItem[index]))
|
|
750
|
+
) as T[];
|
|
751
|
+
|
|
752
|
+
await this.deleteTraverse<T>(
|
|
540
753
|
this.schema.namespaces[nameSpace].relationships[modelName]
|
|
541
754
|
.relationTypes,
|
|
542
755
|
childrenArray,
|
|
@@ -556,7 +769,7 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
556
769
|
}
|
|
557
770
|
|
|
558
771
|
deleteQueue.push({
|
|
559
|
-
storeName:
|
|
772
|
+
storeName: getStorename(nameSpace, srcModel),
|
|
560
773
|
items: models.map(record =>
|
|
561
774
|
this.modelInstanceCreator(
|
|
562
775
|
this.getModelConstructorByModelName(nameSpace, srcModel),
|
|
@@ -579,29 +792,32 @@ export class AsyncStorageAdapter implements Adapter {
|
|
|
579
792
|
): Promise<[T, OpType][]> {
|
|
580
793
|
const { name: modelName } = modelConstructor;
|
|
581
794
|
const namespaceName = this.namespaceResolver(modelConstructor);
|
|
582
|
-
const storeName =
|
|
583
|
-
|
|
795
|
+
const storeName = getStorename(namespaceName, modelName);
|
|
796
|
+
const keys = getIndexKeys(this.schema.namespaces[namespaceName], modelName);
|
|
584
797
|
const batch: ModelInstanceMetadata[] = [];
|
|
585
798
|
|
|
586
799
|
for (const item of items) {
|
|
587
|
-
const
|
|
800
|
+
const model = this.modelInstanceCreator(modelConstructor, item);
|
|
588
801
|
|
|
589
802
|
const connectedModels = traverseModel(
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
this.schema.namespaces[
|
|
803
|
+
modelName,
|
|
804
|
+
model,
|
|
805
|
+
this.schema.namespaces[namespaceName],
|
|
593
806
|
this.modelInstanceCreator,
|
|
594
807
|
this.getModelConstructorByModelName
|
|
595
808
|
);
|
|
596
809
|
|
|
597
|
-
const
|
|
598
|
-
|
|
599
|
-
)
|
|
810
|
+
const keyValuesPath = this.getIndexKeyValuesPath(model);
|
|
811
|
+
|
|
812
|
+
const { instance } = connectedModels.find(({ instance }) => {
|
|
813
|
+
const instanceKeyValuesPath = this.getIndexKeyValuesPath(instance);
|
|
814
|
+
return keysEqual([instanceKeyValuesPath], [keyValuesPath]);
|
|
815
|
+
});
|
|
600
816
|
|
|
601
817
|
batch.push(instance);
|
|
602
818
|
}
|
|
603
819
|
|
|
604
|
-
return await this.db.batchSave(storeName, batch);
|
|
820
|
+
return await this.db.batchSave(storeName, batch, keys);
|
|
605
821
|
}
|
|
606
822
|
}
|
|
607
823
|
|