@salesforce/lds-runtime-mobile 1.252.0 → 1.256.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.js +127 -12
- package/dist/types/main.d.ts +2 -1
- package/package.json +16 -16
- package/sfdc/main.js +127 -12
- package/sfdc/types/main.d.ts +2 -1
package/dist/main.js
CHANGED
|
@@ -18,6 +18,7 @@ import excludeStaleRecordsGate from '@salesforce/gate/lds.graphqlEvalExcludeStal
|
|
|
18
18
|
import { parseAndVisit, Kind, buildSchema, isObjectType, defaultFieldResolver, visit, execute, parse as parse$7, extendSchema, isScalarType } from '@luvio/graphql-parser';
|
|
19
19
|
import { RECORD_ID_PREFIX, RECORD_FIELDS_KEY_JUNCTION, isStoreKeyRecordViewEntity, getRecordId18, RECORD_REPRESENTATION_NAME, extractRecordIdFromStoreKey, keyBuilderQuickActionExecutionRepresentation, ingestQuickActionExecutionRepresentation, keyBuilderContentDocumentCompositeRepresentation, getResponseCacheKeysContentDocumentCompositeRepresentation, keyBuilderFromTypeContentDocumentCompositeRepresentation, ingestContentDocumentCompositeRepresentation, keyBuilderRecord, RECORD_VIEW_ENTITY_ID_PREFIX, getTypeCacheKeysRecord, keyBuilderFromTypeRecordRepresentation, ingestRecord, RecordRepresentationRepresentationType, ObjectInfoRepresentationType, getRecordAdapterFactory, getObjectInfoAdapterFactory, getObjectInfosAdapterFactory, getObjectInfoDirectoryAdapterFactory, UiApiNamespace, RecordRepresentationType, RecordRepresentationTTL, RecordRepresentationVersion, getRecordsAdapterFactory } from '@salesforce/lds-adapters-uiapi';
|
|
20
20
|
import ldsIdempotencyWriteDisabled from '@salesforce/gate/lds.idempotencyWriteDisabled';
|
|
21
|
+
import ldsBackdatingEnabled from '@salesforce/gate/lds.backdatingEnabled';
|
|
21
22
|
import caseSensitiveUserId from '@salesforce/user/Id';
|
|
22
23
|
import { idleDetector, getInstrumentation } from 'o11y/client';
|
|
23
24
|
import ldsUseShortUrlGate from '@salesforce/gate/lds.useShortUrl';
|
|
@@ -4365,8 +4366,9 @@ function rootRecordQuery(selection, input) {
|
|
|
4365
4366
|
// If there is no metadata for this query or it somehow lacks a timestamp
|
|
4366
4367
|
// skip setting the root timestamp
|
|
4367
4368
|
if (queryMetadata !== undefined && queryMetadata.ingestionTimestamp !== undefined) {
|
|
4368
|
-
//
|
|
4369
|
-
|
|
4369
|
+
// adjust the timestamp to account for ingestion processing time
|
|
4370
|
+
// 30s is used because this is the default record TTL
|
|
4371
|
+
input.rootTimestamp = queryMetadata.ingestionTimestamp - 30000;
|
|
4370
4372
|
}
|
|
4371
4373
|
}
|
|
4372
4374
|
return recordQuery(selection, alias, apiName, [], input);
|
|
@@ -6049,6 +6051,8 @@ class DurableDraftStore {
|
|
|
6049
6051
|
}
|
|
6050
6052
|
}
|
|
6051
6053
|
|
|
6054
|
+
const DEFAULT_FIELD_LAST_MODIFIED_DATE$1 = 'LastModifiedDate';
|
|
6055
|
+
const DEFAULT_FIELD_CREATED_DATE$1 = 'CreatedDate';
|
|
6052
6056
|
class AbstractResourceRequestActionHandler {
|
|
6053
6057
|
constructor(draftQueue, networkAdapter, getLuvio) {
|
|
6054
6058
|
this.draftQueue = draftQueue;
|
|
@@ -6067,6 +6071,7 @@ class AbstractResourceRequestActionHandler {
|
|
|
6067
6071
|
this.isLdsIdempotencyWriteDisabled = ldsIdempotencyWriteDisabled.isOpen({
|
|
6068
6072
|
fallback: false,
|
|
6069
6073
|
});
|
|
6074
|
+
this.isBackdatingEnabled = ldsBackdatingEnabled.isOpen({ fallback: false });
|
|
6070
6075
|
}
|
|
6071
6076
|
enqueue(data) {
|
|
6072
6077
|
return this.draftQueue.enqueue(this.handlerId, data);
|
|
@@ -6137,6 +6142,17 @@ class AbstractResourceRequestActionHandler {
|
|
|
6137
6142
|
}
|
|
6138
6143
|
}
|
|
6139
6144
|
}
|
|
6145
|
+
if (this.isBackdatingEnabled &&
|
|
6146
|
+
response.status === HttpStatusCode.BadRequest &&
|
|
6147
|
+
this.isBackdatingError(response.body, action)) {
|
|
6148
|
+
updatedAction.timestamp = Date.now();
|
|
6149
|
+
updatedAction.data.body.fields = {
|
|
6150
|
+
...updatedAction.data.body.fields,
|
|
6151
|
+
LastModifiedDate: new Date(updatedAction.timestamp).toISOString(),
|
|
6152
|
+
};
|
|
6153
|
+
shouldRetry = true;
|
|
6154
|
+
actionDataChanged = true;
|
|
6155
|
+
}
|
|
6140
6156
|
await actionErrored(shouldRetry
|
|
6141
6157
|
? updatedAction
|
|
6142
6158
|
: {
|
|
@@ -6170,7 +6186,21 @@ class AbstractResourceRequestActionHandler {
|
|
|
6170
6186
|
}
|
|
6171
6187
|
// 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.
|
|
6172
6188
|
isUiApiErrors(body) {
|
|
6173
|
-
return body !== undefined &&
|
|
6189
|
+
return body !== undefined && isArray$3(body) && body.length > 0 && body[0].errorCode;
|
|
6190
|
+
}
|
|
6191
|
+
isBackdatingError(body, action) {
|
|
6192
|
+
if (body.enhancedErrorType &&
|
|
6193
|
+
body.enhancedErrorType === 'RecordError' &&
|
|
6194
|
+
body.output &&
|
|
6195
|
+
body.output.errors &&
|
|
6196
|
+
isArray$3(body.output.errors) &&
|
|
6197
|
+
body.output.errors.length > 0 &&
|
|
6198
|
+
action.data.body &&
|
|
6199
|
+
action.data.body.fields &&
|
|
6200
|
+
action.data.body.fields[DEFAULT_FIELD_LAST_MODIFIED_DATE$1]) {
|
|
6201
|
+
return body.output.errors.some((error) => error.errorCode === 'CollisionDetectedException');
|
|
6202
|
+
}
|
|
6203
|
+
return false;
|
|
6174
6204
|
}
|
|
6175
6205
|
async buildPendingAction(request, queue) {
|
|
6176
6206
|
const targetId = await this.getIdFromRequest(request);
|
|
@@ -7112,7 +7142,7 @@ function isArrayLike(x) {
|
|
|
7112
7142
|
|
|
7113
7143
|
const { create: create$4, keys: keys$4, values: values$2, entries: entries$3, assign: assign$4 } = Object;
|
|
7114
7144
|
const { stringify: stringify$4, parse: parse$4 } = JSON;
|
|
7115
|
-
const { isArray: isArray$2, from: from$
|
|
7145
|
+
const { isArray: isArray$2, from: from$2 } = Array;
|
|
7116
7146
|
|
|
7117
7147
|
function recordLoaderFactory(query) {
|
|
7118
7148
|
async function batchRecordQuery(ids) {
|
|
@@ -9326,8 +9356,9 @@ async function fetchIngestionTimeStampFromDatabase(apiName, info, args, query) {
|
|
|
9326
9356
|
const results = await query(sql, [key]);
|
|
9327
9357
|
const [timestamp] = results.rows.map((row) => row[0]);
|
|
9328
9358
|
if (timestamp !== null && typeof timestamp === 'number') {
|
|
9329
|
-
//
|
|
9330
|
-
|
|
9359
|
+
// adjust the timestamp to account for ingestion processing time
|
|
9360
|
+
// 30s is used because this is the default record TTL
|
|
9361
|
+
ingestionTimestamp = timestamp - 30000;
|
|
9331
9362
|
}
|
|
9332
9363
|
}
|
|
9333
9364
|
return ingestionTimestamp;
|
|
@@ -9483,7 +9514,7 @@ function generateRecordQueries(schema, objectInfoMap) {
|
|
|
9483
9514
|
recordQueries: extensionWrapper,
|
|
9484
9515
|
recordConnections,
|
|
9485
9516
|
recordExtensions,
|
|
9486
|
-
polyFieldTypeNameArr: from$
|
|
9517
|
+
polyFieldTypeNameArr: from$2(allPolymorphicFieldTypeNames),
|
|
9487
9518
|
};
|
|
9488
9519
|
}
|
|
9489
9520
|
/**
|
|
@@ -11457,7 +11488,7 @@ function referenceIdFieldForRelationship(relationshipName) {
|
|
|
11457
11488
|
const { keys: keys$3, values: values$1, create: create$3, assign: assign$3, freeze } = Object;
|
|
11458
11489
|
const { stringify: stringify$3, parse: parse$3 } = JSON;
|
|
11459
11490
|
const { shift } = Array.prototype;
|
|
11460
|
-
const { isArray: isArray$1 } = Array;
|
|
11491
|
+
const { isArray: isArray$1, from: from$1 } = Array;
|
|
11461
11492
|
|
|
11462
11493
|
function isFieldLink(field) {
|
|
11463
11494
|
const { value } = field;
|
|
@@ -12251,7 +12282,7 @@ class UiApiActionHandler extends AbstractResourceRequestActionHandler {
|
|
|
12251
12282
|
}
|
|
12252
12283
|
async buildPendingAction(request, queue) {
|
|
12253
12284
|
const resolvedRequest = this.resolveResourceRequest(request);
|
|
12254
|
-
|
|
12285
|
+
let pendingAction = (await super.buildPendingAction(resolvedRequest, queue));
|
|
12255
12286
|
const { tag, targetId } = pendingAction;
|
|
12256
12287
|
const targetApiName = await this.getApiNameForRecordId(targetId, tag, resolvedRequest);
|
|
12257
12288
|
pendingAction.metadata[LDS_ACTION_METADATA_API_NAME] = targetApiName;
|
|
@@ -12297,8 +12328,49 @@ class UiApiActionHandler extends AbstractResourceRequestActionHandler {
|
|
|
12297
12328
|
this.fetchReferenceRecord(referenceFields);
|
|
12298
12329
|
}
|
|
12299
12330
|
}
|
|
12331
|
+
// handles backdating
|
|
12332
|
+
if (this.isBackdatingEnabled &&
|
|
12333
|
+
pendingAction.data &&
|
|
12334
|
+
pendingAction.data.method !== 'delete' && //'delete' action does not have fields
|
|
12335
|
+
metaDataResult.data &&
|
|
12336
|
+
metaDataResult.data.objectInfos) {
|
|
12337
|
+
const objectInfo = metaDataResult.data.objectInfos.get(targetApiName);
|
|
12338
|
+
if (objectInfo === undefined) {
|
|
12339
|
+
throw Error(`Could not generate draft. Object info is missing`);
|
|
12340
|
+
}
|
|
12341
|
+
const appendedFields = this.getBackdatingFields(objectInfo, resolvedRequest.method, pendingAction);
|
|
12342
|
+
if (keys$3(appendedFields).length > 0) {
|
|
12343
|
+
pendingAction.data.body = {
|
|
12344
|
+
...pendingAction.data.body,
|
|
12345
|
+
fields: {
|
|
12346
|
+
...pendingAction.data.body.fields,
|
|
12347
|
+
...appendedFields,
|
|
12348
|
+
},
|
|
12349
|
+
};
|
|
12350
|
+
}
|
|
12351
|
+
}
|
|
12300
12352
|
return pendingAction;
|
|
12301
12353
|
}
|
|
12354
|
+
getBackdatingFields(objectInfo, requestMethod, pendingAction) {
|
|
12355
|
+
const fields = {};
|
|
12356
|
+
const actionFieldNames = keys$3(pendingAction.data.body.fields);
|
|
12357
|
+
if (requestMethod === 'post') {
|
|
12358
|
+
// `CreateRecord` with `CreatedDate` field
|
|
12359
|
+
if (isBackdatingFieldEditable(objectInfo, DEFAULT_FIELD_CREATED_DATE$1, 'createable', actionFieldNames)) {
|
|
12360
|
+
fields[DEFAULT_FIELD_CREATED_DATE$1] = new Date(pendingAction.timestamp).toISOString();
|
|
12361
|
+
}
|
|
12362
|
+
// `CreateRecord` with `LastModifiedDate` field
|
|
12363
|
+
if (isBackdatingFieldEditable(objectInfo, DEFAULT_FIELD_LAST_MODIFIED_DATE$1, 'createable', actionFieldNames)) {
|
|
12364
|
+
fields[DEFAULT_FIELD_LAST_MODIFIED_DATE$1] = new Date(pendingAction.timestamp).toISOString();
|
|
12365
|
+
}
|
|
12366
|
+
}
|
|
12367
|
+
// `UpdateRecord` with `LastModifedDate` field
|
|
12368
|
+
if (requestMethod === 'patch' &&
|
|
12369
|
+
isBackdatingFieldEditable(objectInfo, DEFAULT_FIELD_LAST_MODIFIED_DATE$1, 'updateable', actionFieldNames)) {
|
|
12370
|
+
fields[DEFAULT_FIELD_LAST_MODIFIED_DATE$1] = new Date(pendingAction.timestamp).toISOString();
|
|
12371
|
+
}
|
|
12372
|
+
return fields;
|
|
12373
|
+
}
|
|
12302
12374
|
async fetchReferenceRecord(referenceFields) {
|
|
12303
12375
|
const promises = referenceFields.map(async (referenceFieldInfo) => {
|
|
12304
12376
|
const apiName = await this.identifyApiName(referenceFieldInfo.id, referenceFieldInfo.field);
|
|
@@ -12589,12 +12661,26 @@ class UiApiActionHandler extends AbstractResourceRequestActionHandler {
|
|
|
12589
12661
|
mergeRequestBody(targetBody, sourceBody) {
|
|
12590
12662
|
// IMPORTANT: spread operator isn't deep clone so we have to individually
|
|
12591
12663
|
// spread nested objects (just "fields" for RecordInputReps)
|
|
12664
|
+
const targetInput = targetBody;
|
|
12665
|
+
const sourceInput = sourceBody;
|
|
12666
|
+
// choose the max if both source and target have the `LastModifiedDate` property.
|
|
12667
|
+
// there is no need to check 'CreatedDate` since source and target cannot be `create` action at the same time
|
|
12668
|
+
const lastModifiedFields = {};
|
|
12669
|
+
const targetLastModifiedField = targetInput.fields[DEFAULT_FIELD_LAST_MODIFIED_DATE$1];
|
|
12670
|
+
const sourceLastModifiedField = sourceInput.fields[DEFAULT_FIELD_LAST_MODIFIED_DATE$1];
|
|
12671
|
+
if (targetLastModifiedField && sourceLastModifiedField) {
|
|
12672
|
+
lastModifiedFields[DEFAULT_FIELD_LAST_MODIFIED_DATE$1] =
|
|
12673
|
+
targetLastModifiedField > sourceLastModifiedField
|
|
12674
|
+
? targetLastModifiedField
|
|
12675
|
+
: sourceLastModifiedField;
|
|
12676
|
+
}
|
|
12592
12677
|
return {
|
|
12593
12678
|
...targetBody,
|
|
12594
12679
|
...sourceBody,
|
|
12595
12680
|
fields: {
|
|
12596
12681
|
...targetBody.fields,
|
|
12597
12682
|
...sourceBody.fields,
|
|
12683
|
+
...lastModifiedFields,
|
|
12598
12684
|
},
|
|
12599
12685
|
};
|
|
12600
12686
|
}
|
|
@@ -12602,6 +12688,13 @@ class UiApiActionHandler extends AbstractResourceRequestActionHandler {
|
|
|
12602
12688
|
function isField(key, data) {
|
|
12603
12689
|
return isStoreKeyRecordField(key);
|
|
12604
12690
|
}
|
|
12691
|
+
// true if `createable/updateable` property of backdating field is true and draft does not include that fields. If the customer specifies
|
|
12692
|
+
// `LastModifiedDate`, it is not overwritten.
|
|
12693
|
+
function isBackdatingFieldEditable(objectInfo, backdatingFieldName, attributeName, draftActionFieldNames) {
|
|
12694
|
+
return (objectInfo.fields[backdatingFieldName] &&
|
|
12695
|
+
objectInfo.fields[backdatingFieldName][attributeName] &&
|
|
12696
|
+
!draftActionFieldNames.includes(backdatingFieldName));
|
|
12697
|
+
}
|
|
12605
12698
|
|
|
12606
12699
|
/**
|
|
12607
12700
|
* Records are stored in the durable store with scalar fields denormalized. This function takes that denoramlized
|
|
@@ -13636,7 +13729,7 @@ class ContentDocumentCompositeRepresentationActionHandler extends AbstractResour
|
|
|
13636
13729
|
});
|
|
13637
13730
|
pendingAction.metadata[CONTENT_DOCUMENT_LINK_DRAFT_ID_KEY] = contentDocumentLinkId;
|
|
13638
13731
|
// assert that object infos and references exist
|
|
13639
|
-
const metaDataResult = await this.draftRecordService.getRecordDraftMetadata(
|
|
13732
|
+
const metaDataResult = await this.draftRecordService.getRecordDraftMetadata(contentVersionId, pendingAction);
|
|
13640
13733
|
if (metaDataResult === undefined) {
|
|
13641
13734
|
throw Error('No metadata for draft');
|
|
13642
13735
|
}
|
|
@@ -13650,8 +13743,30 @@ class ContentDocumentCompositeRepresentationActionHandler extends AbstractResour
|
|
|
13650
13743
|
throw new DraftSynthesisError(`unknown draft precondition failed, metaDataResult: ${JSON.stringify(metaDataResult)}`, 'UNKNOWN');
|
|
13651
13744
|
}
|
|
13652
13745
|
}
|
|
13746
|
+
else if (this.isBackdatingEnabled) {
|
|
13747
|
+
const contentVersionObjectInfo = metaDataResult.data.objectInfos.get(CONTENT_VERSION_API_NAME);
|
|
13748
|
+
if (contentVersionObjectInfo === undefined) {
|
|
13749
|
+
throw Error(`Could not generate draft. Object info is missing`);
|
|
13750
|
+
}
|
|
13751
|
+
// `ContentVersion` support back-dating. Ui-api team has WI @W-14219481 to support `CreatedDate` and `LastModifiedDate`.
|
|
13752
|
+
// right now, these two fields are ignored by server
|
|
13753
|
+
pendingAction.data.body.namedEntries = [
|
|
13754
|
+
...pendingAction.data.body.namedEntries,
|
|
13755
|
+
...this.getBackdatingNameEntries(contentVersionObjectInfo, pendingAction.timestamp),
|
|
13756
|
+
];
|
|
13757
|
+
}
|
|
13653
13758
|
return pendingAction;
|
|
13654
13759
|
}
|
|
13760
|
+
getBackdatingNameEntries(objectInfo, timestamp) {
|
|
13761
|
+
return [DEFAULT_FIELD_CREATED_DATE$1, DEFAULT_FIELD_LAST_MODIFIED_DATE$1]
|
|
13762
|
+
.filter((fieldName) => objectInfo.fields[fieldName] && objectInfo.fields[fieldName].createable)
|
|
13763
|
+
.map((fieldValue) => {
|
|
13764
|
+
return {
|
|
13765
|
+
name: fieldValue,
|
|
13766
|
+
value: new Date(timestamp).toISOString(),
|
|
13767
|
+
};
|
|
13768
|
+
});
|
|
13769
|
+
}
|
|
13655
13770
|
/* istanbul ignore next */
|
|
13656
13771
|
canHandlePublish(_key) {
|
|
13657
13772
|
// no need to touch publishing
|
|
@@ -17558,5 +17673,5 @@ register({
|
|
|
17558
17673
|
instrument: instrument,
|
|
17559
17674
|
});
|
|
17560
17675
|
|
|
17561
|
-
export { getRuntime, registerReportObserver, reportGraphqlQueryParseError };
|
|
17562
|
-
// version: 1.
|
|
17676
|
+
export { O11Y_NAMESPACE_LDS_MOBILE, getRuntime, registerReportObserver, reportGraphqlQueryParseError };
|
|
17677
|
+
// version: 1.256.0-ad6a66c18
|
package/dist/types/main.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getRuntime } from './runtime';
|
|
2
|
-
|
|
2
|
+
import { O11Y_NAMESPACE_LDS_MOBILE } from './instrumentation/metrics';
|
|
3
|
+
export { getRuntime, O11Y_NAMESPACE_LDS_MOBILE };
|
|
3
4
|
export type { ObjectInfoService } from './utils/ObjectInfoService';
|
|
4
5
|
export type { ObservabilityContext } from '@salesforce/nimbus-plugin-lds';
|
|
5
6
|
export { registerReportObserver } from './instrumentation/instrumentMobileAdapter';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/lds-runtime-mobile",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.256.0",
|
|
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": "
|
|
36
|
-
"@salesforce/lds-bindings": "
|
|
37
|
-
"@salesforce/lds-instrumentation": "
|
|
38
|
-
"@salesforce/lds-priming": "
|
|
35
|
+
"@salesforce/lds-adapters-uiapi": "^1.256.0",
|
|
36
|
+
"@salesforce/lds-bindings": "^1.256.0",
|
|
37
|
+
"@salesforce/lds-instrumentation": "^1.256.0",
|
|
38
|
+
"@salesforce/lds-priming": "^1.256.0",
|
|
39
39
|
"@salesforce/user": "0.0.21",
|
|
40
40
|
"o11y": "244.0.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@salesforce/lds-adapters-graphql": "
|
|
44
|
-
"@salesforce/lds-drafts": "
|
|
45
|
-
"@salesforce/lds-drafts-adapters-uiapi": "
|
|
46
|
-
"@salesforce/lds-graphql-eval": "
|
|
47
|
-
"@salesforce/lds-network-adapter": "
|
|
48
|
-
"@salesforce/lds-network-nimbus": "
|
|
49
|
-
"@salesforce/lds-store-
|
|
50
|
-
"@salesforce/lds-store-
|
|
51
|
-
"@salesforce/lds-store-sql": "
|
|
52
|
-
"@salesforce/lds-utils-adapters": "
|
|
53
|
-
"@salesforce/nimbus-plugin-lds": "
|
|
43
|
+
"@salesforce/lds-adapters-graphql": "^1.256.0",
|
|
44
|
+
"@salesforce/lds-drafts": "^1.256.0",
|
|
45
|
+
"@salesforce/lds-drafts-adapters-uiapi": "^1.256.0",
|
|
46
|
+
"@salesforce/lds-graphql-eval": "^1.256.0",
|
|
47
|
+
"@salesforce/lds-network-adapter": "^1.256.0",
|
|
48
|
+
"@salesforce/lds-network-nimbus": "^1.256.0",
|
|
49
|
+
"@salesforce/lds-store-binary": "^1.256.0",
|
|
50
|
+
"@salesforce/lds-store-nimbus": "^1.256.0",
|
|
51
|
+
"@salesforce/lds-store-sql": "^1.256.0",
|
|
52
|
+
"@salesforce/lds-utils-adapters": "^1.256.0",
|
|
53
|
+
"@salesforce/nimbus-plugin-lds": "^1.256.0",
|
|
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
|
@@ -18,6 +18,7 @@ import excludeStaleRecordsGate from '@salesforce/gate/lds.graphqlEvalExcludeStal
|
|
|
18
18
|
import { parseAndVisit, Kind, buildSchema, isObjectType, defaultFieldResolver, visit, execute, parse as parse$7, extendSchema, isScalarType } from 'force/ldsGraphqlParser';
|
|
19
19
|
import { RECORD_ID_PREFIX, RECORD_FIELDS_KEY_JUNCTION, isStoreKeyRecordViewEntity, getRecordId18, RECORD_REPRESENTATION_NAME, extractRecordIdFromStoreKey, keyBuilderQuickActionExecutionRepresentation, ingestQuickActionExecutionRepresentation, keyBuilderContentDocumentCompositeRepresentation, getResponseCacheKeysContentDocumentCompositeRepresentation, keyBuilderFromTypeContentDocumentCompositeRepresentation, ingestContentDocumentCompositeRepresentation, keyBuilderRecord, RECORD_VIEW_ENTITY_ID_PREFIX, getTypeCacheKeysRecord, keyBuilderFromTypeRecordRepresentation, ingestRecord, RecordRepresentationRepresentationType, ObjectInfoRepresentationType, getRecordAdapterFactory, getObjectInfoAdapterFactory, getObjectInfosAdapterFactory, getObjectInfoDirectoryAdapterFactory, UiApiNamespace, RecordRepresentationType, RecordRepresentationTTL, RecordRepresentationVersion, getRecordsAdapterFactory } from 'force/ldsAdaptersUiapi';
|
|
20
20
|
import ldsIdempotencyWriteDisabled from '@salesforce/gate/lds.idempotencyWriteDisabled';
|
|
21
|
+
import ldsBackdatingEnabled from '@salesforce/gate/lds.backdatingEnabled';
|
|
21
22
|
import caseSensitiveUserId from '@salesforce/user/Id';
|
|
22
23
|
import { idleDetector, getInstrumentation } from 'o11y/client';
|
|
23
24
|
import ldsUseShortUrlGate from '@salesforce/gate/lds.useShortUrl';
|
|
@@ -4365,8 +4366,9 @@ function rootRecordQuery(selection, input) {
|
|
|
4365
4366
|
// If there is no metadata for this query or it somehow lacks a timestamp
|
|
4366
4367
|
// skip setting the root timestamp
|
|
4367
4368
|
if (queryMetadata !== undefined && queryMetadata.ingestionTimestamp !== undefined) {
|
|
4368
|
-
//
|
|
4369
|
-
|
|
4369
|
+
// adjust the timestamp to account for ingestion processing time
|
|
4370
|
+
// 30s is used because this is the default record TTL
|
|
4371
|
+
input.rootTimestamp = queryMetadata.ingestionTimestamp - 30000;
|
|
4370
4372
|
}
|
|
4371
4373
|
}
|
|
4372
4374
|
return recordQuery(selection, alias, apiName, [], input);
|
|
@@ -6049,6 +6051,8 @@ class DurableDraftStore {
|
|
|
6049
6051
|
}
|
|
6050
6052
|
}
|
|
6051
6053
|
|
|
6054
|
+
const DEFAULT_FIELD_LAST_MODIFIED_DATE$1 = 'LastModifiedDate';
|
|
6055
|
+
const DEFAULT_FIELD_CREATED_DATE$1 = 'CreatedDate';
|
|
6052
6056
|
class AbstractResourceRequestActionHandler {
|
|
6053
6057
|
constructor(draftQueue, networkAdapter, getLuvio) {
|
|
6054
6058
|
this.draftQueue = draftQueue;
|
|
@@ -6067,6 +6071,7 @@ class AbstractResourceRequestActionHandler {
|
|
|
6067
6071
|
this.isLdsIdempotencyWriteDisabled = ldsIdempotencyWriteDisabled.isOpen({
|
|
6068
6072
|
fallback: false,
|
|
6069
6073
|
});
|
|
6074
|
+
this.isBackdatingEnabled = ldsBackdatingEnabled.isOpen({ fallback: false });
|
|
6070
6075
|
}
|
|
6071
6076
|
enqueue(data) {
|
|
6072
6077
|
return this.draftQueue.enqueue(this.handlerId, data);
|
|
@@ -6137,6 +6142,17 @@ class AbstractResourceRequestActionHandler {
|
|
|
6137
6142
|
}
|
|
6138
6143
|
}
|
|
6139
6144
|
}
|
|
6145
|
+
if (this.isBackdatingEnabled &&
|
|
6146
|
+
response.status === HttpStatusCode.BadRequest &&
|
|
6147
|
+
this.isBackdatingError(response.body, action)) {
|
|
6148
|
+
updatedAction.timestamp = Date.now();
|
|
6149
|
+
updatedAction.data.body.fields = {
|
|
6150
|
+
...updatedAction.data.body.fields,
|
|
6151
|
+
LastModifiedDate: new Date(updatedAction.timestamp).toISOString(),
|
|
6152
|
+
};
|
|
6153
|
+
shouldRetry = true;
|
|
6154
|
+
actionDataChanged = true;
|
|
6155
|
+
}
|
|
6140
6156
|
await actionErrored(shouldRetry
|
|
6141
6157
|
? updatedAction
|
|
6142
6158
|
: {
|
|
@@ -6170,7 +6186,21 @@ class AbstractResourceRequestActionHandler {
|
|
|
6170
6186
|
}
|
|
6171
6187
|
// 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.
|
|
6172
6188
|
isUiApiErrors(body) {
|
|
6173
|
-
return body !== undefined &&
|
|
6189
|
+
return body !== undefined && isArray$3(body) && body.length > 0 && body[0].errorCode;
|
|
6190
|
+
}
|
|
6191
|
+
isBackdatingError(body, action) {
|
|
6192
|
+
if (body.enhancedErrorType &&
|
|
6193
|
+
body.enhancedErrorType === 'RecordError' &&
|
|
6194
|
+
body.output &&
|
|
6195
|
+
body.output.errors &&
|
|
6196
|
+
isArray$3(body.output.errors) &&
|
|
6197
|
+
body.output.errors.length > 0 &&
|
|
6198
|
+
action.data.body &&
|
|
6199
|
+
action.data.body.fields &&
|
|
6200
|
+
action.data.body.fields[DEFAULT_FIELD_LAST_MODIFIED_DATE$1]) {
|
|
6201
|
+
return body.output.errors.some((error) => error.errorCode === 'CollisionDetectedException');
|
|
6202
|
+
}
|
|
6203
|
+
return false;
|
|
6174
6204
|
}
|
|
6175
6205
|
async buildPendingAction(request, queue) {
|
|
6176
6206
|
const targetId = await this.getIdFromRequest(request);
|
|
@@ -7112,7 +7142,7 @@ function isArrayLike(x) {
|
|
|
7112
7142
|
|
|
7113
7143
|
const { create: create$4, keys: keys$4, values: values$2, entries: entries$3, assign: assign$4 } = Object;
|
|
7114
7144
|
const { stringify: stringify$4, parse: parse$4 } = JSON;
|
|
7115
|
-
const { isArray: isArray$2, from: from$
|
|
7145
|
+
const { isArray: isArray$2, from: from$2 } = Array;
|
|
7116
7146
|
|
|
7117
7147
|
function recordLoaderFactory(query) {
|
|
7118
7148
|
async function batchRecordQuery(ids) {
|
|
@@ -9326,8 +9356,9 @@ async function fetchIngestionTimeStampFromDatabase(apiName, info, args, query) {
|
|
|
9326
9356
|
const results = await query(sql, [key]);
|
|
9327
9357
|
const [timestamp] = results.rows.map((row) => row[0]);
|
|
9328
9358
|
if (timestamp !== null && typeof timestamp === 'number') {
|
|
9329
|
-
//
|
|
9330
|
-
|
|
9359
|
+
// adjust the timestamp to account for ingestion processing time
|
|
9360
|
+
// 30s is used because this is the default record TTL
|
|
9361
|
+
ingestionTimestamp = timestamp - 30000;
|
|
9331
9362
|
}
|
|
9332
9363
|
}
|
|
9333
9364
|
return ingestionTimestamp;
|
|
@@ -9483,7 +9514,7 @@ function generateRecordQueries(schema, objectInfoMap) {
|
|
|
9483
9514
|
recordQueries: extensionWrapper,
|
|
9484
9515
|
recordConnections,
|
|
9485
9516
|
recordExtensions,
|
|
9486
|
-
polyFieldTypeNameArr: from$
|
|
9517
|
+
polyFieldTypeNameArr: from$2(allPolymorphicFieldTypeNames),
|
|
9487
9518
|
};
|
|
9488
9519
|
}
|
|
9489
9520
|
/**
|
|
@@ -11457,7 +11488,7 @@ function referenceIdFieldForRelationship(relationshipName) {
|
|
|
11457
11488
|
const { keys: keys$3, values: values$1, create: create$3, assign: assign$3, freeze } = Object;
|
|
11458
11489
|
const { stringify: stringify$3, parse: parse$3 } = JSON;
|
|
11459
11490
|
const { shift } = Array.prototype;
|
|
11460
|
-
const { isArray: isArray$1 } = Array;
|
|
11491
|
+
const { isArray: isArray$1, from: from$1 } = Array;
|
|
11461
11492
|
|
|
11462
11493
|
function isFieldLink(field) {
|
|
11463
11494
|
const { value } = field;
|
|
@@ -12251,7 +12282,7 @@ class UiApiActionHandler extends AbstractResourceRequestActionHandler {
|
|
|
12251
12282
|
}
|
|
12252
12283
|
async buildPendingAction(request, queue) {
|
|
12253
12284
|
const resolvedRequest = this.resolveResourceRequest(request);
|
|
12254
|
-
|
|
12285
|
+
let pendingAction = (await super.buildPendingAction(resolvedRequest, queue));
|
|
12255
12286
|
const { tag, targetId } = pendingAction;
|
|
12256
12287
|
const targetApiName = await this.getApiNameForRecordId(targetId, tag, resolvedRequest);
|
|
12257
12288
|
pendingAction.metadata[LDS_ACTION_METADATA_API_NAME] = targetApiName;
|
|
@@ -12297,8 +12328,49 @@ class UiApiActionHandler extends AbstractResourceRequestActionHandler {
|
|
|
12297
12328
|
this.fetchReferenceRecord(referenceFields);
|
|
12298
12329
|
}
|
|
12299
12330
|
}
|
|
12331
|
+
// handles backdating
|
|
12332
|
+
if (this.isBackdatingEnabled &&
|
|
12333
|
+
pendingAction.data &&
|
|
12334
|
+
pendingAction.data.method !== 'delete' && //'delete' action does not have fields
|
|
12335
|
+
metaDataResult.data &&
|
|
12336
|
+
metaDataResult.data.objectInfos) {
|
|
12337
|
+
const objectInfo = metaDataResult.data.objectInfos.get(targetApiName);
|
|
12338
|
+
if (objectInfo === undefined) {
|
|
12339
|
+
throw Error(`Could not generate draft. Object info is missing`);
|
|
12340
|
+
}
|
|
12341
|
+
const appendedFields = this.getBackdatingFields(objectInfo, resolvedRequest.method, pendingAction);
|
|
12342
|
+
if (keys$3(appendedFields).length > 0) {
|
|
12343
|
+
pendingAction.data.body = {
|
|
12344
|
+
...pendingAction.data.body,
|
|
12345
|
+
fields: {
|
|
12346
|
+
...pendingAction.data.body.fields,
|
|
12347
|
+
...appendedFields,
|
|
12348
|
+
},
|
|
12349
|
+
};
|
|
12350
|
+
}
|
|
12351
|
+
}
|
|
12300
12352
|
return pendingAction;
|
|
12301
12353
|
}
|
|
12354
|
+
getBackdatingFields(objectInfo, requestMethod, pendingAction) {
|
|
12355
|
+
const fields = {};
|
|
12356
|
+
const actionFieldNames = keys$3(pendingAction.data.body.fields);
|
|
12357
|
+
if (requestMethod === 'post') {
|
|
12358
|
+
// `CreateRecord` with `CreatedDate` field
|
|
12359
|
+
if (isBackdatingFieldEditable(objectInfo, DEFAULT_FIELD_CREATED_DATE$1, 'createable', actionFieldNames)) {
|
|
12360
|
+
fields[DEFAULT_FIELD_CREATED_DATE$1] = new Date(pendingAction.timestamp).toISOString();
|
|
12361
|
+
}
|
|
12362
|
+
// `CreateRecord` with `LastModifiedDate` field
|
|
12363
|
+
if (isBackdatingFieldEditable(objectInfo, DEFAULT_FIELD_LAST_MODIFIED_DATE$1, 'createable', actionFieldNames)) {
|
|
12364
|
+
fields[DEFAULT_FIELD_LAST_MODIFIED_DATE$1] = new Date(pendingAction.timestamp).toISOString();
|
|
12365
|
+
}
|
|
12366
|
+
}
|
|
12367
|
+
// `UpdateRecord` with `LastModifedDate` field
|
|
12368
|
+
if (requestMethod === 'patch' &&
|
|
12369
|
+
isBackdatingFieldEditable(objectInfo, DEFAULT_FIELD_LAST_MODIFIED_DATE$1, 'updateable', actionFieldNames)) {
|
|
12370
|
+
fields[DEFAULT_FIELD_LAST_MODIFIED_DATE$1] = new Date(pendingAction.timestamp).toISOString();
|
|
12371
|
+
}
|
|
12372
|
+
return fields;
|
|
12373
|
+
}
|
|
12302
12374
|
async fetchReferenceRecord(referenceFields) {
|
|
12303
12375
|
const promises = referenceFields.map(async (referenceFieldInfo) => {
|
|
12304
12376
|
const apiName = await this.identifyApiName(referenceFieldInfo.id, referenceFieldInfo.field);
|
|
@@ -12589,12 +12661,26 @@ class UiApiActionHandler extends AbstractResourceRequestActionHandler {
|
|
|
12589
12661
|
mergeRequestBody(targetBody, sourceBody) {
|
|
12590
12662
|
// IMPORTANT: spread operator isn't deep clone so we have to individually
|
|
12591
12663
|
// spread nested objects (just "fields" for RecordInputReps)
|
|
12664
|
+
const targetInput = targetBody;
|
|
12665
|
+
const sourceInput = sourceBody;
|
|
12666
|
+
// choose the max if both source and target have the `LastModifiedDate` property.
|
|
12667
|
+
// there is no need to check 'CreatedDate` since source and target cannot be `create` action at the same time
|
|
12668
|
+
const lastModifiedFields = {};
|
|
12669
|
+
const targetLastModifiedField = targetInput.fields[DEFAULT_FIELD_LAST_MODIFIED_DATE$1];
|
|
12670
|
+
const sourceLastModifiedField = sourceInput.fields[DEFAULT_FIELD_LAST_MODIFIED_DATE$1];
|
|
12671
|
+
if (targetLastModifiedField && sourceLastModifiedField) {
|
|
12672
|
+
lastModifiedFields[DEFAULT_FIELD_LAST_MODIFIED_DATE$1] =
|
|
12673
|
+
targetLastModifiedField > sourceLastModifiedField
|
|
12674
|
+
? targetLastModifiedField
|
|
12675
|
+
: sourceLastModifiedField;
|
|
12676
|
+
}
|
|
12592
12677
|
return {
|
|
12593
12678
|
...targetBody,
|
|
12594
12679
|
...sourceBody,
|
|
12595
12680
|
fields: {
|
|
12596
12681
|
...targetBody.fields,
|
|
12597
12682
|
...sourceBody.fields,
|
|
12683
|
+
...lastModifiedFields,
|
|
12598
12684
|
},
|
|
12599
12685
|
};
|
|
12600
12686
|
}
|
|
@@ -12602,6 +12688,13 @@ class UiApiActionHandler extends AbstractResourceRequestActionHandler {
|
|
|
12602
12688
|
function isField(key, data) {
|
|
12603
12689
|
return isStoreKeyRecordField(key);
|
|
12604
12690
|
}
|
|
12691
|
+
// true if `createable/updateable` property of backdating field is true and draft does not include that fields. If the customer specifies
|
|
12692
|
+
// `LastModifiedDate`, it is not overwritten.
|
|
12693
|
+
function isBackdatingFieldEditable(objectInfo, backdatingFieldName, attributeName, draftActionFieldNames) {
|
|
12694
|
+
return (objectInfo.fields[backdatingFieldName] &&
|
|
12695
|
+
objectInfo.fields[backdatingFieldName][attributeName] &&
|
|
12696
|
+
!draftActionFieldNames.includes(backdatingFieldName));
|
|
12697
|
+
}
|
|
12605
12698
|
|
|
12606
12699
|
/**
|
|
12607
12700
|
* Records are stored in the durable store with scalar fields denormalized. This function takes that denoramlized
|
|
@@ -13636,7 +13729,7 @@ class ContentDocumentCompositeRepresentationActionHandler extends AbstractResour
|
|
|
13636
13729
|
});
|
|
13637
13730
|
pendingAction.metadata[CONTENT_DOCUMENT_LINK_DRAFT_ID_KEY] = contentDocumentLinkId;
|
|
13638
13731
|
// assert that object infos and references exist
|
|
13639
|
-
const metaDataResult = await this.draftRecordService.getRecordDraftMetadata(
|
|
13732
|
+
const metaDataResult = await this.draftRecordService.getRecordDraftMetadata(contentVersionId, pendingAction);
|
|
13640
13733
|
if (metaDataResult === undefined) {
|
|
13641
13734
|
throw Error('No metadata for draft');
|
|
13642
13735
|
}
|
|
@@ -13650,8 +13743,30 @@ class ContentDocumentCompositeRepresentationActionHandler extends AbstractResour
|
|
|
13650
13743
|
throw new DraftSynthesisError(`unknown draft precondition failed, metaDataResult: ${JSON.stringify(metaDataResult)}`, 'UNKNOWN');
|
|
13651
13744
|
}
|
|
13652
13745
|
}
|
|
13746
|
+
else if (this.isBackdatingEnabled) {
|
|
13747
|
+
const contentVersionObjectInfo = metaDataResult.data.objectInfos.get(CONTENT_VERSION_API_NAME);
|
|
13748
|
+
if (contentVersionObjectInfo === undefined) {
|
|
13749
|
+
throw Error(`Could not generate draft. Object info is missing`);
|
|
13750
|
+
}
|
|
13751
|
+
// `ContentVersion` support back-dating. Ui-api team has WI @W-14219481 to support `CreatedDate` and `LastModifiedDate`.
|
|
13752
|
+
// right now, these two fields are ignored by server
|
|
13753
|
+
pendingAction.data.body.namedEntries = [
|
|
13754
|
+
...pendingAction.data.body.namedEntries,
|
|
13755
|
+
...this.getBackdatingNameEntries(contentVersionObjectInfo, pendingAction.timestamp),
|
|
13756
|
+
];
|
|
13757
|
+
}
|
|
13653
13758
|
return pendingAction;
|
|
13654
13759
|
}
|
|
13760
|
+
getBackdatingNameEntries(objectInfo, timestamp) {
|
|
13761
|
+
return [DEFAULT_FIELD_CREATED_DATE$1, DEFAULT_FIELD_LAST_MODIFIED_DATE$1]
|
|
13762
|
+
.filter((fieldName) => objectInfo.fields[fieldName] && objectInfo.fields[fieldName].createable)
|
|
13763
|
+
.map((fieldValue) => {
|
|
13764
|
+
return {
|
|
13765
|
+
name: fieldValue,
|
|
13766
|
+
value: new Date(timestamp).toISOString(),
|
|
13767
|
+
};
|
|
13768
|
+
});
|
|
13769
|
+
}
|
|
13655
13770
|
/* istanbul ignore next */
|
|
13656
13771
|
canHandlePublish(_key) {
|
|
13657
13772
|
// no need to touch publishing
|
|
@@ -17558,5 +17673,5 @@ register({
|
|
|
17558
17673
|
instrument: instrument,
|
|
17559
17674
|
});
|
|
17560
17675
|
|
|
17561
|
-
export { getRuntime, registerReportObserver, reportGraphqlQueryParseError };
|
|
17562
|
-
// version: 1.
|
|
17676
|
+
export { O11Y_NAMESPACE_LDS_MOBILE, getRuntime, registerReportObserver, reportGraphqlQueryParseError };
|
|
17677
|
+
// version: 1.256.0-ad6a66c18
|
package/sfdc/types/main.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getRuntime } from './runtime';
|
|
2
|
-
|
|
2
|
+
import { O11Y_NAMESPACE_LDS_MOBILE } from './instrumentation/metrics';
|
|
3
|
+
export { getRuntime, O11Y_NAMESPACE_LDS_MOBILE };
|
|
3
4
|
export type { ObjectInfoService } from './utils/ObjectInfoService';
|
|
4
5
|
export type { ObservabilityContext } from '@salesforce/nimbus-plugin-lds';
|
|
5
6
|
export { registerReportObserver } from './instrumentation/instrumentMobileAdapter';
|