@salesforce/lds-runtime-mobile 1.229.0-dev5 → 1.229.0-dev7

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 (3) hide show
  1. package/dist/main.js +67 -10
  2. package/package.json +16 -16
  3. package/sfdc/main.js +67 -10
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
- resourceRequestCopy.headers[HTTP_HEADER_IDEMPOTENCY_KEY] = uuidv4();
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
- const errorCode = response.body[0].errorCode;
5981
- if (errorCode === ERROR_CODE_IDEMPOTENCY_KEY_USED_DIFFERENT_USER) {
5982
- updatedAction.data.headers = updatedAction.data.headers || {};
5983
- updatedAction.data.headers[HTTP_HEADER_IDEMPOTENCY_KEY] = uuidv4();
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
- else if (errorCode === ERROR_CODE_IDEMPOTENCY_CONCURRENT_REQUEST) {
5988
- retryDelayInMs = getRetryAfterInMs(response.headers);
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(() => {
@@ -16864,4 +16921,4 @@ register({
16864
16921
  });
16865
16922
 
16866
16923
  export { getRuntime, registerReportObserver, reportGraphqlQueryParseError };
16867
- // version: 1.229.0-dev5-c763e7f91
16924
+ // version: 1.229.0-dev7-157e3a4db
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/lds-runtime-mobile",
3
- "version": "1.229.0-dev5",
3
+ "version": "1.229.0-dev7",
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-dev5",
36
- "@salesforce/lds-bindings": "1.229.0-dev5",
37
- "@salesforce/lds-instrumentation": "1.229.0-dev5",
38
- "@salesforce/lds-priming": "1.229.0-dev5",
35
+ "@salesforce/lds-adapters-uiapi": "1.229.0-dev7",
36
+ "@salesforce/lds-bindings": "1.229.0-dev7",
37
+ "@salesforce/lds-instrumentation": "1.229.0-dev7",
38
+ "@salesforce/lds-priming": "1.229.0-dev7",
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-dev5",
44
- "@salesforce/lds-drafts": "1.229.0-dev5",
45
- "@salesforce/lds-drafts-adapters-uiapi": "1.229.0-dev5",
46
- "@salesforce/lds-graphql-eval": "1.229.0-dev5",
47
- "@salesforce/lds-network-adapter": "1.229.0-dev5",
48
- "@salesforce/lds-network-nimbus": "1.229.0-dev5",
49
- "@salesforce/lds-store-binary": "1.229.0-dev5",
50
- "@salesforce/lds-store-nimbus": "1.229.0-dev5",
51
- "@salesforce/lds-store-sql": "1.229.0-dev5",
52
- "@salesforce/lds-utils-adapters": "1.229.0-dev5",
53
- "@salesforce/nimbus-plugin-lds": "1.229.0-dev5",
43
+ "@salesforce/lds-adapters-graphql": "1.229.0-dev7",
44
+ "@salesforce/lds-drafts": "1.229.0-dev7",
45
+ "@salesforce/lds-drafts-adapters-uiapi": "1.229.0-dev7",
46
+ "@salesforce/lds-graphql-eval": "1.229.0-dev7",
47
+ "@salesforce/lds-network-adapter": "1.229.0-dev7",
48
+ "@salesforce/lds-network-nimbus": "1.229.0-dev7",
49
+ "@salesforce/lds-store-binary": "1.229.0-dev7",
50
+ "@salesforce/lds-store-nimbus": "1.229.0-dev7",
51
+ "@salesforce/lds-store-sql": "1.229.0-dev7",
52
+ "@salesforce/lds-utils-adapters": "1.229.0-dev7",
53
+ "@salesforce/nimbus-plugin-lds": "1.229.0-dev7",
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
- resourceRequestCopy.headers[HTTP_HEADER_IDEMPOTENCY_KEY] = uuidv4();
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
- const errorCode = response.body[0].errorCode;
5981
- if (errorCode === ERROR_CODE_IDEMPOTENCY_KEY_USED_DIFFERENT_USER) {
5982
- updatedAction.data.headers = updatedAction.data.headers || {};
5983
- updatedAction.data.headers[HTTP_HEADER_IDEMPOTENCY_KEY] = uuidv4();
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
- else if (errorCode === ERROR_CODE_IDEMPOTENCY_CONCURRENT_REQUEST) {
5988
- retryDelayInMs = getRetryAfterInMs(response.headers);
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(() => {
@@ -16864,4 +16921,4 @@ register({
16864
16921
  });
16865
16922
 
16866
16923
  export { getRuntime, registerReportObserver, reportGraphqlQueryParseError };
16867
- // version: 1.229.0-dev5-c763e7f91
16924
+ // version: 1.229.0-dev7-157e3a4db