@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
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
import { ConsoleLogger as Logger } from '@aws-amplify/core';
|
|
2
|
+
import { Adapter } from './index';
|
|
3
|
+
import { ModelInstanceCreator } from '../../datastore/datastore';
|
|
4
|
+
import { ModelPredicateCreator } from '../../predicates';
|
|
5
|
+
import {
|
|
6
|
+
InternalSchema,
|
|
7
|
+
isPredicateObj,
|
|
8
|
+
ModelInstanceMetadata,
|
|
9
|
+
ModelPredicate,
|
|
10
|
+
NamespaceResolver,
|
|
11
|
+
OpType,
|
|
12
|
+
PaginationInput,
|
|
13
|
+
PersistentModel,
|
|
14
|
+
PersistentModelConstructor,
|
|
15
|
+
PredicateObject,
|
|
16
|
+
PredicatesGroup,
|
|
17
|
+
QueryOne,
|
|
18
|
+
} from '../../types';
|
|
19
|
+
import {
|
|
20
|
+
NAMESPACES,
|
|
21
|
+
getStorename,
|
|
22
|
+
getIndexKeys,
|
|
23
|
+
extractPrimaryKeyValues,
|
|
24
|
+
traverseModel,
|
|
25
|
+
validatePredicate,
|
|
26
|
+
isModelConstructor,
|
|
27
|
+
extractPrimaryKeyFieldNames,
|
|
28
|
+
} from '../../util';
|
|
29
|
+
import type { IDBPDatabase, IDBPObjectStore } from 'idb';
|
|
30
|
+
import type AsyncStorageDatabase from './AsyncStorageDatabase';
|
|
31
|
+
import { ModelRelationship } from '../relationship';
|
|
32
|
+
|
|
33
|
+
const logger = new Logger('DataStore');
|
|
34
|
+
const DB_NAME = 'amplify-datastore';
|
|
35
|
+
|
|
36
|
+
export abstract class StorageAdapterBase implements Adapter {
|
|
37
|
+
// Non-null assertions (bang operators) added to most properties to make TS happy.
|
|
38
|
+
// For now, we can be reasonably sure they're available when they're needed, because
|
|
39
|
+
// the adapter is not used directly outside the library boundary.
|
|
40
|
+
protected schema!: InternalSchema;
|
|
41
|
+
protected namespaceResolver!: NamespaceResolver;
|
|
42
|
+
protected modelInstanceCreator!: ModelInstanceCreator;
|
|
43
|
+
protected getModelConstructorByModelName!: (
|
|
44
|
+
namsespaceName: NAMESPACES,
|
|
45
|
+
modelName: string
|
|
46
|
+
) => PersistentModelConstructor<any>;
|
|
47
|
+
protected initPromise!: Promise<void>;
|
|
48
|
+
protected resolve!: (value?: any) => void;
|
|
49
|
+
protected reject!: (value?: any) => void;
|
|
50
|
+
protected dbName: string = DB_NAME;
|
|
51
|
+
protected abstract db: IDBPDatabase | AsyncStorageDatabase;
|
|
52
|
+
|
|
53
|
+
protected abstract preSetUpChecks(): Promise<void>;
|
|
54
|
+
protected abstract preOpCheck(): Promise<void>;
|
|
55
|
+
protected abstract initDb(): Promise<IDBPDatabase | AsyncStorageDatabase>;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Initializes local DB
|
|
59
|
+
*
|
|
60
|
+
* @param theSchema
|
|
61
|
+
* @param namespaceResolver
|
|
62
|
+
* @param modelInstanceCreator
|
|
63
|
+
* @param getModelConstructorByModelName
|
|
64
|
+
* @param sessionId
|
|
65
|
+
*/
|
|
66
|
+
public async setUp(
|
|
67
|
+
theSchema: InternalSchema,
|
|
68
|
+
namespaceResolver: NamespaceResolver,
|
|
69
|
+
modelInstanceCreator: ModelInstanceCreator,
|
|
70
|
+
getModelConstructorByModelName: (
|
|
71
|
+
namsespaceName: NAMESPACES,
|
|
72
|
+
modelName: string
|
|
73
|
+
) => PersistentModelConstructor<any>,
|
|
74
|
+
sessionId?: string
|
|
75
|
+
): Promise<void> {
|
|
76
|
+
await this.preSetUpChecks();
|
|
77
|
+
|
|
78
|
+
if (!this.initPromise) {
|
|
79
|
+
this.initPromise = new Promise((res, rej) => {
|
|
80
|
+
this.resolve = res;
|
|
81
|
+
this.reject = rej;
|
|
82
|
+
});
|
|
83
|
+
} else {
|
|
84
|
+
await this.initPromise;
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (sessionId) {
|
|
88
|
+
this.dbName = `${DB_NAME}-${sessionId}`;
|
|
89
|
+
}
|
|
90
|
+
this.schema = theSchema;
|
|
91
|
+
this.namespaceResolver = namespaceResolver;
|
|
92
|
+
this.modelInstanceCreator = modelInstanceCreator;
|
|
93
|
+
this.getModelConstructorByModelName = getModelConstructorByModelName;
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
if (!this.db) {
|
|
97
|
+
this.db = await this.initDb();
|
|
98
|
+
this.resolve();
|
|
99
|
+
}
|
|
100
|
+
} catch (error) {
|
|
101
|
+
this.reject(error);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/*
|
|
106
|
+
* Abstract Methods for Adapter interface
|
|
107
|
+
* Not enough implementation similarities between the adapters
|
|
108
|
+
* to consolidate in the base class
|
|
109
|
+
*/
|
|
110
|
+
public abstract clear(): Promise<void>;
|
|
111
|
+
|
|
112
|
+
public abstract save<T extends PersistentModel>(
|
|
113
|
+
model: T,
|
|
114
|
+
condition?: ModelPredicate<T>
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
public abstract query<T extends PersistentModel>(
|
|
118
|
+
modelConstructor: PersistentModelConstructor<T>,
|
|
119
|
+
predicate?: ModelPredicate<T>,
|
|
120
|
+
pagination?: PaginationInput<T>
|
|
121
|
+
): Promise<T[]>;
|
|
122
|
+
|
|
123
|
+
public abstract queryOne<T extends PersistentModel>(
|
|
124
|
+
modelConstructor: PersistentModelConstructor<T>,
|
|
125
|
+
firstOrLast: QueryOne
|
|
126
|
+
): Promise<T | undefined>;
|
|
127
|
+
|
|
128
|
+
public abstract batchSave<T extends PersistentModel>(
|
|
129
|
+
modelConstructor: PersistentModelConstructor<any>,
|
|
130
|
+
items: ModelInstanceMetadata[]
|
|
131
|
+
): Promise<[T, OpType][]>;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @param modelConstructor
|
|
135
|
+
* @returns local DB table name
|
|
136
|
+
*/
|
|
137
|
+
protected getStorenameForModel(
|
|
138
|
+
modelConstructor: PersistentModelConstructor<any>
|
|
139
|
+
): string {
|
|
140
|
+
const namespace = this.namespaceResolver(modelConstructor);
|
|
141
|
+
const { name: modelName } = modelConstructor;
|
|
142
|
+
|
|
143
|
+
return getStorename(namespace, modelName);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
*
|
|
148
|
+
* @param model - instantiated model record
|
|
149
|
+
* @returns the record's primary key values
|
|
150
|
+
*/
|
|
151
|
+
protected getIndexKeyValuesFromModel<T extends PersistentModel>(
|
|
152
|
+
model: T
|
|
153
|
+
): string[] {
|
|
154
|
+
const modelConstructor = Object.getPrototypeOf(model)
|
|
155
|
+
.constructor as PersistentModelConstructor<T>;
|
|
156
|
+
const namespaceName = this.namespaceResolver(modelConstructor);
|
|
157
|
+
|
|
158
|
+
const keys = getIndexKeys(
|
|
159
|
+
this.schema.namespaces[namespaceName],
|
|
160
|
+
modelConstructor.name
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
return extractPrimaryKeyValues(model, keys);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Common metadata for `save` operation
|
|
168
|
+
* used by individual storage adapters
|
|
169
|
+
*
|
|
170
|
+
* @param model
|
|
171
|
+
*/
|
|
172
|
+
protected saveMetadata<T extends PersistentModel>(
|
|
173
|
+
model: T
|
|
174
|
+
): {
|
|
175
|
+
storeName: string;
|
|
176
|
+
set: Set<string>;
|
|
177
|
+
connectionStoreNames;
|
|
178
|
+
modelKeyValues: string[];
|
|
179
|
+
} {
|
|
180
|
+
const modelConstructor = Object.getPrototypeOf(model)
|
|
181
|
+
.constructor as PersistentModelConstructor<T>;
|
|
182
|
+
const storeName = this.getStorenameForModel(modelConstructor);
|
|
183
|
+
const namespaceName = this.namespaceResolver(modelConstructor);
|
|
184
|
+
|
|
185
|
+
const connectedModels = traverseModel(
|
|
186
|
+
modelConstructor.name,
|
|
187
|
+
model,
|
|
188
|
+
this.schema.namespaces[namespaceName],
|
|
189
|
+
this.modelInstanceCreator,
|
|
190
|
+
this.getModelConstructorByModelName!
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
const set = new Set<string>();
|
|
194
|
+
const connectionStoreNames = Object.values(connectedModels).map(
|
|
195
|
+
({ modelName, item, instance }) => {
|
|
196
|
+
const storeName = getStorename(namespaceName, modelName);
|
|
197
|
+
set.add(storeName);
|
|
198
|
+
const keys = getIndexKeys(
|
|
199
|
+
this.schema.namespaces[namespaceName],
|
|
200
|
+
modelName
|
|
201
|
+
);
|
|
202
|
+
return { storeName, item, instance, keys };
|
|
203
|
+
}
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
const modelKeyValues = this.getIndexKeyValuesFromModel(model);
|
|
207
|
+
|
|
208
|
+
return { storeName, set, connectionStoreNames, modelKeyValues };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Enforces conditional save. Throws if condition is not met.
|
|
213
|
+
* used by individual storage adapters
|
|
214
|
+
*
|
|
215
|
+
* @param model
|
|
216
|
+
*/
|
|
217
|
+
protected validateSaveCondition<T extends PersistentModel>(
|
|
218
|
+
condition?: ModelPredicate<T>,
|
|
219
|
+
fromDB?: unknown
|
|
220
|
+
): void {
|
|
221
|
+
if (!(condition && fromDB)) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const predicates = ModelPredicateCreator.getPredicates(condition);
|
|
226
|
+
const { predicates: predicateObjs, type } = predicates!;
|
|
227
|
+
|
|
228
|
+
const isValid = validatePredicate(fromDB, type, predicateObjs);
|
|
229
|
+
|
|
230
|
+
if (!isValid) {
|
|
231
|
+
const msg = 'Conditional update failed';
|
|
232
|
+
logger.error(msg, { model: fromDB, condition: predicateObjs });
|
|
233
|
+
|
|
234
|
+
throw new Error(msg);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
protected abstract _get<T>(
|
|
239
|
+
storeOrStoreName: IDBPObjectStore | string,
|
|
240
|
+
keyArr: string[]
|
|
241
|
+
): Promise<T>;
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Instantiate models from POJO records returned from the database
|
|
245
|
+
*
|
|
246
|
+
* @param namespaceName - string model namespace
|
|
247
|
+
* @param srcModelName - string model name
|
|
248
|
+
* @param records - array of uninstantiated records
|
|
249
|
+
* @returns
|
|
250
|
+
*/
|
|
251
|
+
protected async load<T>(
|
|
252
|
+
namespaceName: NAMESPACES,
|
|
253
|
+
srcModelName: string,
|
|
254
|
+
records: T[]
|
|
255
|
+
): Promise<T[]> {
|
|
256
|
+
const namespace = this.schema.namespaces[namespaceName];
|
|
257
|
+
const relations = namespace.relationships![srcModelName].relationTypes;
|
|
258
|
+
const connectionStoreNames = relations.map(({ modelName }) => {
|
|
259
|
+
return getStorename(namespaceName, modelName);
|
|
260
|
+
});
|
|
261
|
+
const modelConstructor = this.getModelConstructorByModelName!(
|
|
262
|
+
namespaceName,
|
|
263
|
+
srcModelName
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
if (connectionStoreNames.length === 0) {
|
|
267
|
+
return records.map(record =>
|
|
268
|
+
this.modelInstanceCreator(modelConstructor, record)
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return records.map(record =>
|
|
273
|
+
this.modelInstanceCreator(modelConstructor, record)
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Extracts operands from a predicate group into an array of key values
|
|
279
|
+
* Used in the query method
|
|
280
|
+
*
|
|
281
|
+
* @param predicates - predicate group
|
|
282
|
+
* @param keyPath - string array of key names ['id', 'sortKey']
|
|
283
|
+
* @returns string[] of key values
|
|
284
|
+
*
|
|
285
|
+
* @example
|
|
286
|
+
* ```js
|
|
287
|
+
* { and:[{ id: { eq: 'abc' }}, { sortKey: { eq: 'def' }}] }
|
|
288
|
+
* ```
|
|
289
|
+
* Becomes
|
|
290
|
+
* ```
|
|
291
|
+
* ['abc', 'def']
|
|
292
|
+
* ```
|
|
293
|
+
*/
|
|
294
|
+
private keyValueFromPredicate<T extends PersistentModel>(
|
|
295
|
+
predicates: PredicatesGroup<T>,
|
|
296
|
+
keyPath: string[]
|
|
297
|
+
): string[] | undefined {
|
|
298
|
+
const { predicates: predicateObjs } = predicates;
|
|
299
|
+
|
|
300
|
+
if (predicateObjs.length !== keyPath.length) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const keyValues = [] as any[];
|
|
305
|
+
|
|
306
|
+
for (const key of keyPath) {
|
|
307
|
+
const predicateObj = predicateObjs.find(
|
|
308
|
+
p =>
|
|
309
|
+
// it's a relevant predicate object only if it's an equality
|
|
310
|
+
// operation for a key field from the key:
|
|
311
|
+
isPredicateObj(p) &&
|
|
312
|
+
p.field === key &&
|
|
313
|
+
p.operator === 'eq' &&
|
|
314
|
+
p.operand !== null &&
|
|
315
|
+
p.operand !== undefined
|
|
316
|
+
) as PredicateObject<T>;
|
|
317
|
+
|
|
318
|
+
predicateObj && keyValues.push(predicateObj.operand);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return keyValues.length === keyPath.length ? keyValues : undefined;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Common metadata for `query` operation
|
|
326
|
+
* used by individual storage adapters
|
|
327
|
+
*
|
|
328
|
+
* @param modelConstructor
|
|
329
|
+
* @param predicate
|
|
330
|
+
* @param pagination
|
|
331
|
+
*/
|
|
332
|
+
protected queryMetadata<T extends PersistentModel>(
|
|
333
|
+
modelConstructor: PersistentModelConstructor<T>,
|
|
334
|
+
predicate?: ModelPredicate<T>,
|
|
335
|
+
pagination?: PaginationInput<T>
|
|
336
|
+
) {
|
|
337
|
+
const storeName = this.getStorenameForModel(modelConstructor);
|
|
338
|
+
const namespaceName = this.namespaceResolver(
|
|
339
|
+
modelConstructor
|
|
340
|
+
) as NAMESPACES;
|
|
341
|
+
|
|
342
|
+
const predicates =
|
|
343
|
+
predicate && ModelPredicateCreator.getPredicates(predicate);
|
|
344
|
+
const keyPath = getIndexKeys(
|
|
345
|
+
this.schema.namespaces[namespaceName],
|
|
346
|
+
modelConstructor.name
|
|
347
|
+
);
|
|
348
|
+
const queryByKey =
|
|
349
|
+
predicates && this.keyValueFromPredicate(predicates, keyPath);
|
|
350
|
+
|
|
351
|
+
const hasSort = pagination && pagination.sort;
|
|
352
|
+
const hasPagination = pagination && pagination.limit;
|
|
353
|
+
|
|
354
|
+
return {
|
|
355
|
+
storeName,
|
|
356
|
+
namespaceName,
|
|
357
|
+
queryByKey,
|
|
358
|
+
predicates,
|
|
359
|
+
hasSort,
|
|
360
|
+
hasPagination,
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Delete record
|
|
366
|
+
* Cascades to related records (for Has One and Has Many relationships)
|
|
367
|
+
*
|
|
368
|
+
* @param modelOrModelConstructor
|
|
369
|
+
* @param condition
|
|
370
|
+
* @returns
|
|
371
|
+
*/
|
|
372
|
+
public async delete<T extends PersistentModel>(
|
|
373
|
+
modelOrModelConstructor: T | PersistentModelConstructor<T>,
|
|
374
|
+
condition?: ModelPredicate<T>
|
|
375
|
+
): Promise<[T[], T[]]> {
|
|
376
|
+
await this.preOpCheck();
|
|
377
|
+
|
|
378
|
+
const deleteQueue: { storeName: string; items: T[] }[] = [];
|
|
379
|
+
|
|
380
|
+
if (isModelConstructor(modelOrModelConstructor)) {
|
|
381
|
+
const modelConstructor =
|
|
382
|
+
modelOrModelConstructor as PersistentModelConstructor<T>;
|
|
383
|
+
const namespace = this.namespaceResolver(modelConstructor) as NAMESPACES;
|
|
384
|
+
const models = await this.query(modelConstructor, condition);
|
|
385
|
+
|
|
386
|
+
if (condition !== undefined) {
|
|
387
|
+
await this.deleteTraverse(
|
|
388
|
+
models,
|
|
389
|
+
modelConstructor,
|
|
390
|
+
namespace,
|
|
391
|
+
deleteQueue
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
await this.deleteItem(deleteQueue);
|
|
395
|
+
|
|
396
|
+
const deletedModels = deleteQueue.reduce(
|
|
397
|
+
(acc, { items }) => acc.concat(items),
|
|
398
|
+
<T[]>[]
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
return [models, deletedModels];
|
|
402
|
+
} else {
|
|
403
|
+
await this.deleteTraverse(
|
|
404
|
+
models,
|
|
405
|
+
modelConstructor,
|
|
406
|
+
namespace,
|
|
407
|
+
deleteQueue
|
|
408
|
+
);
|
|
409
|
+
|
|
410
|
+
await this.deleteItem(deleteQueue);
|
|
411
|
+
|
|
412
|
+
const deletedModels = deleteQueue.reduce(
|
|
413
|
+
(acc, { items }) => acc.concat(items),
|
|
414
|
+
<T[]>[]
|
|
415
|
+
);
|
|
416
|
+
|
|
417
|
+
return [models, deletedModels];
|
|
418
|
+
}
|
|
419
|
+
} else {
|
|
420
|
+
const model = modelOrModelConstructor as T;
|
|
421
|
+
|
|
422
|
+
const modelConstructor = Object.getPrototypeOf(model)
|
|
423
|
+
.constructor as PersistentModelConstructor<T>;
|
|
424
|
+
|
|
425
|
+
const namespaceName = this.namespaceResolver(
|
|
426
|
+
modelConstructor
|
|
427
|
+
) as NAMESPACES;
|
|
428
|
+
|
|
429
|
+
const storeName = this.getStorenameForModel(modelConstructor);
|
|
430
|
+
|
|
431
|
+
if (condition) {
|
|
432
|
+
const keyValues = this.getIndexKeyValuesFromModel(model);
|
|
433
|
+
const fromDB = await this._get(storeName, keyValues);
|
|
434
|
+
|
|
435
|
+
if (fromDB === undefined) {
|
|
436
|
+
const msg = 'Model instance not found in storage';
|
|
437
|
+
logger.warn(msg, { model });
|
|
438
|
+
|
|
439
|
+
return [[model], []];
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const predicates = ModelPredicateCreator.getPredicates(condition);
|
|
443
|
+
const { predicates: predicateObjs, type } =
|
|
444
|
+
predicates as PredicatesGroup<T>;
|
|
445
|
+
|
|
446
|
+
const isValid = validatePredicate(fromDB as T, type, predicateObjs);
|
|
447
|
+
if (!isValid) {
|
|
448
|
+
const msg = 'Conditional update failed';
|
|
449
|
+
logger.error(msg, { model: fromDB, condition: predicateObjs });
|
|
450
|
+
|
|
451
|
+
throw new Error(msg);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
await this.deleteTraverse(
|
|
455
|
+
[model],
|
|
456
|
+
modelConstructor,
|
|
457
|
+
namespaceName,
|
|
458
|
+
deleteQueue
|
|
459
|
+
);
|
|
460
|
+
} else {
|
|
461
|
+
await this.deleteTraverse(
|
|
462
|
+
[model],
|
|
463
|
+
modelConstructor,
|
|
464
|
+
namespaceName,
|
|
465
|
+
deleteQueue
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
await this.deleteItem(deleteQueue);
|
|
469
|
+
|
|
470
|
+
const deletedModels = deleteQueue.reduce(
|
|
471
|
+
(acc, { items }) => acc.concat(items),
|
|
472
|
+
<T[]>[]
|
|
473
|
+
);
|
|
474
|
+
|
|
475
|
+
return [[model], deletedModels];
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
protected abstract deleteItem<T extends PersistentModel>(
|
|
480
|
+
deleteQueue?: {
|
|
481
|
+
storeName: string;
|
|
482
|
+
items: T[] | IDBValidKey[];
|
|
483
|
+
}[]
|
|
484
|
+
);
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Recursively traverse relationship graph and add
|
|
488
|
+
* all Has One and Has Many relations to `deleteQueue` param
|
|
489
|
+
*
|
|
490
|
+
* Actual deletion of records added to `deleteQueue` occurs in the `delete` method
|
|
491
|
+
*
|
|
492
|
+
* @param models
|
|
493
|
+
* @param modelConstructor
|
|
494
|
+
* @param namespace
|
|
495
|
+
* @param deleteQueue
|
|
496
|
+
*/
|
|
497
|
+
private async deleteTraverse<T extends PersistentModel>(
|
|
498
|
+
models: T[],
|
|
499
|
+
modelConstructor: PersistentModelConstructor<T>,
|
|
500
|
+
namespace: NAMESPACES,
|
|
501
|
+
deleteQueue: { storeName: string; items: T[] }[]
|
|
502
|
+
): Promise<void> {
|
|
503
|
+
const cascadingRelationTypes = ['HAS_ONE', 'HAS_MANY'];
|
|
504
|
+
|
|
505
|
+
for await (const model of models) {
|
|
506
|
+
const modelDefinition =
|
|
507
|
+
this.schema.namespaces[namespace].models[modelConstructor.name];
|
|
508
|
+
|
|
509
|
+
const modelMeta = {
|
|
510
|
+
builder: modelConstructor,
|
|
511
|
+
schema: modelDefinition,
|
|
512
|
+
pkField: extractPrimaryKeyFieldNames(modelDefinition),
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
const relationships = ModelRelationship.allFrom(modelMeta).filter(r =>
|
|
516
|
+
cascadingRelationTypes.includes(r.type)
|
|
517
|
+
);
|
|
518
|
+
|
|
519
|
+
for await (const r of relationships) {
|
|
520
|
+
const queryObject = r.createRemoteQueryObject(model);
|
|
521
|
+
if (queryObject !== null) {
|
|
522
|
+
const relatedRecords = await this.query(
|
|
523
|
+
r.remoteModelConstructor,
|
|
524
|
+
ModelPredicateCreator.createFromFlatEqualities(
|
|
525
|
+
r.remoteDefinition!,
|
|
526
|
+
queryObject
|
|
527
|
+
)
|
|
528
|
+
);
|
|
529
|
+
|
|
530
|
+
await this.deleteTraverse(
|
|
531
|
+
relatedRecords,
|
|
532
|
+
r.remoteModelConstructor,
|
|
533
|
+
namespace,
|
|
534
|
+
deleteQueue
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
deleteQueue.push({
|
|
541
|
+
storeName: getStorename(namespace, modelConstructor.name),
|
|
542
|
+
items: models,
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
}
|
|
@@ -173,6 +173,15 @@ export class ModelRelationship<T> {
|
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
+
/**
|
|
177
|
+
* The `remote` model's associated field's `assocation` metadata, if
|
|
178
|
+
* present.
|
|
179
|
+
*
|
|
180
|
+
* This is used when determining if the remote model's associated field
|
|
181
|
+
* specifies which FK fields to use. If this value is `undefined`, the
|
|
182
|
+
* name of the remote field (`this.localAssociatedWith`) *is* the remote
|
|
183
|
+
* key field.
|
|
184
|
+
*/
|
|
176
185
|
private get explicitRemoteAssociation() {
|
|
177
186
|
if (this.localAssociatedWith) {
|
|
178
187
|
if (this.localAssociatedWith.length === 1) {
|
package/src/storage/storage.ts
CHANGED
|
@@ -60,7 +60,7 @@ class StorageClass implements StorageFacade {
|
|
|
60
60
|
private readonly sessionId?: string
|
|
61
61
|
) {
|
|
62
62
|
this.adapter = this.adapter || getDefaultAdapter();
|
|
63
|
-
this.pushStream = new PushStream()
|
|
63
|
+
this.pushStream = new PushStream();
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
static getNamespace() {
|
|
@@ -123,7 +123,10 @@ class StorageClass implements StorageFacade {
|
|
|
123
123
|
let updateMutationInput;
|
|
124
124
|
// don't attempt to calc mutation input when storage.save
|
|
125
125
|
// is called by Merger, i.e., when processing an AppSync response
|
|
126
|
-
if (
|
|
126
|
+
if (
|
|
127
|
+
(opType === OpType.UPDATE || opType === OpType.INSERT) &&
|
|
128
|
+
!syncResponse
|
|
129
|
+
) {
|
|
127
130
|
//
|
|
128
131
|
// TODO: LOOK!!!
|
|
129
132
|
// the `model` used here is in effect regardless of what model
|
|
@@ -136,7 +139,7 @@ class StorageClass implements StorageFacade {
|
|
|
136
139
|
// depends on this remaining as-is.
|
|
137
140
|
//
|
|
138
141
|
|
|
139
|
-
updateMutationInput = this.
|
|
142
|
+
updateMutationInput = this.getChangedFieldsInput(
|
|
140
143
|
model,
|
|
141
144
|
savedElement,
|
|
142
145
|
patchesTuple
|
|
@@ -154,7 +157,7 @@ class StorageClass implements StorageFacade {
|
|
|
154
157
|
.constructor as PersistentModelConstructor<T>;
|
|
155
158
|
|
|
156
159
|
this.pushStream.next({
|
|
157
|
-
model: modelConstructor
|
|
160
|
+
model: modelConstructor,
|
|
158
161
|
opType,
|
|
159
162
|
element,
|
|
160
163
|
mutator,
|
|
@@ -234,7 +237,7 @@ class StorageClass implements StorageFacade {
|
|
|
234
237
|
}
|
|
235
238
|
|
|
236
239
|
this.pushStream.next({
|
|
237
|
-
model: modelConstructor
|
|
240
|
+
model: modelConstructor,
|
|
238
241
|
opType: OpType.DELETE,
|
|
239
242
|
element: model,
|
|
240
243
|
mutator,
|
|
@@ -340,11 +343,11 @@ class StorageClass implements StorageFacade {
|
|
|
340
343
|
});
|
|
341
344
|
});
|
|
342
345
|
|
|
343
|
-
return result
|
|
346
|
+
return result;
|
|
344
347
|
}
|
|
345
348
|
|
|
346
349
|
// returns null if no user fields were changed (determined by value comparison)
|
|
347
|
-
private
|
|
350
|
+
private getChangedFieldsInput<T extends PersistentModel>(
|
|
348
351
|
model: T,
|
|
349
352
|
originalElement: T,
|
|
350
353
|
patchesTuple?: [Patch[], PersistentModel]
|
|
@@ -510,11 +513,11 @@ class ExclusiveStorage implements StorageFacade {
|
|
|
510
513
|
if (isModelConstructor(modelOrModelConstructor)) {
|
|
511
514
|
const modelConstructor = modelOrModelConstructor;
|
|
512
515
|
|
|
513
|
-
return storage.delete(modelConstructor
|
|
516
|
+
return storage.delete(modelConstructor, condition, mutator);
|
|
514
517
|
} else {
|
|
515
518
|
const model = modelOrModelConstructor;
|
|
516
519
|
|
|
517
|
-
return storage.delete(model
|
|
520
|
+
return storage.delete(model, condition, mutator);
|
|
518
521
|
}
|
|
519
522
|
});
|
|
520
523
|
}
|