@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.
Files changed (162) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/lib/authModeStrategies/multiAuthStrategy.js +17 -64
  3. package/lib/authModeStrategies/multiAuthStrategy.js.map +1 -1
  4. package/lib/datastore/datastore.js +682 -469
  5. package/lib/datastore/datastore.js.map +1 -1
  6. package/lib/index.js +2 -4
  7. package/lib/index.js.map +1 -1
  8. package/lib/predicates/index.js +12 -2
  9. package/lib/predicates/index.js.map +1 -1
  10. package/lib/storage/adapter/AsyncStorageAdapter.js +393 -298
  11. package/lib/storage/adapter/AsyncStorageAdapter.js.map +1 -1
  12. package/lib/storage/adapter/AsyncStorageDatabase.js +97 -122
  13. package/lib/storage/adapter/AsyncStorageDatabase.js.map +1 -1
  14. package/lib/storage/adapter/InMemoryStore.js +16 -67
  15. package/lib/storage/adapter/InMemoryStore.js.map +1 -1
  16. package/lib/storage/adapter/InMemoryStore.native.js +2 -4
  17. package/lib/storage/adapter/InMemoryStore.native.js.map +1 -1
  18. package/lib/storage/adapter/IndexedDBAdapter.js +497 -404
  19. package/lib/storage/adapter/IndexedDBAdapter.js.map +1 -1
  20. package/lib/storage/adapter/getDefaultAdapter/index.js +3 -5
  21. package/lib/storage/adapter/getDefaultAdapter/index.js.map +1 -1
  22. package/lib/storage/adapter/getDefaultAdapter/index.native.js +2 -4
  23. package/lib/storage/adapter/getDefaultAdapter/index.native.js.map +1 -1
  24. package/lib/storage/storage.js +129 -151
  25. package/lib/storage/storage.js.map +1 -1
  26. package/lib/sync/datastoreConnectivity.js +13 -17
  27. package/lib/sync/datastoreConnectivity.js.map +1 -1
  28. package/lib/sync/datastoreReachability/index.native.js +2 -4
  29. package/lib/sync/datastoreReachability/index.native.js.map +1 -1
  30. package/lib/sync/index.js +544 -488
  31. package/lib/sync/index.js.map +1 -1
  32. package/lib/sync/merger.js +21 -80
  33. package/lib/sync/merger.js.map +1 -1
  34. package/lib/sync/outbox.js +95 -162
  35. package/lib/sync/outbox.js.map +1 -1
  36. package/lib/sync/processors/errorMaps.js +4 -34
  37. package/lib/sync/processors/errorMaps.js.map +1 -1
  38. package/lib/sync/processors/mutation.js +285 -312
  39. package/lib/sync/processors/mutation.js.map +1 -1
  40. package/lib/sync/processors/subscription.js +218 -259
  41. package/lib/sync/processors/subscription.js.map +1 -1
  42. package/lib/sync/processors/sync.js +141 -212
  43. package/lib/sync/processors/sync.js.map +1 -1
  44. package/lib/sync/utils.js +50 -61
  45. package/lib/sync/utils.js.map +1 -1
  46. package/lib/types.js +13 -39
  47. package/lib/types.js.map +1 -1
  48. package/lib/util.js +429 -242
  49. package/lib/util.js.map +1 -1
  50. package/lib-esm/authModeStrategies/multiAuthStrategy.d.ts +11 -0
  51. package/lib-esm/authModeStrategies/multiAuthStrategy.js +13 -57
  52. package/lib-esm/authModeStrategies/multiAuthStrategy.js.map +1 -1
  53. package/lib-esm/datastore/datastore.d.ts +107 -17
  54. package/lib-esm/datastore/datastore.js +649 -433
  55. package/lib-esm/datastore/datastore.js.map +1 -1
  56. package/lib-esm/index.d.ts +3 -19
  57. package/lib-esm/predicates/index.d.ts +3 -2
  58. package/lib-esm/predicates/index.js +13 -3
  59. package/lib-esm/predicates/index.js.map +1 -1
  60. package/lib-esm/storage/adapter/AsyncStorageAdapter.d.ts +4 -3
  61. package/lib-esm/storage/adapter/AsyncStorageAdapter.js +356 -258
  62. package/lib-esm/storage/adapter/AsyncStorageAdapter.js.map +1 -1
  63. package/lib-esm/storage/adapter/AsyncStorageDatabase.d.ts +14 -4
  64. package/lib-esm/storage/adapter/AsyncStorageDatabase.js +67 -92
  65. package/lib-esm/storage/adapter/AsyncStorageDatabase.js.map +1 -1
  66. package/lib-esm/storage/adapter/InMemoryStore.js +1 -52
  67. package/lib-esm/storage/adapter/InMemoryStore.js.map +1 -1
  68. package/lib-esm/storage/adapter/IndexedDBAdapter.d.ts +26 -4
  69. package/lib-esm/storage/adapter/IndexedDBAdapter.js +446 -346
  70. package/lib-esm/storage/adapter/IndexedDBAdapter.js.map +1 -1
  71. package/lib-esm/storage/adapter/index.d.ts +1 -1
  72. package/lib-esm/storage/storage.d.ts +1 -1
  73. package/lib-esm/storage/storage.js +94 -113
  74. package/lib-esm/storage/storage.js.map +1 -1
  75. package/lib-esm/sync/datastoreConnectivity.d.ts +1 -0
  76. package/lib-esm/sync/datastoreConnectivity.js +10 -11
  77. package/lib-esm/sync/datastoreConnectivity.js.map +1 -1
  78. package/lib-esm/sync/index.d.ts +31 -5
  79. package/lib-esm/sync/index.js +525 -466
  80. package/lib-esm/sync/index.js.map +1 -1
  81. package/lib-esm/sync/merger.d.ts +9 -3
  82. package/lib-esm/sync/merger.js +14 -73
  83. package/lib-esm/sync/merger.js.map +1 -1
  84. package/lib-esm/sync/outbox.d.ts +2 -2
  85. package/lib-esm/sync/outbox.js +79 -146
  86. package/lib-esm/sync/outbox.js.map +1 -1
  87. package/lib-esm/sync/processors/errorMaps.js +1 -31
  88. package/lib-esm/sync/processors/errorMaps.js.map +1 -1
  89. package/lib-esm/sync/processors/mutation.d.ts +2 -0
  90. package/lib-esm/sync/processors/mutation.js +271 -295
  91. package/lib-esm/sync/processors/mutation.js.map +1 -1
  92. package/lib-esm/sync/processors/subscription.d.ts +2 -0
  93. package/lib-esm/sync/processors/subscription.js +214 -245
  94. package/lib-esm/sync/processors/subscription.js.map +1 -1
  95. package/lib-esm/sync/processors/sync.d.ts +2 -1
  96. package/lib-esm/sync/processors/sync.js +127 -195
  97. package/lib-esm/sync/processors/sync.js.map +1 -1
  98. package/lib-esm/sync/utils.d.ts +3 -2
  99. package/lib-esm/sync/utils.js +45 -57
  100. package/lib-esm/sync/utils.js.map +1 -1
  101. package/lib-esm/types.d.ts +65 -26
  102. package/lib-esm/types.js +10 -38
  103. package/lib-esm/types.js.map +1 -1
  104. package/lib-esm/util.d.ts +67 -24
  105. package/lib-esm/util.js +420 -233
  106. package/lib-esm/util.js.map +1 -1
  107. package/package.json +14 -7
  108. package/src/authModeStrategies/multiAuthStrategy.ts +12 -1
  109. package/src/datastore/datastore.ts +798 -397
  110. package/src/predicates/index.ts +32 -10
  111. package/src/storage/adapter/AsyncStorageAdapter.ts +309 -93
  112. package/src/storage/adapter/AsyncStorageDatabase.ts +74 -26
  113. package/src/storage/adapter/IndexedDBAdapter.ts +358 -134
  114. package/src/storage/adapter/index.ts +1 -1
  115. package/src/storage/storage.ts +69 -22
  116. package/src/sync/datastoreConnectivity.ts +6 -0
  117. package/src/sync/index.ts +521 -412
  118. package/src/sync/merger.ts +20 -4
  119. package/src/sync/outbox.ts +22 -9
  120. package/src/sync/processors/mutation.ts +188 -150
  121. package/src/sync/processors/subscription.ts +289 -253
  122. package/src/sync/processors/sync.ts +151 -138
  123. package/src/sync/utils.ts +67 -12
  124. package/src/types.ts +182 -30
  125. package/src/util.ts +505 -176
  126. package/build.js +0 -5
  127. package/dist/aws-amplify-datastore.js +0 -98255
  128. package/dist/aws-amplify-datastore.js.map +0 -1
  129. package/dist/aws-amplify-datastore.min.js +0 -66
  130. package/dist/aws-amplify-datastore.min.js.map +0 -1
  131. package/index.js +0 -7
  132. package/lib/authModeStrategies/defaultAuthStrategy.d.ts +0 -2
  133. package/lib/authModeStrategies/index.d.ts +0 -2
  134. package/lib/authModeStrategies/multiAuthStrategy.d.ts +0 -2
  135. package/lib/datastore/datastore.d.ts +0 -66
  136. package/lib/index.d.ts +0 -31
  137. package/lib/predicates/index.d.ts +0 -15
  138. package/lib/predicates/sort.d.ts +0 -8
  139. package/lib/ssr/index.d.ts +0 -3
  140. package/lib/storage/adapter/AsyncStorageAdapter.d.ts +0 -40
  141. package/lib/storage/adapter/AsyncStorageDatabase.d.ts +0 -29
  142. package/lib/storage/adapter/InMemoryStore.d.ts +0 -11
  143. package/lib/storage/adapter/InMemoryStore.native.d.ts +0 -1
  144. package/lib/storage/adapter/IndexedDBAdapter.d.ts +0 -37
  145. package/lib/storage/adapter/getDefaultAdapter/index.d.ts +0 -3
  146. package/lib/storage/adapter/getDefaultAdapter/index.native.d.ts +0 -3
  147. package/lib/storage/adapter/index.d.ts +0 -9
  148. package/lib/storage/storage.d.ts +0 -49
  149. package/lib/sync/datastoreConnectivity.d.ts +0 -15
  150. package/lib/sync/datastoreReachability/index.d.ts +0 -3
  151. package/lib/sync/datastoreReachability/index.native.d.ts +0 -3
  152. package/lib/sync/index.d.ts +0 -63
  153. package/lib/sync/merger.d.ts +0 -11
  154. package/lib/sync/outbox.d.ts +0 -27
  155. package/lib/sync/processors/errorMaps.d.ts +0 -17
  156. package/lib/sync/processors/mutation.d.ts +0 -56
  157. package/lib/sync/processors/subscription.d.ts +0 -31
  158. package/lib/sync/processors/sync.d.ts +0 -27
  159. package/lib/sync/utils.d.ts +0 -41
  160. package/lib/types.d.ts +0 -462
  161. package/lib/util.d.ts +0 -113
  162. package/webpack.config.dev.js +0 -6
