@aws-amplify/datastore 5.0.1-api-v6-models.c1977f8.0 → 5.0.1-api-v6-models.891fe0d.0

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.
Files changed (70) hide show
  1. package/lib/authModeStrategies/defaultAuthStrategy.js +1 -1
  2. package/lib/authModeStrategies/multiAuthStrategy.js +29 -105
  3. package/lib/datastore/datastore.js +887 -1370
  4. package/lib/index.js +1 -1
  5. package/lib/predicates/index.js +53 -102
  6. package/lib/predicates/next.js +310 -557
  7. package/lib/predicates/sort.js +24 -27
  8. package/lib/ssr/index.js +1 -1
  9. package/lib/storage/adapter/AsyncStorageAdapter.js +115 -449
  10. package/lib/storage/adapter/AsyncStorageDatabase.js +215 -480
  11. package/lib/storage/adapter/InMemoryStore.js +27 -101
  12. package/lib/storage/adapter/InMemoryStore.native.js +1 -1
  13. package/lib/storage/adapter/IndexedDBAdapter.js +441 -1002
  14. package/lib/storage/adapter/StorageAdapterBase.js +177 -396
  15. package/lib/storage/adapter/getDefaultAdapter/index.js +5 -6
  16. package/lib/storage/adapter/getDefaultAdapter/index.native.js +2 -2
  17. package/lib/storage/relationship.js +174 -260
  18. package/lib/storage/storage.js +244 -507
  19. package/lib/sync/datastoreConnectivity.js +29 -84
  20. package/lib/sync/datastoreReachability/index.js +1 -1
  21. package/lib/sync/datastoreReachability/index.native.js +2 -2
  22. package/lib/sync/index.js +512 -885
  23. package/lib/sync/merger.js +30 -133
  24. package/lib/sync/outbox.js +147 -302
  25. package/lib/sync/processors/errorMaps.js +30 -80
  26. package/lib/sync/processors/mutation.js +331 -579
  27. package/lib/sync/processors/subscription.js +268 -428
  28. package/lib/sync/processors/sync.js +276 -464
  29. package/lib/sync/utils.js +248 -393
  30. package/lib/tsconfig.tsbuildinfo +1 -1
  31. package/lib/types.js +16 -58
  32. package/lib/util.js +335 -575
  33. package/lib-esm/authModeStrategies/defaultAuthStrategy.js +1 -1
  34. package/lib-esm/authModeStrategies/multiAuthStrategy.js +27 -103
  35. package/lib-esm/datastore/datastore.js +874 -1359
  36. package/lib-esm/index.js +6 -6
  37. package/lib-esm/predicates/index.js +54 -104
  38. package/lib-esm/predicates/next.js +306 -555
  39. package/lib-esm/predicates/sort.js +24 -27
  40. package/lib-esm/ssr/index.js +1 -1
  41. package/lib-esm/storage/adapter/AsyncStorageAdapter.js +111 -446
  42. package/lib-esm/storage/adapter/AsyncStorageDatabase.js +212 -477
  43. package/lib-esm/storage/adapter/InMemoryStore.js +27 -102
  44. package/lib-esm/storage/adapter/IndexedDBAdapter.js +436 -997
  45. package/lib-esm/storage/adapter/StorageAdapterBase.js +172 -392
  46. package/lib-esm/storage/adapter/getDefaultAdapter/index.js +2 -3
  47. package/lib-esm/storage/adapter/getDefaultAdapter/index.native.js +1 -1
  48. package/lib-esm/storage/relationship.js +173 -260
  49. package/lib-esm/storage/storage.js +236 -499
  50. package/lib-esm/sync/datastoreConnectivity.js +26 -82
  51. package/lib-esm/sync/datastoreReachability/index.js +1 -1
  52. package/lib-esm/sync/datastoreReachability/index.native.js +1 -1
  53. package/lib-esm/sync/index.js +498 -872
  54. package/lib-esm/sync/merger.js +28 -131
  55. package/lib-esm/sync/outbox.js +143 -298
  56. package/lib-esm/sync/processors/errorMaps.js +32 -82
  57. package/lib-esm/sync/processors/mutation.js +324 -572
  58. package/lib-esm/sync/processors/subscription.js +258 -418
  59. package/lib-esm/sync/processors/sync.js +269 -457
  60. package/lib-esm/sync/utils.js +245 -390
  61. package/lib-esm/tsconfig.tsbuildinfo +1 -1
  62. package/lib-esm/types.js +16 -59
  63. package/lib-esm/util.js +335 -577
  64. package/package.json +12 -12
  65. package/src/datastore/datastore.ts +4 -3
  66. package/src/storage/adapter/getDefaultAdapter/index.ts +1 -3
  67. package/src/sync/processors/mutation.ts +1 -1
  68. package/src/sync/processors/sync.ts +1 -1
  69. package/src/sync/utils.ts +1 -1
  70. package/src/util.ts +44 -6
