@aws-amplify/datastore 3.12.6-next.13 → 3.12.6-next.32
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 +58 -0
- package/lib/authModeStrategies/multiAuthStrategy.js +17 -64
- package/lib/authModeStrategies/multiAuthStrategy.js.map +1 -1
- package/lib/datastore/datastore.js +682 -469
- package/lib/datastore/datastore.js.map +1 -1
- package/lib/index.js +2 -4
- package/lib/index.js.map +1 -1
- package/lib/predicates/index.js +12 -2
- package/lib/predicates/index.js.map +1 -1
- package/lib/storage/adapter/AsyncStorageAdapter.js +393 -298
- package/lib/storage/adapter/AsyncStorageAdapter.js.map +1 -1
- package/lib/storage/adapter/AsyncStorageDatabase.js +97 -122
- package/lib/storage/adapter/AsyncStorageDatabase.js.map +1 -1
- package/lib/storage/adapter/InMemoryStore.js +16 -67
- package/lib/storage/adapter/InMemoryStore.js.map +1 -1
- package/lib/storage/adapter/InMemoryStore.native.js +2 -4
- package/lib/storage/adapter/InMemoryStore.native.js.map +1 -1
- package/lib/storage/adapter/IndexedDBAdapter.js +497 -404
- package/lib/storage/adapter/IndexedDBAdapter.js.map +1 -1
- package/lib/storage/adapter/getDefaultAdapter/index.js +3 -5
- package/lib/storage/adapter/getDefaultAdapter/index.js.map +1 -1
- package/lib/storage/adapter/getDefaultAdapter/index.native.js +2 -4
- package/lib/storage/adapter/getDefaultAdapter/index.native.js.map +1 -1
- package/lib/storage/storage.js +129 -151
- package/lib/storage/storage.js.map +1 -1
- package/lib/sync/datastoreConnectivity.js +13 -17
- package/lib/sync/datastoreConnectivity.js.map +1 -1
- package/lib/sync/datastoreReachability/index.native.js +2 -4
- package/lib/sync/datastoreReachability/index.native.js.map +1 -1
- package/lib/sync/index.js +544 -488
- package/lib/sync/index.js.map +1 -1
- package/lib/sync/merger.js +21 -80
- package/lib/sync/merger.js.map +1 -1
- package/lib/sync/outbox.js +95 -162
- package/lib/sync/outbox.js.map +1 -1
- package/lib/sync/processors/errorMaps.js +4 -34
- package/lib/sync/processors/errorMaps.js.map +1 -1
- package/lib/sync/processors/mutation.js +285 -312
- package/lib/sync/processors/mutation.js.map +1 -1
- package/lib/sync/processors/subscription.js +218 -259
- package/lib/sync/processors/subscription.js.map +1 -1
- package/lib/sync/processors/sync.js +141 -212
- package/lib/sync/processors/sync.js.map +1 -1
- package/lib/sync/utils.js +50 -61
- package/lib/sync/utils.js.map +1 -1
- package/lib/types.js +13 -39
- package/lib/types.js.map +1 -1
- package/lib/util.js +429 -242
- package/lib/util.js.map +1 -1
- package/lib-esm/authModeStrategies/multiAuthStrategy.d.ts +11 -0
- package/lib-esm/authModeStrategies/multiAuthStrategy.js +13 -57
- package/lib-esm/authModeStrategies/multiAuthStrategy.js.map +1 -1
- package/lib-esm/datastore/datastore.d.ts +107 -17
- package/lib-esm/datastore/datastore.js +649 -433
- 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 +356 -258
- 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 +67 -92
- package/lib-esm/storage/adapter/AsyncStorageDatabase.js.map +1 -1
- package/lib-esm/storage/adapter/InMemoryStore.js +1 -52
- package/lib-esm/storage/adapter/InMemoryStore.js.map +1 -1
- package/lib-esm/storage/adapter/IndexedDBAdapter.d.ts +26 -4
- package/lib-esm/storage/adapter/IndexedDBAdapter.js +446 -346
- 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 +94 -113
- package/lib-esm/storage/storage.js.map +1 -1
- package/lib-esm/sync/datastoreConnectivity.d.ts +1 -0
- package/lib-esm/sync/datastoreConnectivity.js +10 -11
- package/lib-esm/sync/datastoreConnectivity.js.map +1 -1
- package/lib-esm/sync/index.d.ts +31 -5
- package/lib-esm/sync/index.js +525 -466
- package/lib-esm/sync/index.js.map +1 -1
- package/lib-esm/sync/merger.d.ts +9 -3
- package/lib-esm/sync/merger.js +14 -73
- package/lib-esm/sync/merger.js.map +1 -1
- package/lib-esm/sync/outbox.d.ts +2 -2
- package/lib-esm/sync/outbox.js +79 -146
- package/lib-esm/sync/outbox.js.map +1 -1
- package/lib-esm/sync/processors/errorMaps.js +1 -31
- package/lib-esm/sync/processors/errorMaps.js.map +1 -1
- package/lib-esm/sync/processors/mutation.d.ts +2 -0
- package/lib-esm/sync/processors/mutation.js +271 -295
- 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 +214 -245
- 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 +127 -195
- 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 +45 -57
- package/lib-esm/sync/utils.js.map +1 -1
- package/lib-esm/types.d.ts +65 -26
- package/lib-esm/types.js +10 -38
- package/lib-esm/types.js.map +1 -1
- package/lib-esm/util.d.ts +67 -24
- package/lib-esm/util.js +420 -233
- package/lib-esm/util.js.map +1 -1
- package/package.json +14 -7
- package/src/authModeStrategies/multiAuthStrategy.ts +12 -1
- package/src/datastore/datastore.ts +798 -397
- 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 +358 -134
- 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 +521 -412
- package/src/sync/merger.ts +20 -4
- package/src/sync/outbox.ts +22 -9
- package/src/sync/processors/mutation.ts +188 -150
- package/src/sync/processors/subscription.ts +289 -253
- package/src/sync/processors/sync.ts +151 -138
- package/src/sync/utils.ts +67 -12
- package/src/types.ts +182 -30
- package/src/util.ts +505 -176
- package/build.js +0 -5
- package/dist/aws-amplify-datastore.js +0 -98255
- package/dist/aws-amplify-datastore.js.map +0 -1
- package/dist/aws-amplify-datastore.min.js +0 -66
- package/dist/aws-amplify-datastore.min.js.map +0 -1
- package/index.js +0 -7
- package/lib/authModeStrategies/defaultAuthStrategy.d.ts +0 -2
- package/lib/authModeStrategies/index.d.ts +0 -2
- package/lib/authModeStrategies/multiAuthStrategy.d.ts +0 -2
- package/lib/datastore/datastore.d.ts +0 -66
- package/lib/index.d.ts +0 -31
- package/lib/predicates/index.d.ts +0 -15
- package/lib/predicates/sort.d.ts +0 -8
- package/lib/ssr/index.d.ts +0 -3
- package/lib/storage/adapter/AsyncStorageAdapter.d.ts +0 -40
- package/lib/storage/adapter/AsyncStorageDatabase.d.ts +0 -29
- package/lib/storage/adapter/InMemoryStore.d.ts +0 -11
- package/lib/storage/adapter/InMemoryStore.native.d.ts +0 -1
- package/lib/storage/adapter/IndexedDBAdapter.d.ts +0 -37
- package/lib/storage/adapter/getDefaultAdapter/index.d.ts +0 -3
- package/lib/storage/adapter/getDefaultAdapter/index.native.d.ts +0 -3
- package/lib/storage/adapter/index.d.ts +0 -9
- package/lib/storage/storage.d.ts +0 -49
- package/lib/sync/datastoreConnectivity.d.ts +0 -15
- package/lib/sync/datastoreReachability/index.d.ts +0 -3
- package/lib/sync/datastoreReachability/index.native.d.ts +0 -3
- package/lib/sync/index.d.ts +0 -63
- package/lib/sync/merger.d.ts +0 -11
- package/lib/sync/outbox.d.ts +0 -27
- package/lib/sync/processors/errorMaps.d.ts +0 -17
- package/lib/sync/processors/mutation.d.ts +0 -56
- package/lib/sync/processors/subscription.d.ts +0 -31
- package/lib/sync/processors/sync.d.ts +0 -27
- package/lib/sync/utils.d.ts +0 -41
- package/lib/types.d.ts +0 -462
- package/lib/util.d.ts +0 -113
- package/webpack.config.dev.js +0 -6
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import API,
|
|
1
|
+
import { API, GraphQLResult, GRAPHQL_AUTH_MODE } from '@aws-amplify/api';
|
|
2
2
|
import Observable from 'zen-observable-ts';
|
|
3
3
|
import {
|
|
4
4
|
InternalSchema,
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
ConsoleLogger as Logger,
|
|
27
27
|
Hub,
|
|
28
28
|
NonRetryableError,
|
|
29
|
+
BackgroundProcessManager,
|
|
29
30
|
} from '@aws-amplify/core';
|
|
30
31
|
import { ModelPredicateCreator } from '../../predicates';
|
|
31
32
|
import { getSyncErrorType } from './errorMaps';
|
|
@@ -40,6 +41,8 @@ const logger = new Logger('DataStore');
|
|
|
40
41
|
class SyncProcessor {
|
|
41
42
|
private readonly typeQuery = new WeakMap<SchemaModel, [string, string]>();
|
|
42
43
|
|
|
44
|
+
private runningProcesses = new BackgroundProcessManager();
|
|
45
|
+
|
|
43
46
|
constructor(
|
|
44
47
|
private readonly schema: InternalSchema,
|
|
45
48
|
private readonly syncPredicates: WeakMap<SchemaModel, ModelPredicate<any>>,
|
|
@@ -85,14 +88,13 @@ class SyncProcessor {
|
|
|
85
88
|
return predicateToGraphQLFilter(predicatesGroup);
|
|
86
89
|
}
|
|
87
90
|
|
|
88
|
-
private async retrievePage<
|
|
89
|
-
T extends ModelInstanceMetadata = ModelInstanceMetadata
|
|
90
|
-
>(
|
|
91
|
+
private async retrievePage<T extends ModelInstanceMetadata>(
|
|
91
92
|
modelDefinition: SchemaModel,
|
|
92
93
|
lastSync: number,
|
|
93
94
|
nextToken: string,
|
|
94
95
|
limit: number = null,
|
|
95
|
-
filter: GraphQLFilter
|
|
96
|
+
filter: GraphQLFilter,
|
|
97
|
+
onTerminate: Promise<void>
|
|
96
98
|
): Promise<{ nextToken: string; startedAt: number; items: T[] }> {
|
|
97
99
|
const [opName, query] = this.typeQuery.get(modelDefinition);
|
|
98
100
|
|
|
@@ -115,6 +117,12 @@ class SyncProcessor {
|
|
|
115
117
|
|
|
116
118
|
let authModeAttempts = 0;
|
|
117
119
|
const authModeRetry = async () => {
|
|
120
|
+
if (!this.runningProcesses.isOpen) {
|
|
121
|
+
throw new Error(
|
|
122
|
+
'sync.retreievePage termination was requested. Exiting.'
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
118
126
|
try {
|
|
119
127
|
logger.debug(
|
|
120
128
|
`Attempting sync with authMode: ${readAuthModes[authModeAttempts]}`
|
|
@@ -125,6 +133,7 @@ class SyncProcessor {
|
|
|
125
133
|
opName,
|
|
126
134
|
modelDefinition,
|
|
127
135
|
authMode: readAuthModes[authModeAttempts],
|
|
136
|
+
onTerminate,
|
|
128
137
|
});
|
|
129
138
|
logger.debug(
|
|
130
139
|
`Sync successful with authMode: ${readAuthModes[authModeAttempts]}`
|
|
@@ -171,28 +180,20 @@ class SyncProcessor {
|
|
|
171
180
|
};
|
|
172
181
|
}
|
|
173
182
|
|
|
174
|
-
// Partial data private feature flag. Not a public API. This will be removed in a future release.
|
|
175
|
-
private partialDataFeatureFlagEnabled() {
|
|
176
|
-
try {
|
|
177
|
-
const flag = sessionStorage.getItem('datastorePartialData');
|
|
178
|
-
return Boolean(flag);
|
|
179
|
-
} catch (e) {
|
|
180
|
-
return false;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
183
|
private async jitteredRetry<T>({
|
|
185
184
|
query,
|
|
186
185
|
variables,
|
|
187
186
|
opName,
|
|
188
187
|
modelDefinition,
|
|
189
188
|
authMode,
|
|
189
|
+
onTerminate,
|
|
190
190
|
}: {
|
|
191
191
|
query: string;
|
|
192
192
|
variables: { limit: number; lastSync: number; nextToken: string };
|
|
193
193
|
opName: string;
|
|
194
194
|
modelDefinition: SchemaModel;
|
|
195
195
|
authMode: GRAPHQL_AUTH_MODE;
|
|
196
|
+
onTerminate: Promise<void>;
|
|
196
197
|
}): Promise<
|
|
197
198
|
GraphQLResult<{
|
|
198
199
|
[opName: string]: {
|
|
@@ -217,6 +218,8 @@ class SyncProcessor {
|
|
|
217
218
|
authToken,
|
|
218
219
|
userAgentSuffix: USER_AGENT_SUFFIX_DATASTORE,
|
|
219
220
|
});
|
|
221
|
+
|
|
222
|
+
// TODO: onTerminate.then(() => API.cancel(...))
|
|
220
223
|
} catch (error) {
|
|
221
224
|
// Catch client-side (GraphQLAuthError) & 401/403 errors here so that we don't continue to retry
|
|
222
225
|
const clientOrForbiddenErrorMessage =
|
|
@@ -225,92 +228,90 @@ class SyncProcessor {
|
|
|
225
228
|
throw new NonRetryableError(clientOrForbiddenErrorMessage);
|
|
226
229
|
}
|
|
227
230
|
|
|
228
|
-
const hasItems = Boolean(
|
|
229
|
-
error &&
|
|
230
|
-
error.data &&
|
|
231
|
-
error.data[opName] &&
|
|
232
|
-
error.data[opName].items
|
|
233
|
-
);
|
|
234
|
-
if (this.partialDataFeatureFlagEnabled()) {
|
|
235
|
-
if (hasItems) {
|
|
236
|
-
const result = error;
|
|
237
|
-
result.data[opName].items = result.data[opName].items.filter(
|
|
238
|
-
item => item !== null
|
|
239
|
-
);
|
|
240
|
-
if (error.errors) {
|
|
241
|
-
await Promise.all(
|
|
242
|
-
error.errors.map(async err => {
|
|
243
|
-
try {
|
|
244
|
-
await this.errorHandler({
|
|
245
|
-
recoverySuggestion:
|
|
246
|
-
'Ensure app code is up to date, auth directives exist and are correct on each model, and that server-side data has not been invalidated by a schema change. If the problem persists, search for or create an issue: https://github.com/aws-amplify/amplify-js/issues',
|
|
247
|
-
localModel: null,
|
|
248
|
-
message: err.message,
|
|
249
|
-
model: modelDefinition.name,
|
|
250
|
-
operation: opName,
|
|
251
|
-
errorType: getSyncErrorType(err),
|
|
252
|
-
process: ProcessName.sync,
|
|
253
|
-
remoteModel: null,
|
|
254
|
-
cause: err,
|
|
255
|
-
});
|
|
256
|
-
} catch (e) {
|
|
257
|
-
logger.error('Sync error handler failed with:', e);
|
|
258
|
-
}
|
|
259
|
-
})
|
|
260
|
-
);
|
|
261
|
-
Hub.dispatch('datastore', {
|
|
262
|
-
event: 'syncQueriesPartialSyncError',
|
|
263
|
-
data: {
|
|
264
|
-
errors: error.errors,
|
|
265
|
-
modelName: modelDefinition.name,
|
|
266
|
-
},
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
return result;
|
|
271
|
-
} else {
|
|
272
|
-
throw error;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
231
|
+
const hasItems = Boolean(error?.data?.[opName]?.items);
|
|
275
232
|
|
|
276
|
-
// If the error is unauthorized, filter out unauthorized items and return accessible items
|
|
277
233
|
const unauthorized =
|
|
278
|
-
error &&
|
|
279
|
-
error.errors &&
|
|
234
|
+
error?.errors &&
|
|
280
235
|
(error.errors as [any]).some(
|
|
281
236
|
err => err.errorType === 'Unauthorized'
|
|
282
237
|
);
|
|
283
|
-
if (unauthorized) {
|
|
284
|
-
const result = error;
|
|
285
238
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
239
|
+
const otherErrors =
|
|
240
|
+
error?.errors &&
|
|
241
|
+
(error.errors as [any]).filter(
|
|
242
|
+
err => err.errorType !== 'Unauthorized'
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
const result = error;
|
|
246
|
+
|
|
247
|
+
if (hasItems) {
|
|
248
|
+
result.data[opName].items = result.data[opName].items.filter(
|
|
249
|
+
item => item !== null
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (hasItems && otherErrors?.length) {
|
|
254
|
+
await Promise.all(
|
|
255
|
+
otherErrors.map(async err => {
|
|
256
|
+
try {
|
|
257
|
+
await this.errorHandler({
|
|
258
|
+
recoverySuggestion:
|
|
259
|
+
'Ensure app code is up to date, auth directives exist and are correct on each model, and that server-side data has not been invalidated by a schema change. If the problem persists, search for or create an issue: https://github.com/aws-amplify/amplify-js/issues',
|
|
260
|
+
localModel: null,
|
|
261
|
+
message: err.message,
|
|
262
|
+
model: modelDefinition.name,
|
|
263
|
+
operation: opName,
|
|
264
|
+
errorType: getSyncErrorType(err),
|
|
265
|
+
process: ProcessName.sync,
|
|
266
|
+
remoteModel: null,
|
|
267
|
+
cause: err,
|
|
268
|
+
});
|
|
269
|
+
} catch (e) {
|
|
270
|
+
logger.error('Sync error handler failed with:', e);
|
|
271
|
+
}
|
|
272
|
+
})
|
|
273
|
+
);
|
|
274
|
+
Hub.dispatch('datastore', {
|
|
275
|
+
event: 'nonApplicableDataReceived',
|
|
276
|
+
data: {
|
|
277
|
+
errors: otherErrors,
|
|
278
|
+
modelName: modelDefinition.name,
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (unauthorized) {
|
|
296
284
|
logger.warn(
|
|
297
285
|
'queryError',
|
|
298
286
|
`User is unauthorized to query ${opName}, some items could not be returned.`
|
|
299
287
|
);
|
|
288
|
+
|
|
289
|
+
result.data = result.data || {};
|
|
290
|
+
|
|
291
|
+
result.data[opName] = {
|
|
292
|
+
...opResultDefaults,
|
|
293
|
+
...result.data[opName],
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
return result;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (result.data?.[opName].items?.length) {
|
|
300
300
|
return result;
|
|
301
|
-
} else {
|
|
302
|
-
throw error;
|
|
303
301
|
}
|
|
302
|
+
|
|
303
|
+
throw error;
|
|
304
304
|
}
|
|
305
305
|
},
|
|
306
|
-
[query, variables]
|
|
306
|
+
[query, variables],
|
|
307
|
+
undefined,
|
|
308
|
+
onTerminate
|
|
307
309
|
);
|
|
308
310
|
}
|
|
309
311
|
|
|
310
312
|
start(
|
|
311
313
|
typesLastSync: Map<SchemaModel, [string, number]>
|
|
312
314
|
): Observable<SyncModelPage> {
|
|
313
|
-
let processing = true;
|
|
314
315
|
const { maxRecordsToSync, syncPageSize } = this.amplifyConfig;
|
|
315
316
|
const parentPromises = new Map<string, Promise<void>>();
|
|
316
317
|
const observable = new Observable<SyncModelPage>(observer => {
|
|
@@ -329,76 +330,88 @@ class SyncProcessor {
|
|
|
329
330
|
|
|
330
331
|
const allModelsReady = Array.from(sortedTypesLastSyncs.entries())
|
|
331
332
|
.filter(([{ syncable }]) => syncable)
|
|
332
|
-
.map(
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
await Promise.all(promises);
|
|
350
|
-
|
|
351
|
-
do {
|
|
352
|
-
if (!processing) {
|
|
353
|
-
return;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
const limit = Math.min(
|
|
357
|
-
maxRecordsToSync - recordsReceived,
|
|
358
|
-
syncPageSize
|
|
333
|
+
.map(
|
|
334
|
+
([modelDefinition, [namespace, lastSync]]) =>
|
|
335
|
+
this.runningProcesses.isOpen &&
|
|
336
|
+
this.runningProcesses.add(async onTerminate => {
|
|
337
|
+
let done = false;
|
|
338
|
+
let nextToken: string = null;
|
|
339
|
+
let startedAt: number = null;
|
|
340
|
+
let items: ModelInstanceMetadata[] = null;
|
|
341
|
+
|
|
342
|
+
let recordsReceived = 0;
|
|
343
|
+
const filter = this.graphqlFilterFromPredicate(modelDefinition);
|
|
344
|
+
|
|
345
|
+
const parents = this.schema.namespaces[
|
|
346
|
+
namespace
|
|
347
|
+
].modelTopologicalOrdering.get(modelDefinition.name);
|
|
348
|
+
const promises = parents.map(parent =>
|
|
349
|
+
parentPromises.get(`${namespace}_${parent}`)
|
|
359
350
|
);
|
|
360
351
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
352
|
+
const promise = new Promise<void>(async res => {
|
|
353
|
+
await Promise.all(promises);
|
|
354
|
+
|
|
355
|
+
do {
|
|
356
|
+
if (!this.runningProcesses.isOpen) {
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const limit = Math.min(
|
|
361
|
+
maxRecordsToSync - recordsReceived,
|
|
362
|
+
syncPageSize
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
({ items, nextToken, startedAt } = await this.retrievePage(
|
|
366
|
+
modelDefinition,
|
|
367
|
+
lastSync,
|
|
368
|
+
nextToken,
|
|
369
|
+
limit,
|
|
370
|
+
filter,
|
|
371
|
+
onTerminate
|
|
372
|
+
));
|
|
373
|
+
|
|
374
|
+
recordsReceived += items.length;
|
|
375
|
+
|
|
376
|
+
done =
|
|
377
|
+
nextToken === null || recordsReceived >= maxRecordsToSync;
|
|
378
|
+
|
|
379
|
+
observer.next({
|
|
380
|
+
namespace,
|
|
381
|
+
modelDefinition,
|
|
382
|
+
items,
|
|
383
|
+
done,
|
|
384
|
+
startedAt,
|
|
385
|
+
isFullSync: !lastSync,
|
|
386
|
+
});
|
|
387
|
+
} while (!done);
|
|
388
|
+
|
|
389
|
+
res();
|
|
380
390
|
});
|
|
381
|
-
} while (!done);
|
|
382
|
-
|
|
383
|
-
res();
|
|
384
|
-
});
|
|
385
391
|
|
|
386
|
-
|
|
392
|
+
parentPromises.set(
|
|
393
|
+
`${namespace}_${modelDefinition.name}`,
|
|
394
|
+
promise
|
|
395
|
+
);
|
|
387
396
|
|
|
388
|
-
|
|
389
|
-
|
|
397
|
+
await promise;
|
|
398
|
+
}, `adding model ${modelDefinition.name}`)
|
|
399
|
+
);
|
|
390
400
|
|
|
391
401
|
Promise.all(allModelsReady).then(() => {
|
|
392
402
|
observer.complete();
|
|
393
403
|
});
|
|
394
|
-
|
|
395
|
-
return () => {
|
|
396
|
-
processing = false;
|
|
397
|
-
};
|
|
398
404
|
});
|
|
399
405
|
|
|
400
406
|
return observable;
|
|
401
407
|
}
|
|
408
|
+
|
|
409
|
+
async stop() {
|
|
410
|
+
logger.debug('stopping sync processor');
|
|
411
|
+
await this.runningProcesses.close();
|
|
412
|
+
await this.runningProcesses.open();
|
|
413
|
+
logger.debug('sync processor stopped');
|
|
414
|
+
}
|
|
402
415
|
}
|
|
403
416
|
|
|
404
417
|
export type SyncModelPage = {
|
package/src/sync/utils.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
isGraphQLScalarType,
|
|
12
12
|
isPredicateObj,
|
|
13
13
|
isSchemaModel,
|
|
14
|
+
isSchemaModelWithAttributes,
|
|
14
15
|
isTargetNameAssociation,
|
|
15
16
|
isNonModelFieldType,
|
|
16
17
|
ModelFields,
|
|
@@ -27,7 +28,12 @@ import {
|
|
|
27
28
|
InternalSchema,
|
|
28
29
|
AuthModeStrategy,
|
|
29
30
|
} from '../types';
|
|
30
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
exhaustiveCheck,
|
|
33
|
+
extractPrimaryKeyFieldNames,
|
|
34
|
+
establishRelationAndKeys,
|
|
35
|
+
IDENTIFIER_KEY_SEPARATOR,
|
|
36
|
+
} from '../util';
|
|
31
37
|
import { MutationEvent } from './';
|
|
32
38
|
|
|
33
39
|
const logger = new Logger('DataStore');
|
|
@@ -47,7 +53,7 @@ export enum TransformerMutationType {
|
|
|
47
53
|
GET = 'Get',
|
|
48
54
|
}
|
|
49
55
|
|
|
50
|
-
const dummyMetadata:
|
|
56
|
+
const dummyMetadata: ModelInstanceMetadata = {
|
|
51
57
|
_version: undefined,
|
|
52
58
|
_lastChangedAt: undefined,
|
|
53
59
|
_deleted: undefined,
|
|
@@ -79,7 +85,7 @@ export function generateSelectionSet(
|
|
|
79
85
|
if (isSchemaModel(modelDefinition)) {
|
|
80
86
|
scalarAndMetadataFields = scalarAndMetadataFields
|
|
81
87
|
.concat(getMetadataFields())
|
|
82
|
-
.concat(getConnectionFields(modelDefinition));
|
|
88
|
+
.concat(getConnectionFields(modelDefinition, namespace));
|
|
83
89
|
}
|
|
84
90
|
|
|
85
91
|
const result = scalarAndMetadataFields.join('\n');
|
|
@@ -103,7 +109,7 @@ function getOwnerFields(
|
|
|
103
109
|
modelDefinition: SchemaModel | SchemaNonModel
|
|
104
110
|
): string[] {
|
|
105
111
|
const ownerFields: string[] = [];
|
|
106
|
-
if (
|
|
112
|
+
if (isSchemaModelWithAttributes(modelDefinition)) {
|
|
107
113
|
modelDefinition.attributes.forEach(attr => {
|
|
108
114
|
if (attr.properties && attr.properties.rules) {
|
|
109
115
|
const rule = attr.properties.rules.find(rule => rule.allow === 'owner');
|
|
@@ -138,8 +144,12 @@ function getScalarFields(
|
|
|
138
144
|
return result;
|
|
139
145
|
}
|
|
140
146
|
|
|
141
|
-
|
|
142
|
-
|
|
147
|
+
// Used for generating the selection set for queries and mutations
|
|
148
|
+
function getConnectionFields(
|
|
149
|
+
modelDefinition: SchemaModel,
|
|
150
|
+
namespace: SchemaNamespace
|
|
151
|
+
): string[] {
|
|
152
|
+
const result: string[] = [];
|
|
143
153
|
|
|
144
154
|
Object.values(modelDefinition.fields)
|
|
145
155
|
.filter(({ association }) => association && Object.keys(association).length)
|
|
@@ -153,7 +163,26 @@ function getConnectionFields(modelDefinition: SchemaModel): string[] {
|
|
|
153
163
|
break;
|
|
154
164
|
case 'BELONGS_TO':
|
|
155
165
|
if (isTargetNameAssociation(association)) {
|
|
156
|
-
|
|
166
|
+
// New codegen (CPK)
|
|
167
|
+
if (association.targetNames && association.targetNames.length > 0) {
|
|
168
|
+
// Need to retrieve relations in order to get connected model keys
|
|
169
|
+
const [relations] = establishRelationAndKeys(namespace);
|
|
170
|
+
|
|
171
|
+
const connectedModelName =
|
|
172
|
+
modelDefinition.fields[name].type['model'];
|
|
173
|
+
|
|
174
|
+
const byPkIndex = relations[connectedModelName].indexes.find(
|
|
175
|
+
([name]) => name === 'byPk'
|
|
176
|
+
);
|
|
177
|
+
const keyFields = byPkIndex && byPkIndex[1];
|
|
178
|
+
const keyFieldSelectionSet = keyFields?.join(' ');
|
|
179
|
+
|
|
180
|
+
// We rely on `_deleted` when we process the sync query (e.g. in batchSave in the adapters)
|
|
181
|
+
result.push(`${name} { ${keyFieldSelectionSet} _deleted }`);
|
|
182
|
+
} else {
|
|
183
|
+
// backwards-compatability for schema generated prior to custom primary key support
|
|
184
|
+
result.push(`${name} { id _deleted }`);
|
|
185
|
+
}
|
|
157
186
|
}
|
|
158
187
|
break;
|
|
159
188
|
default:
|
|
@@ -412,10 +441,13 @@ export function createMutationInstanceFromModelOperation<
|
|
|
412
441
|
return v;
|
|
413
442
|
};
|
|
414
443
|
|
|
444
|
+
const modelId = getIdentifierValue(modelDefinition, element);
|
|
445
|
+
const optionalId = OpType.INSERT && id ? { id } : {};
|
|
446
|
+
|
|
415
447
|
const mutationEvent = modelInstanceCreator(MutationEventConstructor, {
|
|
416
|
-
...
|
|
448
|
+
...optionalId,
|
|
417
449
|
data: JSON.stringify(element, replacer),
|
|
418
|
-
modelId
|
|
450
|
+
modelId,
|
|
419
451
|
model: model.name,
|
|
420
452
|
operation,
|
|
421
453
|
condition: JSON.stringify(condition),
|
|
@@ -425,7 +457,8 @@ export function createMutationInstanceFromModelOperation<
|
|
|
425
457
|
}
|
|
426
458
|
|
|
427
459
|
export function predicateToGraphQLCondition(
|
|
428
|
-
predicate: PredicatesGroup<any
|
|
460
|
+
predicate: PredicatesGroup<any>,
|
|
461
|
+
modelDefinition: SchemaModel
|
|
429
462
|
): GraphQLCondition {
|
|
430
463
|
const result = {};
|
|
431
464
|
|
|
@@ -433,17 +466,27 @@ export function predicateToGraphQLCondition(
|
|
|
433
466
|
return result;
|
|
434
467
|
}
|
|
435
468
|
|
|
469
|
+
const keyFields = extractPrimaryKeyFieldNames(modelDefinition);
|
|
470
|
+
|
|
436
471
|
predicate.predicates.forEach(p => {
|
|
437
472
|
if (isPredicateObj(p)) {
|
|
438
473
|
const { field, operator, operand } = p;
|
|
439
474
|
|
|
440
|
-
|
|
475
|
+
// This is compatible with how the GQL Transform currently generates the Condition Input,
|
|
476
|
+
// i.e. any PK and SK fields are omitted and can't be used as conditions.
|
|
477
|
+
// However, I think this limits usability.
|
|
478
|
+
// What if we want to delete all records where SK > some value
|
|
479
|
+
// Or all records where PK = some value but SKs are different values
|
|
480
|
+
|
|
481
|
+
// TODO: if the Transform gets updated ^ we'll need to modify this logic to only omit
|
|
482
|
+
// key fields from the predicate/condition when ALL of the keyFields are present and using `eq` operators
|
|
483
|
+
if (keyFields.includes(field as string)) {
|
|
441
484
|
return;
|
|
442
485
|
}
|
|
443
486
|
|
|
444
487
|
result[field] = { [operator]: operand };
|
|
445
488
|
} else {
|
|
446
|
-
result[p.type] = predicateToGraphQLCondition(p);
|
|
489
|
+
result[p.type] = predicateToGraphQLCondition(p, modelDefinition);
|
|
447
490
|
}
|
|
448
491
|
});
|
|
449
492
|
|
|
@@ -610,3 +653,15 @@ export async function getTokenForCustomAuth(
|
|
|
610
653
|
}
|
|
611
654
|
}
|
|
612
655
|
}
|
|
656
|
+
|
|
657
|
+
// Util that takes a modelDefinition and model and returns either the id value(s) or the custom primary key value(s)
|
|
658
|
+
export function getIdentifierValue(
|
|
659
|
+
modelDefinition: SchemaModel,
|
|
660
|
+
model: ModelInstanceMetadata | PersistentModel
|
|
661
|
+
): string {
|
|
662
|
+
const pkFieldNames = extractPrimaryKeyFieldNames(modelDefinition);
|
|
663
|
+
|
|
664
|
+
const idOrPk = pkFieldNames.map(f => model[f]).join(IDENTIFIER_KEY_SEPARATOR);
|
|
665
|
+
|
|
666
|
+
return idOrPk;
|
|
667
|
+
}
|