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