@salesforce/lds-runtime-mobile 1.229.0-dev4 → 1.229.0-dev6
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/dist/main.js +97 -21
- package/package.json +16 -16
- package/sfdc/main.js +97 -21
package/dist/main.js
CHANGED
|
@@ -5077,8 +5077,12 @@ function uuidv4() {
|
|
|
5077
5077
|
|
|
5078
5078
|
const HTTP_HEADER_RETRY_AFTER = 'Retry-After';
|
|
5079
5079
|
const HTTP_HEADER_IDEMPOTENCY_KEY = 'Idempotency-Key';
|
|
5080
|
+
const ERROR_CODE_IDEMPOTENCY_FEATURE_NOT_ENABLED = 'IDEMPOTENCY_FEATURE_NOT_ENABLED';
|
|
5081
|
+
const ERROR_CODE_IDEMPOTENCY_NOT_SUPPORTED = 'IDEMPOTENCY_NOT_SUPPORTED';
|
|
5080
5082
|
const ERROR_CODE_IDEMPOTENCY_KEY_USED_DIFFERENT_USER = 'IDEMPOTENCY_KEY_USED_DIFFERENT_USER';
|
|
5081
5083
|
const ERROR_CODE_IDEMPOTENCY_CONCURRENT_REQUEST = 'IDEMPOTENCY_CONCURRENT_REQUEST';
|
|
5084
|
+
const ERROR_CODE_IDEMPOTENCY_KEY_ALREADY_USED = 'IDEMPOTENCY_KEY_ALREADY_USED';
|
|
5085
|
+
const ERROR_CODE_IDEMPOTENCY_BACKEND_OPERATION_ERROR = 'IDEMPOTENCY_BACKEND_OPERATION_ERROR';
|
|
5082
5086
|
/**
|
|
5083
5087
|
* Get the retry after in milliseconds from the response headers, undefined if not specified.
|
|
5084
5088
|
* The header could have two different format.
|
|
@@ -5108,7 +5112,9 @@ function buildLuvioOverrideForDraftAdapters(luvio, handler, extractTargetIdFromC
|
|
|
5108
5112
|
const dispatchResourceRequest = async function (resourceRequest, _context) {
|
|
5109
5113
|
const resourceRequestCopy = clone$1(resourceRequest);
|
|
5110
5114
|
resourceRequestCopy.headers = resourceRequestCopy.headers || {};
|
|
5111
|
-
|
|
5115
|
+
if (handler.hasIdempotencySupport()) {
|
|
5116
|
+
resourceRequestCopy.headers[HTTP_HEADER_IDEMPOTENCY_KEY] = uuidv4();
|
|
5117
|
+
}
|
|
5112
5118
|
// enable return extra fields for record creation and record update http call
|
|
5113
5119
|
if (resourceRequest.basePath === '/ui-api/records' &&
|
|
5114
5120
|
(resourceRequest.method === 'post' || resourceRequest.method === 'patch')) {
|
|
@@ -5944,6 +5950,7 @@ class AbstractResourceRequestActionHandler {
|
|
|
5944
5950
|
// the luvio store redirect table, during which a new draft might be enqueued
|
|
5945
5951
|
// which would not see a necessary mapping.
|
|
5946
5952
|
this.ephemeralRedirects = {};
|
|
5953
|
+
this.isIdempotencySupported = true;
|
|
5947
5954
|
}
|
|
5948
5955
|
enqueue(data) {
|
|
5949
5956
|
return this.draftQueue.enqueue(this.handlerId, data);
|
|
@@ -5973,21 +5980,43 @@ class AbstractResourceRequestActionHandler {
|
|
|
5973
5980
|
retryDelayInMs = getRetryAfterInMs(response.headers);
|
|
5974
5981
|
shouldRetry = true;
|
|
5975
5982
|
break;
|
|
5976
|
-
case HttpStatusCode.ServerError:
|
|
5983
|
+
case HttpStatusCode.ServerError: {
|
|
5977
5984
|
shouldRetry = true;
|
|
5985
|
+
if (this.handleIdempotencyServerError(response.body, updatedAction, false, ERROR_CODE_IDEMPOTENCY_BACKEND_OPERATION_ERROR)) {
|
|
5986
|
+
this.isIdempotencySupported = false;
|
|
5987
|
+
retryDelayInMs = 0;
|
|
5988
|
+
actionDataChanged = true;
|
|
5989
|
+
}
|
|
5978
5990
|
break;
|
|
5991
|
+
}
|
|
5979
5992
|
case 409 /* IdempotentWriteSpecificHttpStatusCode.Conflict */: {
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
5983
|
-
|
|
5993
|
+
if (this.isUiApiErrors(response.body)) {
|
|
5994
|
+
const errorCode = response.body[0].errorCode;
|
|
5995
|
+
if (this.handleIdempotencyServerError(response.body, updatedAction, true, ERROR_CODE_IDEMPOTENCY_KEY_USED_DIFFERENT_USER)) {
|
|
5996
|
+
retryDelayInMs = 0;
|
|
5997
|
+
actionDataChanged = true;
|
|
5998
|
+
}
|
|
5999
|
+
else if (errorCode === ERROR_CODE_IDEMPOTENCY_CONCURRENT_REQUEST) {
|
|
6000
|
+
retryDelayInMs = getRetryAfterInMs(response.headers);
|
|
6001
|
+
}
|
|
6002
|
+
shouldRetry = true;
|
|
6003
|
+
}
|
|
6004
|
+
break;
|
|
6005
|
+
}
|
|
6006
|
+
case HttpStatusCode.BadRequest: {
|
|
6007
|
+
if (this.handleIdempotencyServerError(response.body, updatedAction, false, ERROR_CODE_IDEMPOTENCY_FEATURE_NOT_ENABLED, ERROR_CODE_IDEMPOTENCY_NOT_SUPPORTED)) {
|
|
5984
6008
|
retryDelayInMs = 0;
|
|
5985
6009
|
actionDataChanged = true;
|
|
6010
|
+
shouldRetry = true;
|
|
5986
6011
|
}
|
|
5987
|
-
|
|
5988
|
-
|
|
6012
|
+
break;
|
|
6013
|
+
}
|
|
6014
|
+
case 422 /* IdempotentWriteSpecificHttpStatusCode.UnProcessableEntity */: {
|
|
6015
|
+
if (this.handleIdempotencyServerError(response.body, updatedAction, true, ERROR_CODE_IDEMPOTENCY_KEY_ALREADY_USED)) {
|
|
6016
|
+
retryDelayInMs = 0;
|
|
6017
|
+
actionDataChanged = true;
|
|
6018
|
+
shouldRetry = true;
|
|
5989
6019
|
}
|
|
5990
|
-
shouldRetry = true;
|
|
5991
6020
|
break;
|
|
5992
6021
|
}
|
|
5993
6022
|
}
|
|
@@ -6006,6 +6035,27 @@ class AbstractResourceRequestActionHandler {
|
|
|
6006
6035
|
return ProcessActionResult.NETWORK_ERROR;
|
|
6007
6036
|
}
|
|
6008
6037
|
}
|
|
6038
|
+
// true if response is an idempotency server error. updates or deletes idempotency key if the reponse is idempotency related error. Idempotency related error is in format of UiApiError array.
|
|
6039
|
+
handleIdempotencyServerError(responseBody, action, updateIdempotencyKey, ...targetErrorCodes) {
|
|
6040
|
+
if (this.isUiApiErrors(responseBody)) {
|
|
6041
|
+
const errorCode = responseBody[0].errorCode;
|
|
6042
|
+
if (targetErrorCodes.includes(errorCode)) {
|
|
6043
|
+
action.data.headers = action.data.headers || {};
|
|
6044
|
+
if (updateIdempotencyKey) {
|
|
6045
|
+
action.data.headers[HTTP_HEADER_IDEMPOTENCY_KEY] = uuidv4();
|
|
6046
|
+
}
|
|
6047
|
+
else {
|
|
6048
|
+
delete action.data.headers[HTTP_HEADER_IDEMPOTENCY_KEY];
|
|
6049
|
+
}
|
|
6050
|
+
return true;
|
|
6051
|
+
}
|
|
6052
|
+
}
|
|
6053
|
+
return false;
|
|
6054
|
+
}
|
|
6055
|
+
// checks if the body is an array of UiApiError. Sometimes the body has `enhancedErrorType` field as an error indicator(one example is the field validation failure). In such case Action being processed updates to an Error Action.
|
|
6056
|
+
isUiApiErrors(body) {
|
|
6057
|
+
return body !== undefined && Array.isArray(body) && body.length > 0 && body[0].errorCode;
|
|
6058
|
+
}
|
|
6009
6059
|
async buildPendingAction(request, queue) {
|
|
6010
6060
|
const targetId = await this.getIdFromRequest(request);
|
|
6011
6061
|
if (targetId === undefined) {
|
|
@@ -6219,6 +6269,10 @@ class AbstractResourceRequestActionHandler {
|
|
|
6219
6269
|
...targetData,
|
|
6220
6270
|
body: this.mergeRequestBody(targetBody, sourceBody),
|
|
6221
6271
|
};
|
|
6272
|
+
// Updates Idempotency key if target has one
|
|
6273
|
+
if (targetData.headers && targetData.headers[HTTP_HEADER_IDEMPOTENCY_KEY]) {
|
|
6274
|
+
merged.data.headers[HTTP_HEADER_IDEMPOTENCY_KEY] = uuidv4();
|
|
6275
|
+
}
|
|
6222
6276
|
// overlay metadata
|
|
6223
6277
|
merged.metadata = { ...targetMetadata, ...sourceMetadata };
|
|
6224
6278
|
// put status back to pending to auto upload if queue is active and targed is at the head.
|
|
@@ -6255,6 +6309,9 @@ class AbstractResourceRequestActionHandler {
|
|
|
6255
6309
|
getDraftIdsFromAction(action) {
|
|
6256
6310
|
return [action.targetId];
|
|
6257
6311
|
}
|
|
6312
|
+
hasIdempotencySupport() {
|
|
6313
|
+
return this.isIdempotencySupported;
|
|
6314
|
+
}
|
|
6258
6315
|
async ingestResponses(responses, action) {
|
|
6259
6316
|
const luvio = this.getLuvio();
|
|
6260
6317
|
await luvio.handleSuccessResponse(() => {
|
|
@@ -10215,16 +10272,18 @@ function injectFilter(filterNode, idState, parentPath, isParentPolymorphic, obje
|
|
|
10215
10272
|
if (!isIdField) {
|
|
10216
10273
|
let subSelectionNodes = [];
|
|
10217
10274
|
let subFieldsHasId = false;
|
|
10218
|
-
|
|
10219
|
-
|
|
10220
|
-
|
|
10221
|
-
|
|
10222
|
-
|
|
10223
|
-
|
|
10224
|
-
|
|
10225
|
-
|
|
10226
|
-
|
|
10227
|
-
|
|
10275
|
+
if (isSpanning) {
|
|
10276
|
+
filterNode.value.fields.forEach((subFieldNode) => {
|
|
10277
|
+
// Check if the filter field has the 'Id'
|
|
10278
|
+
if (isFieldAnIdField(subFieldNode.name.value, objectInfos[objectInfoName])) {
|
|
10279
|
+
subFieldsHasId = true;
|
|
10280
|
+
updateIDInfo(subFieldNode, idState, draftFunctions);
|
|
10281
|
+
}
|
|
10282
|
+
// try injecting the fields within predicate no matter it has relation or not.
|
|
10283
|
+
let subResults = injectFilter(subFieldNode, idState, curPath, isPolymorphicField, objectInfos, pathToObjectApiNamesMap, draftFunctions, existingFields ? existingFields[0] : undefined);
|
|
10284
|
+
subSelectionNodes = subSelectionNodes.concat(subResults);
|
|
10285
|
+
});
|
|
10286
|
+
}
|
|
10228
10287
|
if (!subFieldsHasId) {
|
|
10229
10288
|
// Check if the query field has the 'Id'
|
|
10230
10289
|
const existingIdsInQuery = existingFields &&
|
|
@@ -10253,6 +10312,7 @@ function injectFilter(filterNode, idState, parentPath, isParentPolymorphic, obje
|
|
|
10253
10312
|
}
|
|
10254
10313
|
//Inject Conditions: 1. Same field does not exist 2. Same fields has different children. 3. Filter spanning field does not have Id. 4. InLineFragment does not have the '__typename' field
|
|
10255
10314
|
if (!existingFields ||
|
|
10315
|
+
existingFields.length === 0 ||
|
|
10256
10316
|
subSelectionNodes.length > 0 ||
|
|
10257
10317
|
(isSpanning && !subFieldsHasId) ||
|
|
10258
10318
|
(isInlineFragment && !isTypeNameExisting)) {
|
|
@@ -12955,7 +13015,7 @@ function draftAwareGraphQLAdapterFactory(userId, objectInfoService, store, luvio
|
|
|
12955
13015
|
// Fulfilled snapshot (this only happens in this code path if
|
|
12956
13016
|
// the error is network error or 504), otherwise we spread over
|
|
12957
13017
|
// the non-eval'ed snapshot (which will be either Fulfilled or Stale)
|
|
12958
|
-
|
|
13018
|
+
const resultSnapshot = nonEvaluatedSnapshot.state === 'Error'
|
|
12959
13019
|
? createLocalEvalSnapshot(gqlResult, seenRecords, recordId, rebuildWithLocalEval)
|
|
12960
13020
|
: {
|
|
12961
13021
|
...nonEvaluatedSnapshot,
|
|
@@ -12964,6 +13024,22 @@ function draftAwareGraphQLAdapterFactory(userId, objectInfoService, store, luvio
|
|
|
12964
13024
|
seenRecords,
|
|
12965
13025
|
rebuildWithLocalEval,
|
|
12966
13026
|
};
|
|
13027
|
+
const { refresh, state } = resultSnapshot;
|
|
13028
|
+
if (state !== 'Error' && refresh) {
|
|
13029
|
+
// after refreshing a graphql snapshot, we want to force a rebuild regardless
|
|
13030
|
+
// of if the call failed or not or if the data changed or not because we want
|
|
13031
|
+
// to make sure any potential new drafts are picked up
|
|
13032
|
+
resultSnapshot.refresh = {
|
|
13033
|
+
...refresh,
|
|
13034
|
+
resolve: (config) => {
|
|
13035
|
+
return refresh.resolve(config).finally(() => {
|
|
13036
|
+
luvio.storePublish(resultSnapshot.recordId, undefined);
|
|
13037
|
+
luvio.storeBroadcast();
|
|
13038
|
+
});
|
|
13039
|
+
},
|
|
13040
|
+
};
|
|
13041
|
+
}
|
|
13042
|
+
return resultSnapshot;
|
|
12967
13043
|
};
|
|
12968
13044
|
}
|
|
12969
13045
|
|
|
@@ -16845,4 +16921,4 @@ register({
|
|
|
16845
16921
|
});
|
|
16846
16922
|
|
|
16847
16923
|
export { getRuntime, registerReportObserver, reportGraphqlQueryParseError };
|
|
16848
|
-
// version: 1.229.0-
|
|
16924
|
+
// version: 1.229.0-dev6-53f3d6911
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/lds-runtime-mobile",
|
|
3
|
-
"version": "1.229.0-
|
|
3
|
+
"version": "1.229.0-dev6",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
5
5
|
"description": "LDS runtime for mobile/hybrid environments.",
|
|
6
6
|
"main": "dist/main.js",
|
|
@@ -32,25 +32,25 @@
|
|
|
32
32
|
"release:corejar": "yarn build && ../core-build/scripts/core.js --name=lds-runtime-mobile"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@salesforce/lds-adapters-uiapi": "1.229.0-
|
|
36
|
-
"@salesforce/lds-bindings": "1.229.0-
|
|
37
|
-
"@salesforce/lds-instrumentation": "1.229.0-
|
|
38
|
-
"@salesforce/lds-priming": "1.229.0-
|
|
35
|
+
"@salesforce/lds-adapters-uiapi": "1.229.0-dev6",
|
|
36
|
+
"@salesforce/lds-bindings": "1.229.0-dev6",
|
|
37
|
+
"@salesforce/lds-instrumentation": "1.229.0-dev6",
|
|
38
|
+
"@salesforce/lds-priming": "1.229.0-dev6",
|
|
39
39
|
"@salesforce/user": "0.0.21",
|
|
40
40
|
"o11y": "244.0.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@salesforce/lds-adapters-graphql": "1.229.0-
|
|
44
|
-
"@salesforce/lds-drafts": "1.229.0-
|
|
45
|
-
"@salesforce/lds-drafts-adapters-uiapi": "1.229.0-
|
|
46
|
-
"@salesforce/lds-graphql-eval": "1.229.0-
|
|
47
|
-
"@salesforce/lds-network-adapter": "1.229.0-
|
|
48
|
-
"@salesforce/lds-network-nimbus": "1.229.0-
|
|
49
|
-
"@salesforce/lds-store-binary": "1.229.0-
|
|
50
|
-
"@salesforce/lds-store-nimbus": "1.229.0-
|
|
51
|
-
"@salesforce/lds-store-sql": "1.229.0-
|
|
52
|
-
"@salesforce/lds-utils-adapters": "1.229.0-
|
|
53
|
-
"@salesforce/nimbus-plugin-lds": "1.229.0-
|
|
43
|
+
"@salesforce/lds-adapters-graphql": "1.229.0-dev6",
|
|
44
|
+
"@salesforce/lds-drafts": "1.229.0-dev6",
|
|
45
|
+
"@salesforce/lds-drafts-adapters-uiapi": "1.229.0-dev6",
|
|
46
|
+
"@salesforce/lds-graphql-eval": "1.229.0-dev6",
|
|
47
|
+
"@salesforce/lds-network-adapter": "1.229.0-dev6",
|
|
48
|
+
"@salesforce/lds-network-nimbus": "1.229.0-dev6",
|
|
49
|
+
"@salesforce/lds-store-binary": "1.229.0-dev6",
|
|
50
|
+
"@salesforce/lds-store-nimbus": "1.229.0-dev6",
|
|
51
|
+
"@salesforce/lds-store-sql": "1.229.0-dev6",
|
|
52
|
+
"@salesforce/lds-utils-adapters": "1.229.0-dev6",
|
|
53
|
+
"@salesforce/nimbus-plugin-lds": "1.229.0-dev6",
|
|
54
54
|
"babel-plugin-dynamic-import-node": "^2.3.3",
|
|
55
55
|
"wait-for-expect": "^3.0.2"
|
|
56
56
|
},
|
package/sfdc/main.js
CHANGED
|
@@ -5077,8 +5077,12 @@ function uuidv4() {
|
|
|
5077
5077
|
|
|
5078
5078
|
const HTTP_HEADER_RETRY_AFTER = 'Retry-After';
|
|
5079
5079
|
const HTTP_HEADER_IDEMPOTENCY_KEY = 'Idempotency-Key';
|
|
5080
|
+
const ERROR_CODE_IDEMPOTENCY_FEATURE_NOT_ENABLED = 'IDEMPOTENCY_FEATURE_NOT_ENABLED';
|
|
5081
|
+
const ERROR_CODE_IDEMPOTENCY_NOT_SUPPORTED = 'IDEMPOTENCY_NOT_SUPPORTED';
|
|
5080
5082
|
const ERROR_CODE_IDEMPOTENCY_KEY_USED_DIFFERENT_USER = 'IDEMPOTENCY_KEY_USED_DIFFERENT_USER';
|
|
5081
5083
|
const ERROR_CODE_IDEMPOTENCY_CONCURRENT_REQUEST = 'IDEMPOTENCY_CONCURRENT_REQUEST';
|
|
5084
|
+
const ERROR_CODE_IDEMPOTENCY_KEY_ALREADY_USED = 'IDEMPOTENCY_KEY_ALREADY_USED';
|
|
5085
|
+
const ERROR_CODE_IDEMPOTENCY_BACKEND_OPERATION_ERROR = 'IDEMPOTENCY_BACKEND_OPERATION_ERROR';
|
|
5082
5086
|
/**
|
|
5083
5087
|
* Get the retry after in milliseconds from the response headers, undefined if not specified.
|
|
5084
5088
|
* The header could have two different format.
|
|
@@ -5108,7 +5112,9 @@ function buildLuvioOverrideForDraftAdapters(luvio, handler, extractTargetIdFromC
|
|
|
5108
5112
|
const dispatchResourceRequest = async function (resourceRequest, _context) {
|
|
5109
5113
|
const resourceRequestCopy = clone$1(resourceRequest);
|
|
5110
5114
|
resourceRequestCopy.headers = resourceRequestCopy.headers || {};
|
|
5111
|
-
|
|
5115
|
+
if (handler.hasIdempotencySupport()) {
|
|
5116
|
+
resourceRequestCopy.headers[HTTP_HEADER_IDEMPOTENCY_KEY] = uuidv4();
|
|
5117
|
+
}
|
|
5112
5118
|
// enable return extra fields for record creation and record update http call
|
|
5113
5119
|
if (resourceRequest.basePath === '/ui-api/records' &&
|
|
5114
5120
|
(resourceRequest.method === 'post' || resourceRequest.method === 'patch')) {
|
|
@@ -5944,6 +5950,7 @@ class AbstractResourceRequestActionHandler {
|
|
|
5944
5950
|
// the luvio store redirect table, during which a new draft might be enqueued
|
|
5945
5951
|
// which would not see a necessary mapping.
|
|
5946
5952
|
this.ephemeralRedirects = {};
|
|
5953
|
+
this.isIdempotencySupported = true;
|
|
5947
5954
|
}
|
|
5948
5955
|
enqueue(data) {
|
|
5949
5956
|
return this.draftQueue.enqueue(this.handlerId, data);
|
|
@@ -5973,21 +5980,43 @@ class AbstractResourceRequestActionHandler {
|
|
|
5973
5980
|
retryDelayInMs = getRetryAfterInMs(response.headers);
|
|
5974
5981
|
shouldRetry = true;
|
|
5975
5982
|
break;
|
|
5976
|
-
case HttpStatusCode.ServerError:
|
|
5983
|
+
case HttpStatusCode.ServerError: {
|
|
5977
5984
|
shouldRetry = true;
|
|
5985
|
+
if (this.handleIdempotencyServerError(response.body, updatedAction, false, ERROR_CODE_IDEMPOTENCY_BACKEND_OPERATION_ERROR)) {
|
|
5986
|
+
this.isIdempotencySupported = false;
|
|
5987
|
+
retryDelayInMs = 0;
|
|
5988
|
+
actionDataChanged = true;
|
|
5989
|
+
}
|
|
5978
5990
|
break;
|
|
5991
|
+
}
|
|
5979
5992
|
case 409 /* IdempotentWriteSpecificHttpStatusCode.Conflict */: {
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
5983
|
-
|
|
5993
|
+
if (this.isUiApiErrors(response.body)) {
|
|
5994
|
+
const errorCode = response.body[0].errorCode;
|
|
5995
|
+
if (this.handleIdempotencyServerError(response.body, updatedAction, true, ERROR_CODE_IDEMPOTENCY_KEY_USED_DIFFERENT_USER)) {
|
|
5996
|
+
retryDelayInMs = 0;
|
|
5997
|
+
actionDataChanged = true;
|
|
5998
|
+
}
|
|
5999
|
+
else if (errorCode === ERROR_CODE_IDEMPOTENCY_CONCURRENT_REQUEST) {
|
|
6000
|
+
retryDelayInMs = getRetryAfterInMs(response.headers);
|
|
6001
|
+
}
|
|
6002
|
+
shouldRetry = true;
|
|
6003
|
+
}
|
|
6004
|
+
break;
|
|
6005
|
+
}
|
|
6006
|
+
case HttpStatusCode.BadRequest: {
|
|
6007
|
+
if (this.handleIdempotencyServerError(response.body, updatedAction, false, ERROR_CODE_IDEMPOTENCY_FEATURE_NOT_ENABLED, ERROR_CODE_IDEMPOTENCY_NOT_SUPPORTED)) {
|
|
5984
6008
|
retryDelayInMs = 0;
|
|
5985
6009
|
actionDataChanged = true;
|
|
6010
|
+
shouldRetry = true;
|
|
5986
6011
|
}
|
|
5987
|
-
|
|
5988
|
-
|
|
6012
|
+
break;
|
|
6013
|
+
}
|
|
6014
|
+
case 422 /* IdempotentWriteSpecificHttpStatusCode.UnProcessableEntity */: {
|
|
6015
|
+
if (this.handleIdempotencyServerError(response.body, updatedAction, true, ERROR_CODE_IDEMPOTENCY_KEY_ALREADY_USED)) {
|
|
6016
|
+
retryDelayInMs = 0;
|
|
6017
|
+
actionDataChanged = true;
|
|
6018
|
+
shouldRetry = true;
|
|
5989
6019
|
}
|
|
5990
|
-
shouldRetry = true;
|
|
5991
6020
|
break;
|
|
5992
6021
|
}
|
|
5993
6022
|
}
|
|
@@ -6006,6 +6035,27 @@ class AbstractResourceRequestActionHandler {
|
|
|
6006
6035
|
return ProcessActionResult.NETWORK_ERROR;
|
|
6007
6036
|
}
|
|
6008
6037
|
}
|
|
6038
|
+
// true if response is an idempotency server error. updates or deletes idempotency key if the reponse is idempotency related error. Idempotency related error is in format of UiApiError array.
|
|
6039
|
+
handleIdempotencyServerError(responseBody, action, updateIdempotencyKey, ...targetErrorCodes) {
|
|
6040
|
+
if (this.isUiApiErrors(responseBody)) {
|
|
6041
|
+
const errorCode = responseBody[0].errorCode;
|
|
6042
|
+
if (targetErrorCodes.includes(errorCode)) {
|
|
6043
|
+
action.data.headers = action.data.headers || {};
|
|
6044
|
+
if (updateIdempotencyKey) {
|
|
6045
|
+
action.data.headers[HTTP_HEADER_IDEMPOTENCY_KEY] = uuidv4();
|
|
6046
|
+
}
|
|
6047
|
+
else {
|
|
6048
|
+
delete action.data.headers[HTTP_HEADER_IDEMPOTENCY_KEY];
|
|
6049
|
+
}
|
|
6050
|
+
return true;
|
|
6051
|
+
}
|
|
6052
|
+
}
|
|
6053
|
+
return false;
|
|
6054
|
+
}
|
|
6055
|
+
// checks if the body is an array of UiApiError. Sometimes the body has `enhancedErrorType` field as an error indicator(one example is the field validation failure). In such case Action being processed updates to an Error Action.
|
|
6056
|
+
isUiApiErrors(body) {
|
|
6057
|
+
return body !== undefined && Array.isArray(body) && body.length > 0 && body[0].errorCode;
|
|
6058
|
+
}
|
|
6009
6059
|
async buildPendingAction(request, queue) {
|
|
6010
6060
|
const targetId = await this.getIdFromRequest(request);
|
|
6011
6061
|
if (targetId === undefined) {
|
|
@@ -6219,6 +6269,10 @@ class AbstractResourceRequestActionHandler {
|
|
|
6219
6269
|
...targetData,
|
|
6220
6270
|
body: this.mergeRequestBody(targetBody, sourceBody),
|
|
6221
6271
|
};
|
|
6272
|
+
// Updates Idempotency key if target has one
|
|
6273
|
+
if (targetData.headers && targetData.headers[HTTP_HEADER_IDEMPOTENCY_KEY]) {
|
|
6274
|
+
merged.data.headers[HTTP_HEADER_IDEMPOTENCY_KEY] = uuidv4();
|
|
6275
|
+
}
|
|
6222
6276
|
// overlay metadata
|
|
6223
6277
|
merged.metadata = { ...targetMetadata, ...sourceMetadata };
|
|
6224
6278
|
// put status back to pending to auto upload if queue is active and targed is at the head.
|
|
@@ -6255,6 +6309,9 @@ class AbstractResourceRequestActionHandler {
|
|
|
6255
6309
|
getDraftIdsFromAction(action) {
|
|
6256
6310
|
return [action.targetId];
|
|
6257
6311
|
}
|
|
6312
|
+
hasIdempotencySupport() {
|
|
6313
|
+
return this.isIdempotencySupported;
|
|
6314
|
+
}
|
|
6258
6315
|
async ingestResponses(responses, action) {
|
|
6259
6316
|
const luvio = this.getLuvio();
|
|
6260
6317
|
await luvio.handleSuccessResponse(() => {
|
|
@@ -10215,16 +10272,18 @@ function injectFilter(filterNode, idState, parentPath, isParentPolymorphic, obje
|
|
|
10215
10272
|
if (!isIdField) {
|
|
10216
10273
|
let subSelectionNodes = [];
|
|
10217
10274
|
let subFieldsHasId = false;
|
|
10218
|
-
|
|
10219
|
-
|
|
10220
|
-
|
|
10221
|
-
|
|
10222
|
-
|
|
10223
|
-
|
|
10224
|
-
|
|
10225
|
-
|
|
10226
|
-
|
|
10227
|
-
|
|
10275
|
+
if (isSpanning) {
|
|
10276
|
+
filterNode.value.fields.forEach((subFieldNode) => {
|
|
10277
|
+
// Check if the filter field has the 'Id'
|
|
10278
|
+
if (isFieldAnIdField(subFieldNode.name.value, objectInfos[objectInfoName])) {
|
|
10279
|
+
subFieldsHasId = true;
|
|
10280
|
+
updateIDInfo(subFieldNode, idState, draftFunctions);
|
|
10281
|
+
}
|
|
10282
|
+
// try injecting the fields within predicate no matter it has relation or not.
|
|
10283
|
+
let subResults = injectFilter(subFieldNode, idState, curPath, isPolymorphicField, objectInfos, pathToObjectApiNamesMap, draftFunctions, existingFields ? existingFields[0] : undefined);
|
|
10284
|
+
subSelectionNodes = subSelectionNodes.concat(subResults);
|
|
10285
|
+
});
|
|
10286
|
+
}
|
|
10228
10287
|
if (!subFieldsHasId) {
|
|
10229
10288
|
// Check if the query field has the 'Id'
|
|
10230
10289
|
const existingIdsInQuery = existingFields &&
|
|
@@ -10253,6 +10312,7 @@ function injectFilter(filterNode, idState, parentPath, isParentPolymorphic, obje
|
|
|
10253
10312
|
}
|
|
10254
10313
|
//Inject Conditions: 1. Same field does not exist 2. Same fields has different children. 3. Filter spanning field does not have Id. 4. InLineFragment does not have the '__typename' field
|
|
10255
10314
|
if (!existingFields ||
|
|
10315
|
+
existingFields.length === 0 ||
|
|
10256
10316
|
subSelectionNodes.length > 0 ||
|
|
10257
10317
|
(isSpanning && !subFieldsHasId) ||
|
|
10258
10318
|
(isInlineFragment && !isTypeNameExisting)) {
|
|
@@ -12955,7 +13015,7 @@ function draftAwareGraphQLAdapterFactory(userId, objectInfoService, store, luvio
|
|
|
12955
13015
|
// Fulfilled snapshot (this only happens in this code path if
|
|
12956
13016
|
// the error is network error or 504), otherwise we spread over
|
|
12957
13017
|
// the non-eval'ed snapshot (which will be either Fulfilled or Stale)
|
|
12958
|
-
|
|
13018
|
+
const resultSnapshot = nonEvaluatedSnapshot.state === 'Error'
|
|
12959
13019
|
? createLocalEvalSnapshot(gqlResult, seenRecords, recordId, rebuildWithLocalEval)
|
|
12960
13020
|
: {
|
|
12961
13021
|
...nonEvaluatedSnapshot,
|
|
@@ -12964,6 +13024,22 @@ function draftAwareGraphQLAdapterFactory(userId, objectInfoService, store, luvio
|
|
|
12964
13024
|
seenRecords,
|
|
12965
13025
|
rebuildWithLocalEval,
|
|
12966
13026
|
};
|
|
13027
|
+
const { refresh, state } = resultSnapshot;
|
|
13028
|
+
if (state !== 'Error' && refresh) {
|
|
13029
|
+
// after refreshing a graphql snapshot, we want to force a rebuild regardless
|
|
13030
|
+
// of if the call failed or not or if the data changed or not because we want
|
|
13031
|
+
// to make sure any potential new drafts are picked up
|
|
13032
|
+
resultSnapshot.refresh = {
|
|
13033
|
+
...refresh,
|
|
13034
|
+
resolve: (config) => {
|
|
13035
|
+
return refresh.resolve(config).finally(() => {
|
|
13036
|
+
luvio.storePublish(resultSnapshot.recordId, undefined);
|
|
13037
|
+
luvio.storeBroadcast();
|
|
13038
|
+
});
|
|
13039
|
+
},
|
|
13040
|
+
};
|
|
13041
|
+
}
|
|
13042
|
+
return resultSnapshot;
|
|
12967
13043
|
};
|
|
12968
13044
|
}
|
|
12969
13045
|
|
|
@@ -16845,4 +16921,4 @@ register({
|
|
|
16845
16921
|
});
|
|
16846
16922
|
|
|
16847
16923
|
export { getRuntime, registerReportObserver, reportGraphqlQueryParseError };
|
|
16848
|
-
// version: 1.229.0-
|
|
16924
|
+
// version: 1.229.0-dev6-53f3d6911
|