@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.
- package/lib/authModeStrategies/defaultAuthStrategy.js +1 -1
- package/lib/authModeStrategies/multiAuthStrategy.js +29 -105
- package/lib/datastore/datastore.js +887 -1370
- package/lib/index.js +1 -1
- package/lib/predicates/index.js +53 -102
- package/lib/predicates/next.js +310 -557
- package/lib/predicates/sort.js +24 -27
- package/lib/ssr/index.js +1 -1
- package/lib/storage/adapter/AsyncStorageAdapter.js +115 -449
- package/lib/storage/adapter/AsyncStorageDatabase.js +215 -480
- package/lib/storage/adapter/InMemoryStore.js +27 -101
- package/lib/storage/adapter/InMemoryStore.native.js +1 -1
- package/lib/storage/adapter/IndexedDBAdapter.js +441 -1002
- package/lib/storage/adapter/StorageAdapterBase.js +177 -396
- package/lib/storage/adapter/getDefaultAdapter/index.js +5 -6
- package/lib/storage/adapter/getDefaultAdapter/index.native.js +2 -2
- package/lib/storage/relationship.js +174 -260
- package/lib/storage/storage.js +244 -507
- package/lib/sync/datastoreConnectivity.js +29 -84
- package/lib/sync/datastoreReachability/index.js +1 -1
- package/lib/sync/datastoreReachability/index.native.js +2 -2
- package/lib/sync/index.js +512 -885
- package/lib/sync/merger.js +30 -133
- package/lib/sync/outbox.js +147 -302
- package/lib/sync/processors/errorMaps.js +30 -80
- package/lib/sync/processors/mutation.js +331 -579
- package/lib/sync/processors/subscription.js +268 -428
- package/lib/sync/processors/sync.js +276 -464
- package/lib/sync/utils.js +248 -393
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/types.js +16 -58
- package/lib/util.js +335 -575
- package/lib-esm/authModeStrategies/defaultAuthStrategy.js +1 -1
- package/lib-esm/authModeStrategies/multiAuthStrategy.js +27 -103
- package/lib-esm/datastore/datastore.js +874 -1359
- package/lib-esm/index.js +6 -6
- package/lib-esm/predicates/index.js +54 -104
- package/lib-esm/predicates/next.js +306 -555
- package/lib-esm/predicates/sort.js +24 -27
- package/lib-esm/ssr/index.js +1 -1
- package/lib-esm/storage/adapter/AsyncStorageAdapter.js +111 -446
- package/lib-esm/storage/adapter/AsyncStorageDatabase.js +212 -477
- package/lib-esm/storage/adapter/InMemoryStore.js +27 -102
- package/lib-esm/storage/adapter/IndexedDBAdapter.js +436 -997
- package/lib-esm/storage/adapter/StorageAdapterBase.js +172 -392
- package/lib-esm/storage/adapter/getDefaultAdapter/index.js +2 -3
- package/lib-esm/storage/adapter/getDefaultAdapter/index.native.js +1 -1
- package/lib-esm/storage/relationship.js +173 -260
- package/lib-esm/storage/storage.js +236 -499
- package/lib-esm/sync/datastoreConnectivity.js +26 -82
- package/lib-esm/sync/datastoreReachability/index.js +1 -1
- package/lib-esm/sync/datastoreReachability/index.native.js +1 -1
- package/lib-esm/sync/index.js +498 -872
- package/lib-esm/sync/merger.js +28 -131
- package/lib-esm/sync/outbox.js +143 -298
- package/lib-esm/sync/processors/errorMaps.js +32 -82
- package/lib-esm/sync/processors/mutation.js +324 -572
- package/lib-esm/sync/processors/subscription.js +258 -418
- package/lib-esm/sync/processors/sync.js +269 -457
- package/lib-esm/sync/utils.js +245 -390
- package/lib-esm/tsconfig.tsbuildinfo +1 -1
- package/lib-esm/types.js +16 -59
- package/lib-esm/util.js +335 -577
- package/package.json +12 -12
- package/src/datastore/datastore.ts +4 -3
- package/src/storage/adapter/getDefaultAdapter/index.ts +1 -3
- package/src/sync/processors/mutation.ts +1 -1
- package/src/sync/processors/sync.ts +1 -1
- package/src/sync/utils.ts +1 -1
- 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 {
|
|
6
|
+
import { ConsoleLogger, Hub } from '@aws-amplify/core';
|
|
70
7
|
import { ModelPredicateCreator } from '../../predicates';
|
|
71
8
|
import { getSyncErrorType } from './errorMaps';
|
|
72
|
-
|
|
9
|
+
const opResultDefaults = {
|
|
73
10
|
items: [],
|
|
74
11
|
nextToken: null,
|
|
75
12
|
startedAt: null,
|
|
76
13
|
};
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
93
|
-
|
|
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(
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
37
|
+
}
|
|
38
|
+
graphqlFilterFromPredicate(model) {
|
|
107
39
|
if (!this.syncPredicates) {
|
|
108
40
|
return null;
|
|
109
41
|
}
|
|
110
|
-
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
132
|
-
|
|
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
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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
|
-
}
|
|
334
|
-
|
|
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
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
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
|
-
|
|
363
|
-
.filter(
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
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
|
-
|
|
477
|
-
|
|
478
|
-
|
|
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
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
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 };
|