@@ -1,83 +1,19 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
- var __generator = (this && this.__generator) || function (thisArg, body) {
11
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
12
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
13
- function verb(n) { return function (v) { return step([n, v]); }; }
14
- function step(op) {
15
- if (f) throw new TypeError("Generator is already executing.");
16
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
17
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
18
- if (y = 0, t) op = [op[0] & 2, t.value];
19
- switch (op[0]) {
20
- case 0: case 1: t = op; break;
21
- case 4: _.label++; return { value: op[1], done: false };
22
- case 5: _.label++; y = op[1]; op = [0]; continue;
23
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
24
- default:
25
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
26
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
27
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
28
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
29
- if (t[2]) _.ops.pop();
30
- _.trys.pop(); continue;
31
- }
32
- op = body.call(thisArg, _);
33
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
34
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
35
- }
36
- };
37
- var __read = (this && this.__read) || function (o, n) {
38
- var m = typeof Symbol === "function" && o[Symbol.iterator];
39
- if (!m) return o;
40
- var i = m.call(o), r, ar = [], e;
41
- try {
42
- while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
43
- }
44
- catch (error) { e = { error: error }; }
45
- finally {
46
- try {
47
- if (r && !r.done && (m = i["return"])) m.call(i);
48
- }
49
- finally { if (e) throw e.error; }
50
- }
51
- return ar;
52
- };
53
- var __values = (this && this.__values) || function(o) {
54
- var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
55
- if (m) return m.call(o);
56
- if (o && typeof o.length === "number") return {
57
- next: function () {
58
- if (o && i >= o.length) o = void 0;
59
- return { value: o && o[i++], done: !o };
60
- }
61
- };
62
- throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
63
- };
64
1
  import { InternalAPI } from '@aws-amplify/api/internals';
65
2
  import { Observable } from 'rxjs';
66
3
  import { ProcessName, } from '../../types';
67
4
  import { buildGraphQLOperation, getModelAuthModes, getClientSideAuthError, getForbiddenError, predicateToGraphQLFilter, getTokenForCustomAuth, } from '../utils';
68
5
  import { jitteredExponentialRetry, Category, DataStoreAction, NonRetryableError, BackgroundProcessManager, } from '@aws-amplify/core/internals/utils';
69
- import { Hub, ConsoleLogger } from '@aws-amplify/core';
6
+ import { ConsoleLogger, Hub } from '@aws-amplify/core';
70
7
  import { ModelPredicateCreator } from '../../predicates';
71
8
  import { getSyncErrorType } from './errorMaps';
