@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
package/src/sync/merger.ts
CHANGED
|
@@ -3,8 +3,10 @@ import {
|
|
|
3
3
|
ModelInstanceMetadata,
|
|
4
4
|
OpType,
|
|
5
5
|
PersistentModelConstructor,
|
|
6
|
+
SchemaModel,
|
|
6
7
|
} from '../types';
|
|
7
8
|
import { MutationEventOutbox } from './outbox';
|
|
9
|
+
import { getIdentifierValue } from './utils';
|
|
8
10
|
|
|
9
11
|
// https://github.com/aws-amplify/amplify-js/blob/datastore-docs/packages/datastore/docs/sync-engine.md#merger
|
|
10
12
|
class ModelMerger {
|
|
@@ -13,12 +15,23 @@ class ModelMerger {
|
|
|
13
15
|
private readonly ownSymbol: Symbol
|
|
14
16
|
) {}
|
|
15
17
|
|
|
18
|
+
/**
|
|
19
|
+
*
|
|
20
|
+
* @param storage Storage adapter that contains the data.
|
|
21
|
+
* @param model The model from an outbox mutation.
|
|
22
|
+
* @returns The type of operation (INSERT/UPDATE/DELETE)
|
|
23
|
+
*/
|
|
16
24
|
public async merge<T extends ModelInstanceMetadata>(
|
|
17
25
|
storage: Storage,
|
|
18
|
-
model: T
|
|
26
|
+
model: T,
|
|
27
|
+
modelDefinition: SchemaModel
|
|
19
28
|
): Promise<OpType> {
|
|
20
29
|
let result: OpType;
|
|
21
|
-
const mutationsForModel = await this.outbox.getForModel(
|
|
30
|
+
const mutationsForModel = await this.outbox.getForModel(
|
|
31
|
+
storage,
|
|
32
|
+
model,
|
|
33
|
+
modelDefinition
|
|
34
|
+
);
|
|
22
35
|
|
|
23
36
|
const isDelete = model._deleted;
|
|
24
37
|
|
|
@@ -37,13 +50,16 @@ class ModelMerger {
|
|
|
37
50
|
public async mergePage(
|
|
38
51
|
storage: Storage,
|
|
39
52
|
modelConstructor: PersistentModelConstructor<any>,
|
|
40
|
-
items: ModelInstanceMetadata[]
|
|
53
|
+
items: ModelInstanceMetadata[],
|
|
54
|
+
modelDefinition: SchemaModel
|
|
41
55
|
): Promise<[ModelInstanceMetadata, OpType][]> {
|
|
42
56
|
const itemsMap: Map<string, ModelInstanceMetadata> = new Map();
|
|
43
57
|
|
|
44
58
|
for (const item of items) {
|
|
45
59
|
// merge items by model id. Latest record for a given id remains.
|
|
46
|
-
|
|
60
|
+
const modelId = getIdentifierValue(modelDefinition, item);
|
|
61
|
+
|
|
62
|
+
itemsMap.set(modelId, item);
|
|
47
63
|
}
|
|
48
64
|
|
|
49
65
|
const page = [...itemsMap.values()];
|
package/src/sync/outbox.ts
CHANGED
|
@@ -11,9 +11,10 @@ import {
|
|
|
11
11
|
PersistentModel,
|
|
12
12
|
PersistentModelConstructor,
|
|
13
13
|
QueryOne,
|
|
14
|
+
SchemaModel,
|
|
14
15
|
} from '../types';
|
|
15
16
|
import { USER, SYNC, valuesEqual } from '../util';
|
|
16
|
-
import { TransformerMutationType } from './utils';
|
|
17
|
+
import { getIdentifierValue, TransformerMutationType } from './utils';
|
|
17
18
|
|
|
18
19
|
// TODO: Persist deleted ids
|
|
19
20
|
// https://github.com/aws-amplify/amplify-js/blob/datastore-docs/packages/datastore/docs/sync-engine.md#outbox
|
|
@@ -31,10 +32,12 @@ class MutationEventOutbox {
|
|
|
31
32
|
storage: Storage,
|
|
32
33
|
mutationEvent: MutationEvent
|
|
33
34
|
): Promise<void> {
|
|
34
|
-
storage.runExclusive(async s => {
|
|
35
|
+
await storage.runExclusive(async s => {
|
|
35
36
|
const mutationEventModelDefinition =
|
|
36
37
|
this.schema.namespaces[SYNC].models['MutationEvent'];
|
|
37
38
|
|
|
39
|
+
// `id` is the key for the record in the mutationEvent;
|
|
40
|
+
// `modelId` is the key for the actual record that was mutated
|
|
38
41
|
const predicate = ModelPredicateCreator.createFromExisting<MutationEvent>(
|
|
39
42
|
mutationEventModelDefinition,
|
|
40
43
|
c =>
|
|
@@ -43,13 +46,16 @@ class MutationEventOutbox {
|
|
|
43
46
|
.id('ne', this.inProgressMutationEventId)
|
|
44
47
|
);
|
|
45
48
|
|
|
49
|
+
// Check if there are any other records with same id
|
|
46
50
|
const [first] = await s.query(this.MutationEvent, predicate);
|
|
47
51
|
|
|
52
|
+
// No other record with same modelId, so enqueue
|
|
48
53
|
if (first === undefined) {
|
|
49
54
|
await s.save(mutationEvent, undefined, this.ownSymbol);
|
|
50
55
|
return;
|
|
51
56
|
}
|
|
52
57
|
|
|
58
|
+
// There was an enqueued mutation for the modelId, so continue
|
|
53
59
|
const { operation: incomingMutationType } = mutationEvent;
|
|
54
60
|
|
|
55
61
|
if (first.operation === TransformerMutationType.CREATE) {
|
|
@@ -122,16 +128,19 @@ class MutationEventOutbox {
|
|
|
122
128
|
|
|
123
129
|
public async getForModel<T extends PersistentModel>(
|
|
124
130
|
storage: StorageFacade,
|
|
125
|
-
model: T
|
|
131
|
+
model: T,
|
|
132
|
+
userModelDefinition: SchemaModel
|
|
126
133
|
): Promise<MutationEvent[]> {
|
|
127
134
|
const mutationEventModelDefinition =
|
|
128
135
|
this.schema.namespaces[SYNC].models.MutationEvent;
|
|
129
136
|
|
|
137
|
+
const modelId = getIdentifierValue(userModelDefinition, model);
|
|
138
|
+
|
|
130
139
|
const mutationEvents = await storage.query(
|
|
131
140
|
this.MutationEvent,
|
|
132
141
|
ModelPredicateCreator.createFromExisting(
|
|
133
142
|
mutationEventModelDefinition,
|
|
134
|
-
c => c.modelId('eq',
|
|
143
|
+
c => c.modelId('eq', modelId)
|
|
135
144
|
)
|
|
136
145
|
);
|
|
137
146
|
|
|
@@ -187,9 +196,14 @@ class MutationEventOutbox {
|
|
|
187
196
|
const mutationEventModelDefinition =
|
|
188
197
|
this.schema.namespaces[SYNC].models['MutationEvent'];
|
|
189
198
|
|
|
199
|
+
const userModelDefinition =
|
|
200
|
+
this.schema.namespaces['user'].models[head.model];
|
|
201
|
+
|
|
202
|
+
const recordId = getIdentifierValue(userModelDefinition, record);
|
|
203
|
+
|
|
190
204
|
const predicate = ModelPredicateCreator.createFromExisting<MutationEvent>(
|
|
191
205
|
mutationEventModelDefinition,
|
|
192
|
-
c => c.modelId('eq',
|
|
206
|
+
c => c.modelId('eq', recordId).id('ne', this.inProgressMutationEventId)
|
|
193
207
|
);
|
|
194
208
|
|
|
195
209
|
const outdatedMutations = await storage.query(
|
|
@@ -224,11 +238,11 @@ class MutationEventOutbox {
|
|
|
224
238
|
previous: MutationEvent,
|
|
225
239
|
current: MutationEvent
|
|
226
240
|
): MutationEvent {
|
|
227
|
-
const { _version,
|
|
228
|
-
|
|
241
|
+
const { _version, _lastChangedAt, _deleted, ...previousData } = JSON.parse(
|
|
242
|
+
previous.data
|
|
243
|
+
);
|
|
229
244
|
|
|
230
245
|
const {
|
|
231
|
-
id: __id,
|
|
232
246
|
_version: __version,
|
|
233
247
|
_lastChangedAt: __lastChangedAt,
|
|
234
248
|
_deleted: __deleted,
|
|
@@ -236,7 +250,6 @@ class MutationEventOutbox {
|
|
|
236
250
|
} = JSON.parse(current.data);
|
|
237
251
|
|
|
238
252
|
const data = JSON.stringify({
|
|
239
|
-
id,
|
|
240
253
|
_version,
|
|
241
254
|
_lastChangedAt,
|
|
242
255
|
_deleted,
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import API,
|
|
1
|
+
import { API, GraphQLResult, GRAPHQL_AUTH_MODE } from '@aws-amplify/api';
|
|
2
2
|
import {
|
|
3
3
|
ConsoleLogger as Logger,
|
|
4
4
|
jitteredBackoff,
|
|
5
5
|
NonRetryableError,
|
|
6
6
|
retry,
|
|
7
|
+
BackgroundProcessManager,
|
|
7
8
|
} from '@aws-amplify/core';
|
|
8
9
|
import Observable, { ZenObservable } from 'zen-observable-ts';
|
|
9
10
|
import { MutationEvent } from '../';
|
|
@@ -27,7 +28,13 @@ import {
|
|
|
27
28
|
ProcessName,
|
|
28
29
|
AmplifyContext,
|
|
29
30
|
} from '../../types';
|
|
30
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
exhaustiveCheck,
|
|
33
|
+
extractTargetNamesFromSrc,
|
|
34
|
+
USER,
|
|
35
|
+
USER_AGENT_SUFFIX_DATASTORE,
|
|
36
|
+
ID,
|
|
37
|
+
} from '../../util';
|
|
31
38
|
import { MutationEventOutbox } from '../outbox';
|
|
32
39
|
import {
|
|
33
40
|
buildGraphQLOperation,
|
|
@@ -57,6 +64,8 @@ class MutationProcessor {
|
|
|
57
64
|
>();
|
|
58
65
|
private processing: boolean = false;
|
|
59
66
|
|
|
67
|
+
private runningProcesses = new BackgroundProcessManager();
|
|
68
|
+
|
|
60
69
|
constructor(
|
|
61
70
|
private readonly schema: InternalSchema,
|
|
62
71
|
private readonly storage: Storage,
|
|
@@ -112,126 +121,150 @@ class MutationProcessor {
|
|
|
112
121
|
const observable = new Observable<MutationProcessorEvent>(observer => {
|
|
113
122
|
this.observer = observer;
|
|
114
123
|
|
|
115
|
-
|
|
124
|
+
try {
|
|
125
|
+
this.resume();
|
|
126
|
+
} catch (error) {
|
|
127
|
+
logger.error('mutations processor start error', error);
|
|
128
|
+
throw error;
|
|
129
|
+
}
|
|
116
130
|
|
|
117
|
-
return () => {
|
|
131
|
+
return this.runningProcesses.addCleaner(async () => {
|
|
118
132
|
this.pause();
|
|
119
|
-
};
|
|
133
|
+
});
|
|
120
134
|
});
|
|
121
135
|
|
|
122
136
|
return observable;
|
|
123
137
|
}
|
|
124
138
|
|
|
125
|
-
public async
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
139
|
+
public async stop() {
|
|
140
|
+
await this.runningProcesses.close();
|
|
141
|
+
await this.runningProcesses.open();
|
|
142
|
+
}
|
|
129
143
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const modelConstructor = this.userClasses[
|
|
141
|
-
model
|
|
142
|
-
] as PersistentModelConstructor<MutationEvent>;
|
|
143
|
-
let result: GraphQLResult<Record<string, PersistentModel>>;
|
|
144
|
-
let opName: string;
|
|
145
|
-
let modelDefinition: SchemaModel;
|
|
146
|
-
try {
|
|
147
|
-
const modelAuthModes = await getModelAuthModes({
|
|
148
|
-
authModeStrategy: this.authModeStrategy,
|
|
149
|
-
defaultAuthMode: this.amplifyConfig.aws_appsync_authenticationType,
|
|
150
|
-
modelName: model,
|
|
151
|
-
schema: this.schema,
|
|
152
|
-
});
|
|
144
|
+
public async resume(): Promise<void> {
|
|
145
|
+
await (this.runningProcesses.isOpen &&
|
|
146
|
+
this.runningProcesses.add(async onTerminate => {
|
|
147
|
+
if (
|
|
148
|
+
this.processing ||
|
|
149
|
+
!this.isReady() ||
|
|
150
|
+
!this.runningProcesses.isOpen
|
|
151
|
+
) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
153
154
|
|
|
154
|
-
|
|
155
|
+
this.processing = true;
|
|
156
|
+
let head: MutationEvent;
|
|
157
|
+
const namespaceName = USER;
|
|
158
|
+
|
|
159
|
+
// start to drain outbox
|
|
160
|
+
while (
|
|
161
|
+
this.processing &&
|
|
162
|
+
this.runningProcesses.isOpen &&
|
|
163
|
+
(head = await this.outbox.peek(this.storage)) !== undefined
|
|
164
|
+
) {
|
|
165
|
+
const { model, operation, data, condition } = head;
|
|
166
|
+
const modelConstructor = this.userClasses[
|
|
167
|
+
model
|
|
168
|
+
] as PersistentModelConstructor<MutationEvent>;
|
|
169
|
+
let result: GraphQLResult<Record<string, PersistentModel>>;
|
|
170
|
+
let opName: string;
|
|
171
|
+
let modelDefinition: SchemaModel;
|
|
155
172
|
|
|
156
|
-
let authModeAttempts = 0;
|
|
157
|
-
const authModeRetry = async () => {
|
|
158
173
|
try {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
174
|
+
const modelAuthModes = await getModelAuthModes({
|
|
175
|
+
authModeStrategy: this.authModeStrategy,
|
|
176
|
+
defaultAuthMode:
|
|
177
|
+
this.amplifyConfig.aws_appsync_authenticationType,
|
|
178
|
+
modelName: model,
|
|
179
|
+
schema: this.schema,
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const operationAuthModes = modelAuthModes[operation.toUpperCase()];
|
|
183
|
+
|
|
184
|
+
let authModeAttempts = 0;
|
|
185
|
+
const authModeRetry = async () => {
|
|
186
|
+
try {
|
|
187
|
+
logger.debug(
|
|
188
|
+
`Attempting mutation with authMode: ${operationAuthModes[authModeAttempts]}`
|
|
189
|
+
);
|
|
190
|
+
const response = await this.jitteredRetry(
|
|
191
|
+
namespaceName,
|
|
192
|
+
model,
|
|
193
|
+
operation,
|
|
194
|
+
data,
|
|
195
|
+
condition,
|
|
196
|
+
modelConstructor,
|
|
197
|
+
this.MutationEvent,
|
|
198
|
+
head,
|
|
199
|
+
operationAuthModes[authModeAttempts],
|
|
200
|
+
onTerminate
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
logger.debug(
|
|
204
|
+
`Mutation sent successfully with authMode: ${operationAuthModes[authModeAttempts]}`
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
return response;
|
|
208
|
+
} catch (error) {
|
|
209
|
+
authModeAttempts++;
|
|
210
|
+
if (authModeAttempts >= operationAuthModes.length) {
|
|
211
|
+
logger.debug(
|
|
212
|
+
`Mutation failed with authMode: ${
|
|
213
|
+
operationAuthModes[authModeAttempts - 1]
|
|
214
|
+
}`
|
|
215
|
+
);
|
|
216
|
+
throw error;
|
|
217
|
+
}
|
|
218
|
+
logger.debug(
|
|
219
|
+
`Mutation failed with authMode: ${
|
|
220
|
+
operationAuthModes[authModeAttempts - 1]
|
|
221
|
+
}. Retrying with authMode: ${
|
|
222
|
+
operationAuthModes[authModeAttempts]
|
|
223
|
+
}`
|
|
224
|
+
);
|
|
225
|
+
return await authModeRetry();
|
|
226
|
+
}
|
|
227
|
+
};
|
|
177
228
|
|
|
178
|
-
|
|
229
|
+
[result, opName, modelDefinition] = await authModeRetry();
|
|
179
230
|
} catch (error) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}`
|
|
186
|
-
);
|
|
187
|
-
throw error;
|
|
231
|
+
if (
|
|
232
|
+
error.message === 'Offline' ||
|
|
233
|
+
error.message === 'RetryMutation'
|
|
234
|
+
) {
|
|
235
|
+
continue;
|
|
188
236
|
}
|
|
189
|
-
logger.debug(
|
|
190
|
-
`Mutation failed with authMode: ${
|
|
191
|
-
operationAuthModes[authModeAttempts - 1]
|
|
192
|
-
}. Retrying with authMode: ${
|
|
193
|
-
operationAuthModes[authModeAttempts]
|
|
194
|
-
}`
|
|
195
|
-
);
|
|
196
|
-
return await authModeRetry();
|
|
197
237
|
}
|
|
198
|
-
};
|
|
199
238
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
if (result === undefined) {
|
|
208
|
-
logger.debug('done retrying');
|
|
209
|
-
await this.storage.runExclusive(async storage => {
|
|
210
|
-
await this.outbox.dequeue(storage);
|
|
211
|
-
});
|
|
212
|
-
continue;
|
|
213
|
-
}
|
|
239
|
+
if (result === undefined) {
|
|
240
|
+
logger.debug('done retrying');
|
|
241
|
+
await this.storage.runExclusive(async storage => {
|
|
242
|
+
await this.outbox.dequeue(storage);
|
|
243
|
+
});
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
214
246
|
|
|
215
|
-
|
|
216
|
-
|
|
247
|
+
const record = result.data[opName];
|
|
248
|
+
let hasMore = false;
|
|
217
249
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
250
|
+
await this.storage.runExclusive(async storage => {
|
|
251
|
+
// using runExclusive to prevent possible race condition
|
|
252
|
+
// when another record gets enqueued between dequeue and peek
|
|
253
|
+
await this.outbox.dequeue(storage, record, operation);
|
|
254
|
+
hasMore = (await this.outbox.peek(storage)) !== undefined;
|
|
255
|
+
});
|
|
224
256
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
257
|
+
this.observer.next({
|
|
258
|
+
operation,
|
|
259
|
+
modelDefinition,
|
|
260
|
+
model: record,
|
|
261
|
+
hasMore,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
232
264
|
|
|
233
|
-
|
|
234
|
-
|
|
265
|
+
// pauses itself
|
|
266
|
+
this.pause();
|
|
267
|
+
}, 'mutation resume loop'));
|
|
235
268
|
}
|
|
236
269
|
|
|
237
270
|
private async jitteredRetry(
|
|
@@ -243,7 +276,8 @@ class MutationProcessor {
|
|
|
243
276
|
modelConstructor: PersistentModelConstructor<PersistentModel>,
|
|
244
277
|
MutationEvent: PersistentModelConstructor<MutationEvent>,
|
|
245
278
|
mutationEvent: MutationEvent,
|
|
246
|
-
authMode: GRAPHQL_AUTH_MODE
|
|
279
|
+
authMode: GRAPHQL_AUTH_MODE,
|
|
280
|
+
onTerminate: Promise<void>
|
|
247
281
|
): Promise<
|
|
248
282
|
[GraphQLResult<Record<string, PersistentModel>>, string, SchemaModel]
|
|
249
283
|
> {
|
|
@@ -287,7 +321,10 @@ class MutationProcessor {
|
|
|
287
321
|
const result = <GraphQLResult<Record<string, PersistentModel>>>(
|
|
288
322
|
await this.amplifyContext.API.graphql(tryWith)
|
|
289
323
|
);
|
|
290
|
-
|
|
324
|
+
|
|
325
|
+
// `as any` because TypeScript doesn't seem to like passing tuples
|
|
326
|
+
// through generic params???
|
|
327
|
+
return [result, opName, modelDefinition] as any;
|
|
291
328
|
} catch (err) {
|
|
292
329
|
if (err.errors && err.errors.length > 0) {
|
|
293
330
|
const [error] = err.errors;
|
|
@@ -360,6 +397,8 @@ class MutationProcessor {
|
|
|
360
397
|
userAgentSuffix: USER_AGENT_SUFFIX_DATASTORE,
|
|
361
398
|
});
|
|
362
399
|
|
|
400
|
+
// onTerminate cancel graphql()
|
|
401
|
+
|
|
363
402
|
return [serverData, opName, modelDefinition];
|
|
364
403
|
}
|
|
365
404
|
|
|
@@ -384,7 +423,7 @@ class MutationProcessor {
|
|
|
384
423
|
throw new NonRetryableError('RetryMutation');
|
|
385
424
|
} else {
|
|
386
425
|
try {
|
|
387
|
-
|
|
426
|
+
this.errorHandler({
|
|
388
427
|
recoverySuggestion:
|
|
389
428
|
'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',
|
|
390
429
|
localModel: variables.input,
|
|
@@ -428,7 +467,8 @@ class MutationProcessor {
|
|
|
428
467
|
MutationEvent,
|
|
429
468
|
mutationEvent,
|
|
430
469
|
],
|
|
431
|
-
safeJitteredBackoff
|
|
470
|
+
safeJitteredBackoff,
|
|
471
|
+
onTerminate
|
|
432
472
|
);
|
|
433
473
|
}
|
|
434
474
|
|
|
@@ -452,63 +492,61 @@ class MutationProcessor {
|
|
|
452
492
|
|
|
453
493
|
// include all the fields that comprise a custom PK if one is specified
|
|
454
494
|
const deleteInput = {};
|
|
455
|
-
if (primaryKey
|
|
495
|
+
if (primaryKey?.length) {
|
|
456
496
|
for (const pkField of primaryKey) {
|
|
457
497
|
deleteInput[pkField] = parsedData[pkField];
|
|
458
498
|
}
|
|
459
499
|
} else {
|
|
460
|
-
deleteInput[
|
|
500
|
+
deleteInput[ID] = (<any>parsedData).id;
|
|
461
501
|
}
|
|
462
502
|
|
|
463
|
-
|
|
464
|
-
operation === TransformerMutationType.DELETE
|
|
465
|
-
? <ModelInstanceMetadata>deleteInput // For DELETE mutations, only PK is sent
|
|
466
|
-
: Object.values(modelDefinition.fields)
|
|
467
|
-
.filter(({ name, type, association }) => {
|
|
468
|
-
// connections
|
|
469
|
-
if (isModelFieldType(type)) {
|
|
470
|
-
// BELONGS_TO
|
|
471
|
-
if (
|
|
472
|
-
isTargetNameAssociation(association) &&
|
|
473
|
-
association.connectionType === 'BELONGS_TO'
|
|
474
|
-
) {
|
|
475
|
-
return true;
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
// All other connections
|
|
479
|
-
return false;
|
|
480
|
-
}
|
|
503
|
+
let mutationInput;
|
|
481
504
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
505
|
+
if (operation === TransformerMutationType.DELETE) {
|
|
506
|
+
// For DELETE mutations, only the key(s) are included in the input
|
|
507
|
+
mutationInput = <ModelInstanceMetadata>deleteInput;
|
|
508
|
+
} else {
|
|
509
|
+
// Otherwise, we construct the mutation input with the following logic
|
|
510
|
+
mutationInput = {};
|
|
511
|
+
const modelFields = Object.values(modelDefinition.fields);
|
|
512
|
+
|
|
513
|
+
for (const { name, type, association } of modelFields) {
|
|
514
|
+
// model fields should be stripped out from the input
|
|
515
|
+
if (isModelFieldType(type)) {
|
|
516
|
+
// except for belongs to relations - we need to replace them with the correct foreign key(s)
|
|
517
|
+
if (
|
|
518
|
+
isTargetNameAssociation(association) &&
|
|
519
|
+
association.connectionType === 'BELONGS_TO'
|
|
520
|
+
) {
|
|
521
|
+
const targetNames: string[] | undefined =
|
|
522
|
+
extractTargetNamesFromSrc(association);
|
|
523
|
+
|
|
524
|
+
if (targetNames) {
|
|
525
|
+
// instead of including the connected model itself, we add its key(s) to the mutation input
|
|
526
|
+
for (const targetName of targetNames) {
|
|
527
|
+
mutationInput[targetName] = parsedData[targetName];
|
|
485
528
|
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
533
|
+
// scalar fields / non-model types
|
|
486
534
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
if (
|
|
495
|
-
isModelFieldType(type) &&
|
|
496
|
-
isTargetNameAssociation(association)
|
|
497
|
-
) {
|
|
498
|
-
fieldName = association.targetName;
|
|
499
|
-
val = parsedData[fieldName];
|
|
500
|
-
}
|
|
535
|
+
if (operation === TransformerMutationType.UPDATE) {
|
|
536
|
+
if (!parsedData.hasOwnProperty(name)) {
|
|
537
|
+
// for update mutations - strip out a field if it's unchanged
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
501
541
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
return acc;
|
|
507
|
-
}, <typeof parsedData>{});
|
|
542
|
+
// all other fields are added to the input object
|
|
543
|
+
mutationInput[name] = parsedData[name];
|
|
544
|
+
}
|
|
545
|
+
}
|
|
508
546
|
|
|
509
547
|
// Build mutation variables input object
|
|
510
548
|
const input: ModelInstanceMetadata = {
|
|
511
|
-
...
|
|
549
|
+
...mutationInput,
|
|
512
550
|
_version,
|
|
513
551
|
};
|
|
514
552
|
|