@@ -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(storage, model);
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
- itemsMap.set(item.id, item);
60
+ const modelId = getIdentifierValue(modelDefinition, item);
61
+
62
+ itemsMap.set(modelId, item);
47
63
  }
48
64
 
49
65
  const page = [...itemsMap.values()];
@@ -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', model.id)
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', record.id).id('ne', this.inProgressMutationEventId)
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, id, _lastChangedAt, _deleted, ...previousData } =
228
- JSON.parse(previous.data);
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, { GraphQLResult, GRAPHQL_AUTH_MODE } from '@aws-amplify/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 { exhaustiveCheck, USER, USER_AGENT_SUFFIX_DATASTORE } from '../../util';
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
- this.resume();
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 resume(): Promise<void> {
126
- if (this.processing || !this.isReady()) {
127
- return;
128
- }
139
+ public async stop() {
140
+ await this.runningProcesses.close();
141
+ await this.runningProcesses.open();
142
+ }
129
143
 
130
- this.processing = true;
131
- let head: MutationEvent;
132
- const namespaceName = USER;
133
-
134
- // start to drain outbox
135
- while (
136
- this.processing &&
137
- (head = await this.outbox.peek(this.storage)) !== undefined
138
- ) {
139
- const { model, operation, data, condition } = head;
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
- const operationAuthModes = modelAuthModes[operation.toUpperCase()];
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
- logger.debug(
160
- `Attempting mutation with authMode: ${operationAuthModes[authModeAttempts]}`
161
- );
162
- const response = await this.jitteredRetry(
163
- namespaceName,
164
- model,
165
- operation,
166
- data,
167
- condition,
168
- modelConstructor,
169
- this.MutationEvent,
170
- head,
171
- operationAuthModes[authModeAttempts]
172
- );
173
-
174
- logger.debug(
175
- `Mutation sent successfully with authMode: ${operationAuthModes[authModeAttempts]}`
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
- return response;
229
+ [result, opName, modelDefinition] = await authModeRetry();
179
230
  } catch (error) {
180
- authModeAttempts++;
181
- if (authModeAttempts >= operationAuthModes.length) {
182
- logger.debug(
183
- `Mutation failed with authMode: ${
184
- operationAuthModes[authModeAttempts - 1]
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
- [result, opName, modelDefinition] = await authModeRetry();
201
- } catch (error) {
202
- if (error.message === 'Offline' || error.message === 'RetryMutation') {
203
- continue;
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
- const record = result.data[opName];
216
- let hasMore = false;
247
+ const record = result.data[opName];
248
+ let hasMore = false;
217
249
 
218
- await this.storage.runExclusive(async storage => {
219
- // using runExclusive to prevent possible race condition
220
- // when another record gets enqueued between dequeue and peek
221
- await this.outbox.dequeue(storage, record, operation);
222
- hasMore = (await this.outbox.peek(storage)) !== undefined;
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
- this.observer.next({
226
- operation,
227
- modelDefinition,
228
- model: record,
229
- hasMore,
230
- });
231
- }
257
+ this.observer.next({
258
+ operation,
259
+ modelDefinition,
260
+ model: record,
261
+ hasMore,
262
+ });
263
+ }
232
264
 
233
- // pauses itself
234
- this.pause();
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
- return [result, opName, modelDefinition];
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
- await this.errorHandler({
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 && primaryKey.length) {
495
+ if (primaryKey?.length) {
456
496
  for (const pkField of primaryKey) {
457
497
  deleteInput[pkField] = parsedData[pkField];
458
498
  }
459
499
  } else {
460
- deleteInput['id'] = parsedData.id;
500
+ deleteInput[ID] = (<any>parsedData).id;
461
501
  }
462
502
 
463
- const filteredData =
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
- if (operation === TransformerMutationType.UPDATE) {
483
- // this limits the update mutation input to changed fields only
484
- return parsedData.hasOwnProperty(name);
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
- // scalars and non-model types
488
- return true;
489
- })
490
- .map(({ name, type, association }) => {
491
- let fieldName = name;
492
- let val = parsedData[name];
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
- return [fieldName, val];
503
- })
504
- .reduce((acc, [k, v]) => {
505
- acc[k] = v;
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
- ...filteredData,
549
+ ...mutationInput,
512
550
  _version,
513
551
  };
514
552