72
- var opResultDefaults = {
9
+ const opResultDefaults = {
73
10
  items: [],
74
11
  nextToken: null,
75
12
  startedAt: null,
76
13
  };
77
- var logger = new ConsoleLogger('DataStore');
78
- var SyncProcessor = /** @class */ (function () {
79
- function SyncProcessor(schema, syncPredicates, amplifyConfig, authModeStrategy, errorHandler, amplifyContext) {
80
- if (amplifyConfig === void 0) { amplifyConfig = {}; }
14
+ const logger = new ConsoleLogger('DataStore');
15
+ class SyncProcessor {
16
+ constructor(schema, syncPredicates, amplifyConfig = {}, authModeStrategy, errorHandler, amplifyContext) {
81
17
  this.schema = schema;
82
18
  this.syncPredicates = syncPredicates;
83
19
  this.amplifyConfig = amplifyConfig;
@@ -89,416 +25,292 @@ var SyncProcessor = /** @class */ (function () {
89
25
  amplifyContext.InternalAPI = amplifyContext.InternalAPI || InternalAPI;
90
26
  this.generateQueries();
91
27
  }
92
- SyncProcessor.prototype.generateQueries = function () {
93
- var _this = this;
94
- Object.values(this.schema.namespaces).forEach(function (namespace) {
28
+ generateQueries() {
29
+ Object.values(this.schema.namespaces).forEach(namespace => {
95
30
  Object.values(namespace.models)
96
- .filter(function (_a) {
97
- var syncable = _a.syncable;
98
- return syncable;
99
- })
100
- .forEach(function (model) {
101
- var _a = __read(buildGraphQLOperation(namespace, model, 'LIST'), 1), _b = __read(_a[0]), opNameQuery = _b.slice(1);
102
- _this.typeQuery.set(model, opNameQuery);
31
+ .filter(({ syncable }) => syncable)
32
+ .forEach(model => {
33
+ const [[, ...opNameQuery]] = buildGraphQLOperation(namespace, model, 'LIST');
34
+ this.typeQuery.set(model, opNameQuery);
103
35
  });
104
36
  });
105
- };
106
- SyncProcessor.prototype.graphqlFilterFromPredicate = function (model) {
37
+ }
38
+ graphqlFilterFromPredicate(model) {
107
39
  if (!this.syncPredicates) {
108
40
  return null;
109
41
  }
110
- var predicatesGroup = ModelPredicateCreator.getPredicates(this.syncPredicates.get(model), false);
42
+ const predicatesGroup = ModelPredicateCreator.getPredicates(this.syncPredicates.get(model), false);
111
43
  if (!predicatesGroup) {
112
44
  return null;
113
45
  }
114
46
  return predicateToGraphQLFilter(predicatesGroup);
115
- };
116
- SyncProcessor.prototype.retrievePage = function (modelDefinition, lastSync, nextToken, limit, filter, onTerminate) {
117
- if (limit === void 0) { limit = null; }
118
- return __awaiter(this, void 0, void 0, function () {
119
- var _a, opName, query, variables, modelAuthModes, readAuthModes, authModeAttempts, authModeRetry, data, _b, _c, opResult, items, newNextToken, startedAt;
120
- var _this = this;
121
- return __generator(this, function (_d) {
122
- switch (_d.label) {
123
- case 0:
124
- _a = __read(this.typeQuery.get(modelDefinition), 2), opName = _a[0], query = _a[1];
125
- variables = {
126
- limit: limit,
127
- nextToken: nextToken,
128
- lastSync: lastSync,
129
- filter: filter,
47
+ }
48
+ async retrievePage(modelDefinition, lastSync, nextToken, limit = null, filter, onTerminate) {
49
+ const [opName, query] = this.typeQuery.get(modelDefinition);
50
+ const variables = {
51
+ limit,
52
+ nextToken,
53
+ lastSync,
54
+ filter,
55
+ };
56
+ const modelAuthModes = await getModelAuthModes({
57
+ authModeStrategy: this.authModeStrategy,
58
+ defaultAuthMode: this.amplifyConfig.aws_appsync_authenticationType,
59
+ modelName: modelDefinition.name,
60
+ schema: this.schema,
61
+ });
62
+ // sync only needs the READ auth mode(s)
63
+ const readAuthModes = modelAuthModes.READ;
64
+ let authModeAttempts = 0;
65
+ const authModeRetry = async () => {
66
+ if (!this.runningProcesses.isOpen) {
67
+ throw new Error('sync.retreievePage termination was requested. Exiting.');
68
+ }
69
+ try {
70
+ logger.debug(`Attempting sync with authMode: ${readAuthModes[authModeAttempts]}`);
71
+ const response = await this.jitteredRetry({
72
+ query,
73
+ variables,
74
+ opName,
75
+ modelDefinition,
76
+ authMode: readAuthModes[authModeAttempts],
77
+ onTerminate,
78
+ });
79
+ logger.debug(`Sync successful with authMode: ${readAuthModes[authModeAttempts]}`);
80
+ return response;
81
+ }
82
+ catch (error) {
83
+ authModeAttempts++;
84
+ if (authModeAttempts >= readAuthModes.length) {
85
+ const authMode = readAuthModes[authModeAttempts - 1];
86
+ logger.debug(`Sync failed with authMode: ${authMode}`, error);
87
+ if (getClientSideAuthError(error) || getForbiddenError(error)) {
88
+ // return empty list of data so DataStore will continue to sync other models
89
+ logger.warn(`User is unauthorized to query ${opName} with auth mode ${authMode}. No data could be returned.`);
90
+ return {
91
+ data: {
92
+ [opName]: opResultDefaults,
93
+ },
130
94
  };
131
- return [4 /*yield*/, getModelAuthModes({
132
- authModeStrategy: this.authModeStrategy,
133
- defaultAuthMode: this.amplifyConfig.aws_appsync_authenticationType,
134
- modelName: modelDefinition.name,
135
- schema: this.schema,
136
- })];
137
- case 1:
138
- modelAuthModes = _d.sent();
139
- readAuthModes = modelAuthModes.READ;
140
- authModeAttempts = 0;
141
- authModeRetry = function () { return __awaiter(_this, void 0, void 0, function () {
142
- var response, error_1, authMode;
143
- var _a;
144
- return __generator(this, function (_b) {
145
- switch (_b.label) {
146
- case 0:
147
- if (!this.runningProcesses.isOpen) {
148
- throw new Error('sync.retreievePage termination was requested. Exiting.');
149
- }
150
- _b.label = 1;
151
- case 1:
152
- _b.trys.push([1, 3, , 5]);
153
- logger.debug("Attempting sync with authMode: ".concat(readAuthModes[authModeAttempts]));
154
- return [4 /*yield*/, this.jitteredRetry({
155
- query: query,
156
- variables: variables,
157
- opName: opName,
158
- modelDefinition: modelDefinition,
159
- authMode: readAuthModes[authModeAttempts],
160
- onTerminate: onTerminate,
161
- })];
162
- case 2:
163
- response = _b.sent();
164
- logger.debug("Sync successful with authMode: ".concat(readAuthModes[authModeAttempts]));
165
- return [2 /*return*/, response];
166
- case 3:
167
- error_1 = _b.sent();
168
- authModeAttempts++;
169
- if (authModeAttempts >= readAuthModes.length) {
170
- authMode = readAuthModes[authModeAttempts - 1];
171
- logger.debug("Sync failed with authMode: ".concat(authMode), error_1);
172
- if (getClientSideAuthError(error_1) || getForbiddenError(error_1)) {
173
- // return empty list of data so DataStore will continue to sync other models
174
- logger.warn("User is unauthorized to query ".concat(opName, " with auth mode ").concat(authMode, ". No data could be returned."));
175
- return [2 /*return*/, {
176
- data: (_a = {},
177
- _a[opName] = opResultDefaults,
178
- _a),
179
- }];
180
- }
181
- throw error_1;
182
- }
183
- logger.debug("Sync failed with authMode: ".concat(readAuthModes[authModeAttempts - 1], ". Retrying with authMode: ").concat(readAuthModes[authModeAttempts]));
184
- return [4 /*yield*/, authModeRetry()];
185
- case 4: return [2 /*return*/, _b.sent()];
186
- case 5: return [2 /*return*/];
187
- }
188
- });
189
- }); };
190
- return [4 /*yield*/, authModeRetry()];
191
- case 2:
192
- data = (_d.sent()).data;
193
- _b = data, _c = opName, opResult = _b[_c];
194
- items = opResult.items, newNextToken = opResult.nextToken, startedAt = opResult.startedAt;
195
- return [2 /*return*/, {
196
- nextToken: newNextToken,
197
- startedAt: startedAt,
198
- items: items,
199
- }];
95
+ }
96
+ throw error;
200
97
  }
201
- });
202
- });
203
- };
204
- SyncProcessor.prototype.jitteredRetry = function (_a) {
205
- var query = _a.query, variables = _a.variables, opName = _a.opName, modelDefinition = _a.modelDefinition, authMode = _a.authMode, onTerminate = _a.onTerminate;
206
- return __awaiter(this, void 0, void 0, function () {
207
- var _this = this;
208
- return __generator(this, function (_b) {
209
- switch (_b.label) {
210
- case 0: return [4 /*yield*/, jitteredExponentialRetry(function (query, variables) { return __awaiter(_this, void 0, void 0, function () {
211
- var authToken, customUserAgentDetails, error_2, clientOrForbiddenErrorMessage, hasItems, unauthorized, otherErrors, result;
212
- var _this = this;
213
- var _a, _b, _c, _d, _e;
214
- return __generator(this, function (_f) {
215
- switch (_f.label) {
216
- case 0:
217
- _f.trys.push([0, 3, , 6]);
218
- return [4 /*yield*/, getTokenForCustomAuth(authMode, this.amplifyConfig)];
219
- case 1:
220
- authToken = _f.sent();
221
- customUserAgentDetails = {
222
- category: Category.DataStore,
223
- action: DataStoreAction.GraphQl,
224
- };
225
- return [4 /*yield*/, this.amplifyContext.InternalAPI.graphql({
226
- query: query,
227
- variables: variables,
228
- authMode: authMode,
229
- authToken: authToken,
230
- }, undefined, customUserAgentDetails)];
231
- case 2: return [2 /*return*/, _f.sent()];
232
- case 3:
233
- error_2 = _f.sent();
234
- clientOrForbiddenErrorMessage = getClientSideAuthError(error_2) || getForbiddenError(error_2);
235
- if (clientOrForbiddenErrorMessage) {
236
- logger.error('Sync processor retry error:', error_2);
237
- throw new NonRetryableError(clientOrForbiddenErrorMessage);
238
- }
239
- hasItems = Boolean((_b = (_a = error_2 === null || error_2 === void 0 ? void 0 : error_2.data) === null || _a === void 0 ? void 0 : _a[opName]) === null || _b === void 0 ? void 0 : _b.items);
240
- unauthorized = (error_2 === null || error_2 === void 0 ? void 0 : error_2.errors) &&
241
- error_2.errors.some(function (err) { return err.errorType === 'Unauthorized'; });
242
- otherErrors = (error_2 === null || error_2 === void 0 ? void 0 : error_2.errors) &&
243
- error_2.errors.filter(function (err) { return err.errorType !== 'Unauthorized'; });
244
- result = error_2;
245
- if (hasItems) {
246
- result.data[opName].items = result.data[opName].items.filter(function (item) { return item !== null; });
247
- }
248
- if (!(hasItems && (otherErrors === null || otherErrors === void 0 ? void 0 : otherErrors.length))) return [3 /*break*/, 5];
249
- return [4 /*yield*/, Promise.all(otherErrors.map(function (err) { return __awaiter(_this, void 0, void 0, function () {
250
- var e_1;
251
- return __generator(this, function (_a) {
252
- switch (_a.label) {
253
- case 0:
254
- _a.trys.push([0, 2, , 3]);
255
- return [4 /*yield*/, this.errorHandler({
256
- recoverySuggestion: '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',
257
- localModel: null,
258
- message: err.message,
259
- model: modelDefinition.name,
260
- operation: opName,
261
- errorType: getSyncErrorType(err),
262
- process: ProcessName.sync,
263
- remoteModel: null,
264
- cause: err,
265
- })];
266
- case 1:
267
- _a.sent();
268
- return [3 /*break*/, 3];
269
- case 2:
270
- e_1 = _a.sent();
271
- logger.error('Sync error handler failed with:', e_1);
272
- return [3 /*break*/, 3];
273
- case 3: return [2 /*return*/];
274
- }
275
- });
276
- }); }))];
277
- case 4:
278
- _f.sent();
279
- Hub.dispatch('datastore', {
280
- event: 'nonApplicableDataReceived',
281
- data: {
282
- errors: otherErrors,
283
- modelName: modelDefinition.name,
284
- },
285
- });
286
- _f.label = 5;
287
- case 5:
288
- /**
289
- * Handle $util.unauthorized() in resolver request mapper, which responses with something
290
- * like this:
291
- *
292
- * ```
293
- * {
294
- * data: { syncYourModel: null },
295
- * errors: [
296
- * {
297
- * path: ['syncLegacyJSONComments'],
298
- * data: null,
299
- * errorType: 'Unauthorized',
300
- * errorInfo: null,
301
- * locations: [{ line: 2, column: 3, sourceName: null }],
302
- * message:
303
- * 'Not Authorized to access syncYourModel on type Query',
304
- * },
305
- * ],
306
- * }
307
- * ```
308
- *
309
- * The correct handling for this is to signal that we've encountered a non-retryable error,
310
- * since the server has responded with an auth error and *NO DATA* at this point.
311
- */
312
- if (unauthorized) {
313
- this.errorHandler({
314
- recoverySuggestion: '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',
315
- localModel: null,
316
- message: error_2.message,
317
- model: modelDefinition.name,
318
- operation: opName,
319
- errorType: getSyncErrorType(error_2.errors[0]),
320
- process: ProcessName.sync,
321
- remoteModel: null,
322
- cause: error_2,
323
- });
324
- throw new NonRetryableError(error_2);
325
- }
326
- if ((_e = (_d = (_c = result.data) === null || _c === void 0 ? void 0 : _c[opName]) === null || _d === void 0 ? void 0 : _d.items) === null || _e === void 0 ? void 0 : _e.length) {
327
- return [2 /*return*/, result];
328
- }
329
- throw error_2;
330
- case 6: return [2 /*return*/];
331
- }
98
+ logger.debug(`Sync failed with authMode: ${readAuthModes[authModeAttempts - 1]}. Retrying with authMode: ${readAuthModes[authModeAttempts]}`);
99
+ return await authModeRetry();
100
+ }
101
+ };
102
+ const { data } = await authModeRetry();
103
+ const { [opName]: opResult } = data;
104
+ const { items, nextToken: newNextToken, startedAt } = opResult;
105
+ return {
106
+ nextToken: newNextToken,
107
+ startedAt,
108
+ items,
109
+ };
110
+ }
111
+ async jitteredRetry({ query, variables, opName, modelDefinition, authMode, onTerminate, }) {
112
+ return await jitteredExponentialRetry(async (query, variables) => {
113
+ try {
114
+ const authToken = await getTokenForCustomAuth(authMode, this.amplifyConfig);
115
+ const customUserAgentDetails = {
116
+ category: Category.DataStore,
117
+ action: DataStoreAction.GraphQl,
118
+ };
119
+ return await this.amplifyContext.InternalAPI.graphql({
120
+ query,
121
+ variables,
122
+ authMode,
123
+ authToken,
124
+ }, undefined, customUserAgentDetails);
125
+ // TODO: onTerminate.then(() => API.cancel(...))
126
+ }
127
+ catch (error) {
128
+ // Catch client-side (GraphQLAuthError) & 401/403 errors here so that we don't continue to retry
129
+ const clientOrForbiddenErrorMessage = getClientSideAuthError(error) || getForbiddenError(error);
130
+ if (clientOrForbiddenErrorMessage) {
131
+ logger.error('Sync processor retry error:', error);
132
+ throw new NonRetryableError(clientOrForbiddenErrorMessage);
133
+ }
134
+ const hasItems = Boolean(error?.data?.[opName]?.items);
135
+ const unauthorized = error?.errors &&
136
+ error.errors.some(err => err.errorType === 'Unauthorized');
137
+ const otherErrors = error?.errors &&
138
+ error.errors.filter(err => err.errorType !== 'Unauthorized');
139
+ const result = error;
140
+ if (hasItems) {
141
+ result.data[opName].items = result.data[opName].items.filter(item => item !== null);
142
+ }
143
+ if (hasItems && otherErrors?.length) {
144
+ await Promise.all(otherErrors.map(async (err) => {
145
+ try {
146
+ await this.errorHandler({
147
+ recoverySuggestion: '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',
148
+ localModel: null,
149
+ message: err.message,
150
+ model: modelDefinition.name,
151
+ operation: opName,
152
+ errorType: getSyncErrorType(err),
153
+ process: ProcessName.sync,
154
+ remoteModel: null,
155
+ cause: err,
332
156
  });
333
- }); }, [query, variables], undefined, onTerminate)];
334
- case 1: return [2 /*return*/, _b.sent()];
157
+ }
158
+ catch (e) {
159
+ logger.error('Sync error handler failed with:', e);
160
+ }
161
+ }));
162
+ Hub.dispatch('datastore', {
163
+ event: 'nonApplicableDataReceived',
164
+ data: {
165
+ errors: otherErrors,
166
+ modelName: modelDefinition.name,
167
+ },
168
+ });
335
169
  }
336
- });
337
- });
338
- };
339
- SyncProcessor.prototype.start = function (typesLastSync) {
340
- var _this = this;
341
- var _a = this.amplifyConfig, maxRecordsToSync = _a.maxRecordsToSync, syncPageSize = _a.syncPageSize;
342
- var parentPromises = new Map();
343
- var observable = new Observable(function (observer) {
344
- var sortedTypesLastSyncs = Object.values(_this.schema.namespaces).reduce(function (map, namespace) {
345
- var e_2, _a;
346
- try {
347
- for (var _b = __values(Array.from(namespace.modelTopologicalOrdering.keys())), _c = _b.next(); !_c.done; _c = _b.next()) {
348
- var modelName = _c.value;
349
- var typeLastSync = typesLastSync.get(namespace.models[modelName]);
350
- map.set(namespace.models[modelName], typeLastSync);
351
- }
170
+ /**
171
+ * Handle $util.unauthorized() in resolver request mapper, which responses with something
172
+ * like this:
173
+ *
174
+ * ```
175
+ * {
176
+ * data: { syncYourModel: null },
177
+ * errors: [
178
+ * {
179
+ * path: ['syncLegacyJSONComments'],
180
+ * data: null,
181
+ * errorType: 'Unauthorized',
182
+ * errorInfo: null,
183
+ * locations: [{ line: 2, column: 3, sourceName: null }],
184
+ * message:
185
+ * 'Not Authorized to access syncYourModel on type Query',
186
+ * },
187
+ * ],
188
+ * }
189
+ * ```
190
+ *
191
+ * The correct handling for this is to signal that we've encountered a non-retryable error,
192
+ * since the server has responded with an auth error and *NO DATA* at this point.
193
+ */
194
+ if (unauthorized) {
195
+ this.errorHandler({
196
+ recoverySuggestion: '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',
197
+ localModel: null,
198
+ message: error.message,
199
+ model: modelDefinition.name,
200
+ operation: opName,
201
+ errorType: getSyncErrorType(error.errors[0]),
202
+ process: ProcessName.sync,
203
+ remoteModel: null,
204
+ cause: error,
205
+ });
206
+ throw new NonRetryableError(error);
352
207
  }
353
- catch (e_2_1) { e_2 = { error: e_2_1 }; }
354
- finally {
355
- try {
356
- if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
357
- }
358
- finally { if (e_2) throw e_2.error; }
208
+ if (result.data?.[opName]?.items?.length) {
209
+ return result;
210
+ }
211
+ throw error;
212
+ }
213
+ }, [query, variables], undefined, onTerminate);
214
+ }
215
+ start(typesLastSync) {
216
+ const { maxRecordsToSync, syncPageSize } = this.amplifyConfig;
217
+ const parentPromises = new Map();
218
+ const observable = new Observable(observer => {
219
+ const sortedTypesLastSyncs = Object.values(this.schema.namespaces).reduce((map, namespace) => {
220
+ for (const modelName of Array.from(namespace.modelTopologicalOrdering.keys())) {
221
+ const typeLastSync = typesLastSync.get(namespace.models[modelName]);
222
+ map.set(namespace.models[modelName], typeLastSync);
359
223
  }
360
224
  return map;
361
225
  }, new Map());
362
- var allModelsReady = Array.from(sortedTypesLastSyncs.entries())
363
- .filter(function (_a) {
364
- var _b = __read(_a, 1), syncable = _b[0].syncable;
365
- return syncable;
366
- })
367
- .map(function (_a) {
368
- var _b = __read(_a, 2), modelDefinition = _b[0], _c = __read(_b[1], 2), namespace = _c[0], lastSync = _c[1];
369
- return _this.runningProcesses.isOpen &&
370
- _this.runningProcesses.add(function (onTerminate) { return __awaiter(_this, void 0, void 0, function () {
371
- var done, nextToken, startedAt, items, recordsReceived, filter, parents, promises, promise;
372
- var _this = this;
373
- return __generator(this, function (_a) {
374
- switch (_a.label) {
375
- case 0:
376
- done = false;
377
- nextToken = null;
378
- startedAt = null;
379
- items = null;
380
- recordsReceived = 0;
381
- filter = this.graphqlFilterFromPredicate(modelDefinition);
382
- parents = this.schema.namespaces[namespace].modelTopologicalOrdering.get(modelDefinition.name);
383
- promises = parents.map(function (parent) {
384
- return parentPromises.get("".concat(namespace, "_").concat(parent));
226
+ const allModelsReady = Array.from(sortedTypesLastSyncs.entries())
227
+ .filter(([{ syncable }]) => syncable)
228
+ .map(([modelDefinition, [namespace, lastSync]]) => this.runningProcesses.isOpen &&
229
+ this.runningProcesses.add(async (onTerminate) => {
230
+ let done = false;
231
+ let nextToken = null;
232
+ let startedAt = null;
233
+ let items = null;
234
+ let recordsReceived = 0;
235
+ const filter = this.graphqlFilterFromPredicate(modelDefinition);
236
+ const parents = this.schema.namespaces[namespace].modelTopologicalOrdering.get(modelDefinition.name);
237
+ const promises = parents.map(parent => parentPromises.get(`${namespace}_${parent}`));
238
+ const promise = new Promise(async (res) => {
239
+ await Promise.all(promises);
240
+ do {
241
+ /**
242
+ * If `runningProcesses` is not open, it means that the sync processor has been
243
+ * stopped (for example by calling `DataStore.clear()` upstream) and has not yet
244
+ * finished terminating and/or waiting for its background processes to complete.
245
+ */
246
+ if (!this.runningProcesses.isOpen) {
247
+ logger.debug(`Sync processor has been stopped, terminating sync for ${modelDefinition.name}`);
248
+ return res();
249
+ }
250
+ const limit = Math.min(maxRecordsToSync - recordsReceived, syncPageSize);
251
+ /**
252
+ * It's possible that `retrievePage` will fail.
253
+ * If it does fail, continue merging the rest of the data,
254
+ * and invoke the error handler for non-applicable data.
255
+ */
256
+ try {
257
+ ({ items, nextToken, startedAt } = await this.retrievePage(modelDefinition, lastSync, nextToken, limit, filter, onTerminate));
258
+ }
259
+ catch (error) {
260
+ try {
261
+ await this.errorHandler({
262
+ recoverySuggestion: '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',
263
+ localModel: null,
264
+ message: error.message,
265
+ model: modelDefinition.name,
266
+ operation: null,
267
+ errorType: getSyncErrorType(error),
268
+ process: ProcessName.sync,
269
+ remoteModel: null,
270
+ cause: error,
385
271
  });
386
- promise = new Promise(function (res) { return __awaiter(_this, void 0, void 0, function () {
387
- var limit, error_3, e_3;
388
- var _a;
389
- return __generator(this, function (_b) {
390
- switch (_b.label) {
391
- case 0: return [4 /*yield*/, Promise.all(promises)];
392
- case 1:
393
- _b.sent();
394
- _b.label = 2;
395
- case 2:
396
- /**
397
- * If `runningProcesses` is not open, it means that the sync processor has been
398
- * stopped (for example by calling `DataStore.clear()` upstream) and has not yet
399
- * finished terminating and/or waiting for its background processes to complete.
400
- */
401
- if (!this.runningProcesses.isOpen) {
402
- logger.debug("Sync processor has been stopped, terminating sync for ".concat(modelDefinition.name));
403
- return [2 /*return*/, res()];
404
- }
405
- limit = Math.min(maxRecordsToSync - recordsReceived, syncPageSize);
406
- _b.label = 3;
407
- case 3:
408
- _b.trys.push([3, 5, , 10]);
409
- return [4 /*yield*/, this.retrievePage(modelDefinition, lastSync, nextToken, limit, filter, onTerminate)];
410
- case 4:
411
- (_a = _b.sent(), items = _a.items, nextToken = _a.nextToken, startedAt = _a.startedAt);
412
- return [3 /*break*/, 10];
413
- case 5:
414
- error_3 = _b.sent();
415
- _b.label = 6;
416
- case 6:
417
- _b.trys.push([6, 8, , 9]);
418
- return [4 /*yield*/, this.errorHandler({
419
- recoverySuggestion: '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',
420
- localModel: null,
421
- message: error_3.message,
422
- model: modelDefinition.name,
423
- operation: null,
424
- errorType: getSyncErrorType(error_3),
425
- process: ProcessName.sync,
426
- remoteModel: null,
427
- cause: error_3,
428
- })];
429
- case 7:
430
- _b.sent();
431
- return [3 /*break*/, 9];
432
- case 8:
433
- e_3 = _b.sent();
434
- logger.error('Sync error handler failed with:', e_3);
435
- return [3 /*break*/, 9];
436
- case 9:
437
- /**
438
- * If there's an error, this model fails, but the rest of the sync should
439
- * continue. To facilitate this, we explicitly mark this model as `done`
440
- * with no items and allow the loop to continue organically. This ensures
441
- * all callbacks (subscription messages) happen as normal, so anything
442
- * waiting on them knows the model is as done as it can be.
443
- */
444
- done = true;
445
- items = [];
446
- return [3 /*break*/, 10];
447
- case 10:
448
- recordsReceived += items.length;
449
- done =
450
- nextToken === null || recordsReceived >= maxRecordsToSync;
451
- observer.next({
452
- namespace: namespace,
453
- modelDefinition: modelDefinition,
454
- items: items,
455
- done: done,
456
- startedAt: startedAt,
457
- isFullSync: !lastSync,
458
- });
459
- _b.label = 11;
460
- case 11:
461
- if (!done) return [3 /*break*/, 2];
462
- _b.label = 12;
463
- case 12:
464
- res();
465
- return [2 /*return*/];
466
- }
467
- });
468
- }); });
469
- parentPromises.set("".concat(namespace, "_").concat(modelDefinition.name), promise);
470
- return [4 /*yield*/, promise];
471
- case 1:
472
- _a.sent();
473
- return [2 /*return*/];
272
+ }
273
+ catch (e) {
274
+ logger.error('Sync error handler failed with:', e);
275
+ }
276
+ /**
277
+ * If there's an error, this model fails, but the rest of the sync should
278
+ * continue. To facilitate this, we explicitly mark this model as `done`
279
+ * with no items and allow the loop to continue organically. This ensures
280
+ * all callbacks (subscription messages) happen as normal, so anything
281
+ * waiting on them knows the model is as done as it can be.
282
+ */
283
+ done = true;
284
+ items = [];
474
285
  }
475
- });
476
- }); }, "adding model ".concat(modelDefinition.name));
477
- });
478
- Promise.all(allModelsReady).then(function () {
286
+ recordsReceived += items.length;
287
+ done =
288
+ nextToken === null || recordsReceived >= maxRecordsToSync;
289
+ observer.next({
290
+ namespace,
291
+ modelDefinition,
292
+ items,
293
+ done,
294
+ startedAt,
295
+ isFullSync: !lastSync,
296
+ });
297
+ } while (!done);
298
+ res();
299
+ });
300
+ parentPromises.set(`${namespace}_${modelDefinition.name}`, promise);
301
+ await promise;
302
+ }, `adding model ${modelDefinition.name}`));
303
+ Promise.all(allModelsReady).then(() => {
479
304
  observer.complete();
480
305
  });
481
306
  });
482
307
  return observable;
483
- };
484
- SyncProcessor.prototype.stop = function () {
485
- return __awaiter(this, void 0, void 0, function () {
486
- return __generator(this, function (_a) {
487
- switch (_a.label) {
488
- case 0:
489
- logger.debug('stopping sync processor');
490
- return [4 /*yield*/, this.runningProcesses.close()];
491
- case 1:
492
- _a.sent();
493
- return [4 /*yield*/, this.runningProcesses.open()];
494
- case 2:
495
- _a.sent();
496
- logger.debug('sync processor stopped');
497
- return [2 /*return*/];
498
- }
499
- });
500
- });
501
- };
502
- return SyncProcessor;
503
- }());
308
+ }
309
+ async stop() {
310
+ logger.debug('stopping sync processor');
311
+ await this.runningProcesses.close();
312
+ await this.runningProcesses.open();
313
+ logger.debug('sync processor stopped');
314
+ }
315
+ }
504
316
  export { SyncProcessor };