@salesforce/lds-ads-bridge 1.431.0 → 1.433.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/ads-bridge-perf.js +38 -11
- package/dist/adsBridge.js +36 -9
- package/dist/types/ads-bridge.d.ts +1 -0
- package/package.json +7 -7
- package/src/__tests__/ads-bridge.spec.ts +235 -1
- package/src/__tests__/initial_record.json +50 -0
- package/src/__tests__/task_record_gvp_get.json +233 -0
- package/src/__tests__/task_record_gvp_get2.json +233 -0
- package/src/__tests__/task_record_gvp_save.json +233 -0
- package/src/ads-bridge.ts +40 -10
package/dist/ads-bridge-perf.js
CHANGED
|
@@ -483,7 +483,7 @@ const callbacks$1 = [];
|
|
|
483
483
|
function register(r) {
|
|
484
484
|
callbacks$1.forEach((callback) => callback(r));
|
|
485
485
|
}
|
|
486
|
-
// version: 1.
|
|
486
|
+
// version: 1.433.0-8a15a98f24
|
|
487
487
|
|
|
488
488
|
/**
|
|
489
489
|
* Returns true if the value acts like a Promise, i.e. has a "then" function,
|
|
@@ -7000,7 +7000,7 @@ function getResponseCacheKeys$17(storeKeyMap, luvio, resourceParams, response) {
|
|
|
7000
7000
|
function createResourceRequest$1g(config) {
|
|
7001
7001
|
const headers = {};
|
|
7002
7002
|
return {
|
|
7003
|
-
baseUri: '/services/data/
|
|
7003
|
+
baseUri: '/services/data/v68.0',
|
|
7004
7004
|
basePath: '/ui-api/records/' + config.urlParams.recordId + '',
|
|
7005
7005
|
method: 'get',
|
|
7006
7006
|
body: null,
|
|
@@ -7377,7 +7377,7 @@ function ingestError$Q(luvio, params, error, snapshotRefresh) {
|
|
|
7377
7377
|
function createResourceRequest$1f(config) {
|
|
7378
7378
|
const headers = {};
|
|
7379
7379
|
return {
|
|
7380
|
-
baseUri: '/services/data/
|
|
7380
|
+
baseUri: '/services/data/v68.0',
|
|
7381
7381
|
basePath: '/ui-api/records/batch/' + config.urlParams.recordIds + '',
|
|
7382
7382
|
method: 'get',
|
|
7383
7383
|
body: null,
|
|
@@ -7900,6 +7900,9 @@ function isSpanningRecord(fieldValue) {
|
|
|
7900
7900
|
function isStoreKeyRecordId(key) {
|
|
7901
7901
|
return key.indexOf(RECORD_ID_PREFIX) > -1 && key.indexOf(RECORD_FIELDS_KEY_JUNCTION) === -1;
|
|
7902
7902
|
}
|
|
7903
|
+
function isStoreKeyRecordField(key) {
|
|
7904
|
+
return key.indexOf(RECORD_ID_PREFIX) > -1 && key.indexOf(RECORD_FIELDS_KEY_JUNCTION) > -1;
|
|
7905
|
+
}
|
|
7903
7906
|
/**
|
|
7904
7907
|
* Returns a shallow copy of a record with its field values if it is a scalar and a reference and a
|
|
7905
7908
|
* a RecordRepresentation with no field if the value if a spanning record.
|
|
@@ -8194,6 +8197,33 @@ class AdsBridge {
|
|
|
8194
8197
|
this.isRecordEmitLocked = false;
|
|
8195
8198
|
}
|
|
8196
8199
|
}
|
|
8200
|
+
filterAndSynthesizeBaseIds(updatedEntries) {
|
|
8201
|
+
// Collect base record IDs that are directly present in the entries.
|
|
8202
|
+
const directBaseIds = new Set();
|
|
8203
|
+
for (let i = 0; i < updatedEntries.length; i++) {
|
|
8204
|
+
if (isStoreKeyRecordId(updatedEntries[i].id)) {
|
|
8205
|
+
directBaseIds.add(updatedEntries[i].id);
|
|
8206
|
+
}
|
|
8207
|
+
}
|
|
8208
|
+
// For field entries whose base record ID has no direct entry, synthesize one.
|
|
8209
|
+
const syntheticBaseIds = new Set();
|
|
8210
|
+
for (let i = 0; i < updatedEntries.length; i++) {
|
|
8211
|
+
if (isStoreKeyRecordField(updatedEntries[i].id)) {
|
|
8212
|
+
const baseId = updatedEntries[i].id.split(RECORD_FIELDS_KEY_JUNCTION)[0];
|
|
8213
|
+
if (!directBaseIds.has(baseId)) {
|
|
8214
|
+
syntheticBaseIds.add(baseId);
|
|
8215
|
+
}
|
|
8216
|
+
}
|
|
8217
|
+
}
|
|
8218
|
+
// Exclude all the store record ids not matching with the record id pattern.
|
|
8219
|
+
// Note: FieldValueRepresentation have the same prefix than RecordRepresentation so we
|
|
8220
|
+
// need to filter them out.
|
|
8221
|
+
const filteredUpdatedEntries = updatedEntries.filter((entry) => isStoreKeyRecordId(entry.id));
|
|
8222
|
+
syntheticBaseIds.forEach((baseId) => {
|
|
8223
|
+
push.call(filteredUpdatedEntries, { id: baseId });
|
|
8224
|
+
});
|
|
8225
|
+
return filteredUpdatedEntries;
|
|
8226
|
+
}
|
|
8197
8227
|
/**
|
|
8198
8228
|
* This method retrieves queries the store with with passed record ids to retrieve their
|
|
8199
8229
|
* associated records and object info. Note that the passed ids are not Salesforce record id
|
|
@@ -8204,14 +8234,11 @@ class AdsBridge {
|
|
|
8204
8234
|
let shouldEmit = false;
|
|
8205
8235
|
const adsRecordMap = {};
|
|
8206
8236
|
const adsObjectMap = {};
|
|
8207
|
-
|
|
8208
|
-
|
|
8209
|
-
|
|
8210
|
-
|
|
8211
|
-
|
|
8212
|
-
if (!isStoreKeyRecordId(storeRecordId)) {
|
|
8213
|
-
continue;
|
|
8214
|
-
}
|
|
8237
|
+
// W-21715343
|
|
8238
|
+
// Context for change: https://docs.google.com/document/d/1iF6M9jldEX_K9FOpdttLqVwO9uESUUrlyWi2mWnXtAQ/edit?usp=sharing
|
|
8239
|
+
const filteredUpdatedEntries = this.filterAndSynthesizeBaseIds(updatedEntries);
|
|
8240
|
+
for (let i = 0; i < filteredUpdatedEntries.length; i++) {
|
|
8241
|
+
const storeRecordId = filteredUpdatedEntries[i].id;
|
|
8215
8242
|
const record = this.recordRepresentationIngestOverride !== undefined
|
|
8216
8243
|
? getShallowRecordDenormalized(luvio, storeRecordId)
|
|
8217
8244
|
: getShallowRecord(luvio, storeRecordId);
|
package/dist/adsBridge.js
CHANGED
|
@@ -58,6 +58,9 @@ function isSpanningRecord(fieldValue) {
|
|
|
58
58
|
function isStoreKeyRecordId(key) {
|
|
59
59
|
return key.indexOf(RECORD_ID_PREFIX) > -1 && key.indexOf(RECORD_FIELDS_KEY_JUNCTION) === -1;
|
|
60
60
|
}
|
|
61
|
+
function isStoreKeyRecordField(key) {
|
|
62
|
+
return key.indexOf(RECORD_ID_PREFIX) > -1 && key.indexOf(RECORD_FIELDS_KEY_JUNCTION) > -1;
|
|
63
|
+
}
|
|
61
64
|
/**
|
|
62
65
|
* Returns a shallow copy of a record with its field values if it is a scalar and a reference and a
|
|
63
66
|
* a RecordRepresentation with no field if the value if a spanning record.
|
|
@@ -356,6 +359,33 @@ class AdsBridge {
|
|
|
356
359
|
this.isRecordEmitLocked = false;
|
|
357
360
|
}
|
|
358
361
|
}
|
|
362
|
+
filterAndSynthesizeBaseIds(updatedEntries) {
|
|
363
|
+
// Collect base record IDs that are directly present in the entries.
|
|
364
|
+
const directBaseIds = new Set();
|
|
365
|
+
for (let i = 0; i < updatedEntries.length; i++) {
|
|
366
|
+
if (isStoreKeyRecordId(updatedEntries[i].id)) {
|
|
367
|
+
directBaseIds.add(updatedEntries[i].id);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
// For field entries whose base record ID has no direct entry, synthesize one.
|
|
371
|
+
const syntheticBaseIds = new Set();
|
|
372
|
+
for (let i = 0; i < updatedEntries.length; i++) {
|
|
373
|
+
if (isStoreKeyRecordField(updatedEntries[i].id)) {
|
|
374
|
+
const baseId = updatedEntries[i].id.split(RECORD_FIELDS_KEY_JUNCTION)[0];
|
|
375
|
+
if (!directBaseIds.has(baseId)) {
|
|
376
|
+
syntheticBaseIds.add(baseId);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
// Exclude all the store record ids not matching with the record id pattern.
|
|
381
|
+
// Note: FieldValueRepresentation have the same prefix than RecordRepresentation so we
|
|
382
|
+
// need to filter them out.
|
|
383
|
+
const filteredUpdatedEntries = updatedEntries.filter((entry) => isStoreKeyRecordId(entry.id));
|
|
384
|
+
syntheticBaseIds.forEach((baseId) => {
|
|
385
|
+
push.call(filteredUpdatedEntries, { id: baseId });
|
|
386
|
+
});
|
|
387
|
+
return filteredUpdatedEntries;
|
|
388
|
+
}
|
|
359
389
|
/**
|
|
360
390
|
* This method retrieves queries the store with with passed record ids to retrieve their
|
|
361
391
|
* associated records and object info. Note that the passed ids are not Salesforce record id
|
|
@@ -367,14 +397,11 @@ class AdsBridge {
|
|
|
367
397
|
let shouldEmit = false;
|
|
368
398
|
const adsRecordMap = {};
|
|
369
399
|
const adsObjectMap = {};
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
if (!isStoreKeyRecordId(storeRecordId)) {
|
|
376
|
-
continue;
|
|
377
|
-
}
|
|
400
|
+
// W-21715343
|
|
401
|
+
// Context for change: https://docs.google.com/document/d/1iF6M9jldEX_K9FOpdttLqVwO9uESUUrlyWi2mWnXtAQ/edit?usp=sharing
|
|
402
|
+
const filteredUpdatedEntries = this.filterAndSynthesizeBaseIds(updatedEntries);
|
|
403
|
+
for (let i = 0; i < filteredUpdatedEntries.length; i++) {
|
|
404
|
+
const storeRecordId = filteredUpdatedEntries[i].id;
|
|
378
405
|
const record = this.recordRepresentationIngestOverride !== undefined
|
|
379
406
|
? getShallowRecordDenormalized(luvio, storeRecordId)
|
|
380
407
|
: getShallowRecord(luvio, storeRecordId);
|
|
@@ -435,4 +462,4 @@ function withAdsBridge(callback) {
|
|
|
435
462
|
}
|
|
436
463
|
|
|
437
464
|
export { instrument, withAdsBridge };
|
|
438
|
-
// version: 1.
|
|
465
|
+
// version: 1.433.0-8a15a98f24
|
|
@@ -78,6 +78,7 @@ export default class AdsBridge {
|
|
|
78
78
|
* mutations triggered by ADS to be emit back to ADS.
|
|
79
79
|
*/
|
|
80
80
|
private lockLdsRecordEmit;
|
|
81
|
+
private filterAndSynthesizeBaseIds;
|
|
81
82
|
/**
|
|
82
83
|
* This method retrieves queries the store with with passed record ids to retrieve their
|
|
83
84
|
* associated records and object info. Note that the passed ids are not Salesforce record id
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/lds-ads-bridge",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.433.0",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
5
5
|
"description": "Bridge to sync data between LDS and ADS",
|
|
6
6
|
"main": "dist/adsBridge.js",
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
"release:corejar": "yarn build && ../core-build/scripts/core.js --name=lds-ads-bridge"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@salesforce/lds-adapters-uiapi": "^1.
|
|
34
|
-
"@salesforce/lds-runtime-mobile": "^1.
|
|
35
|
-
"@salesforce/lds-uiapi-record-utils-mobile": "^1.
|
|
33
|
+
"@salesforce/lds-adapters-uiapi": "^1.433.0",
|
|
34
|
+
"@salesforce/lds-runtime-mobile": "^1.433.0",
|
|
35
|
+
"@salesforce/lds-uiapi-record-utils-mobile": "^1.433.0"
|
|
36
36
|
},
|
|
37
37
|
"volta": {
|
|
38
38
|
"extends": "../../package.json"
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
{
|
|
42
42
|
"path": "./dist/adsBridge.js",
|
|
43
43
|
"maxSize": {
|
|
44
|
-
"none": "
|
|
45
|
-
"min": "
|
|
46
|
-
"compressed": "4 kB"
|
|
44
|
+
"none": "18.4 kB",
|
|
45
|
+
"min": "6 kB",
|
|
46
|
+
"compressed": "4.3 kB"
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
]
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { Luvio, InMemoryStore, Environment } from '@luvio/engine';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
keyBuilderRecord,
|
|
4
|
+
ingestRecord,
|
|
5
|
+
type Registration,
|
|
6
|
+
ingestRecordSuccess,
|
|
7
|
+
} from '@salesforce/lds-adapters-uiapi';
|
|
3
8
|
import { expect } from '@jest/globals';
|
|
4
9
|
|
|
5
10
|
import AdsBridge from '../ads-bridge';
|
|
@@ -417,6 +422,159 @@ describe('AdsBridge', () => {
|
|
|
417
422
|
});
|
|
418
423
|
});
|
|
419
424
|
|
|
425
|
+
// W-21715343
|
|
426
|
+
// Context for change: https://docs.google.com/document/d/1iF6M9jldEX_K9FOpdttLqVwO9uESUUrlyWi2mWnXtAQ/edit?usp=sharing
|
|
427
|
+
it('correctly emits the updated Case record to ADS when refreshSnapshot is called, and the Case record was updated via spanning record updates on the Task record', async () => {
|
|
428
|
+
const initialLdsState = JSON.parse(JSON.stringify(require('./initial_record.json')));
|
|
429
|
+
const addRecordsGet = JSON.parse(JSON.stringify(require('./task_record_gvp_get.json')));
|
|
430
|
+
const addRecordsSave = JSON.parse(
|
|
431
|
+
JSON.stringify(require('./task_record_gvp_save.json'))
|
|
432
|
+
);
|
|
433
|
+
const addRecordsGet2 = JSON.parse(
|
|
434
|
+
JSON.stringify(require('./task_record_gvp_get2.json'))
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
const { bridge, luvio } = createBridge();
|
|
438
|
+
const caseId: string = initialLdsState.id;
|
|
439
|
+
|
|
440
|
+
const selector = {
|
|
441
|
+
recordId: keyBuilderRecord(luvio, { recordId: caseId }),
|
|
442
|
+
node: { kind: 'Fragment' as const, private: [] as string[] },
|
|
443
|
+
variables: {},
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
// Creating a refreshable snapshot of the Case record as we don't have the wire adapter bindings available to us in jest environment to refresh the snapshot manually.
|
|
447
|
+
const snapshot = luvio.storeLookup(selector, {
|
|
448
|
+
config: { recordId: caseId },
|
|
449
|
+
resolve: async () => {
|
|
450
|
+
const response = await luvio.dispatchResourceRequest({
|
|
451
|
+
baseUri: '',
|
|
452
|
+
basePath: `/ui-api/records/${caseId}`,
|
|
453
|
+
method: 'get',
|
|
454
|
+
body: null,
|
|
455
|
+
queryParams: {},
|
|
456
|
+
urlParams: {},
|
|
457
|
+
headers: {},
|
|
458
|
+
priority: 'normal',
|
|
459
|
+
});
|
|
460
|
+
const config = {
|
|
461
|
+
optionalFields: ['Case.Id'],
|
|
462
|
+
recordId: caseId,
|
|
463
|
+
};
|
|
464
|
+
const trackedFields = ['Case.Status'];
|
|
465
|
+
ingestRecordSuccess(luvio, config, caseId, trackedFields, response as any, 0);
|
|
466
|
+
await luvio.storeBroadcast();
|
|
467
|
+
return luvio.storeLookup(selector);
|
|
468
|
+
},
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
// 1. Prime LDS with the Case fixture (via addRecord → storeIngest + storeBroadcast).
|
|
472
|
+
addRecord(luvio, initialLdsState);
|
|
473
|
+
|
|
474
|
+
const expectedStatus = queryRecord(luvio, { recordId: caseId }).data.fields.Status;
|
|
475
|
+
expect(expectedStatus).toEqual({ value: 'New', displayValue: 'New' });
|
|
476
|
+
|
|
477
|
+
luvio.dispatchResourceRequest = jest.fn();
|
|
478
|
+
|
|
479
|
+
const fn = jest.fn();
|
|
480
|
+
bridge.receiveFromLdsCallback = fn;
|
|
481
|
+
|
|
482
|
+
// 2. Ingest the Task (+ spanning Case__r) via addRecords().
|
|
483
|
+
bridge.addRecords(addRecordsGet); // A RecordGvp.getRecord call is made to get the initial Task record when switching from the Case record to the Task record in console mode.
|
|
484
|
+
bridge.addRecords(addRecordsSave); // Clicking the complete button on the Task record which does a RecordGvp.saveRecord call.
|
|
485
|
+
bridge.addRecords(addRecordsGet2); // A RecordGvp.getRecord call is made to get the updated Task record.
|
|
486
|
+
|
|
487
|
+
expect(luvio.dispatchResourceRequest).toHaveBeenCalledTimes(0);
|
|
488
|
+
|
|
489
|
+
// 3. Synthesize the closed Case record server response which is expected to be received when the refreshSnapshot is called.
|
|
490
|
+
const closedCaseResponse = JSON.parse(JSON.stringify(require('./initial_record.json')));
|
|
491
|
+
closedCaseResponse.fields.Status = { value: 'Closed', displayValue: 'Closed' };
|
|
492
|
+
closedCaseResponse.weakEtag = addRecordsGet2[0].weakEtag;
|
|
493
|
+
closedCaseResponse.systemModstamp = addRecordsGet2[0].systemModstamp;
|
|
494
|
+
closedCaseResponse.lastModifiedDate = addRecordsGet2[0].lastModifiedDate;
|
|
495
|
+
|
|
496
|
+
luvio.dispatchResourceRequest = jest
|
|
497
|
+
.fn()
|
|
498
|
+
.mockResolvedValueOnce({ body: closedCaseResponse })
|
|
499
|
+
.mockImplementation(() => new Promise(() => {}));
|
|
500
|
+
|
|
501
|
+
// 4. Refresh the snapshot to get the updated Case record. This was previously not working because the interactions on the ADS-Bridge had updated the Case record with the latest weakEtag, but NOT the updated Status field value.
|
|
502
|
+
const refreshedSnapshot = await luvio.refreshSnapshot(snapshot);
|
|
503
|
+
expect((refreshedSnapshot.data as any).fields.Status).toEqual(
|
|
504
|
+
expect.objectContaining({ value: 'Closed' })
|
|
505
|
+
);
|
|
506
|
+
|
|
507
|
+
// 5. With the ADS-Bridge changes, the Case record is now emitted when the refreshSnapshot is called.
|
|
508
|
+
expect(fn.mock.calls[0]).toMatchInlineSnapshot(`
|
|
509
|
+
Array [
|
|
510
|
+
Object {
|
|
511
|
+
"500xx000000boDJAAY": Object {
|
|
512
|
+
"Case": Object {
|
|
513
|
+
"isPrimary": true,
|
|
514
|
+
"record": Object {
|
|
515
|
+
"apiName": "Case",
|
|
516
|
+
"childRelationships": Object {},
|
|
517
|
+
"eTag": "",
|
|
518
|
+
"fields": Object {
|
|
519
|
+
"CaseNumber": Object {
|
|
520
|
+
"displayValue": null,
|
|
521
|
+
"value": "00001026",
|
|
522
|
+
},
|
|
523
|
+
"Id": Object {
|
|
524
|
+
"displayValue": null,
|
|
525
|
+
"value": "500xx000000boDJAAY",
|
|
526
|
+
},
|
|
527
|
+
"IsEscalated": Object {
|
|
528
|
+
"displayValue": null,
|
|
529
|
+
"value": false,
|
|
530
|
+
},
|
|
531
|
+
"MasterRecordId": Object {
|
|
532
|
+
"displayValue": null,
|
|
533
|
+
"value": null,
|
|
534
|
+
},
|
|
535
|
+
"Priority": Object {
|
|
536
|
+
"displayValue": "Medium",
|
|
537
|
+
"value": "Medium",
|
|
538
|
+
},
|
|
539
|
+
"RecordTypeId": Object {
|
|
540
|
+
"displayValue": null,
|
|
541
|
+
"value": "012000000000000AAA",
|
|
542
|
+
},
|
|
543
|
+
"Status": Object {
|
|
544
|
+
"displayValue": "Closed",
|
|
545
|
+
"value": "Closed",
|
|
546
|
+
},
|
|
547
|
+
"Subject": Object {
|
|
548
|
+
"displayValue": null,
|
|
549
|
+
"value": "Fixture subject",
|
|
550
|
+
},
|
|
551
|
+
"SystemModstamp": Object {
|
|
552
|
+
"displayValue": null,
|
|
553
|
+
"value": "2026-04-11T00:46:56.000Z",
|
|
554
|
+
},
|
|
555
|
+
},
|
|
556
|
+
"id": "500xx000000boDJAAY",
|
|
557
|
+
"lastModifiedById": "005xx000001X8gHAAS",
|
|
558
|
+
"lastModifiedDate": "2026-04-13T20:54:41.000Z",
|
|
559
|
+
"recordTypeId": "012000000000000AAA",
|
|
560
|
+
"recordTypeInfo": null,
|
|
561
|
+
"systemModstamp": "2026-04-13T20:54:41.000Z",
|
|
562
|
+
"weakEtag": 1776113681000,
|
|
563
|
+
},
|
|
564
|
+
},
|
|
565
|
+
},
|
|
566
|
+
},
|
|
567
|
+
Object {
|
|
568
|
+
"Case": Object {
|
|
569
|
+
"_entityLabel": "Case",
|
|
570
|
+
"_keyPrefix": "500",
|
|
571
|
+
"_nameField": "Name",
|
|
572
|
+
},
|
|
573
|
+
},
|
|
574
|
+
]
|
|
575
|
+
`);
|
|
576
|
+
});
|
|
577
|
+
|
|
420
578
|
describe('displayValue', () => {
|
|
421
579
|
it('does not let null overwrite non-null displayValue', () => {
|
|
422
580
|
const { bridge, luvio } = createBridge();
|
|
@@ -1383,6 +1541,82 @@ describe('AdsBridge', () => {
|
|
|
1383
1541
|
});
|
|
1384
1542
|
});
|
|
1385
1543
|
|
|
1544
|
+
describe('filterAndSynthesizeBaseIds', () => {
|
|
1545
|
+
const BASE_KEY = 'UiApi::RecordRepresentation:001xx000000001AAA';
|
|
1546
|
+
const FIELD_KEY = 'UiApi::RecordRepresentation:001xx000000001AAA__fields__Name';
|
|
1547
|
+
const BASE_KEY_2 = 'UiApi::RecordRepresentation:001xx000000002AAA';
|
|
1548
|
+
const FIELD_KEY_2A = 'UiApi::RecordRepresentation:001xx000000002AAA__fields__Name';
|
|
1549
|
+
const FIELD_KEY_2B = 'UiApi::RecordRepresentation:001xx000000002AAA__fields__Id';
|
|
1550
|
+
const NON_RECORD_KEY = 'SomeOther::Thing:abc123';
|
|
1551
|
+
|
|
1552
|
+
function callFilter(bridge: AdsBridge, entries: { id: string }[]): { id: string }[] {
|
|
1553
|
+
return (bridge as any).filterAndSynthesizeBaseIds(entries);
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
it('returns an empty array when given an empty array', () => {
|
|
1557
|
+
const { bridge } = createBridge();
|
|
1558
|
+
expect(callFilter(bridge, [])).toEqual([]);
|
|
1559
|
+
});
|
|
1560
|
+
|
|
1561
|
+
it('filters out non-record entries', () => {
|
|
1562
|
+
const { bridge } = createBridge();
|
|
1563
|
+
const result = callFilter(bridge, [{ id: NON_RECORD_KEY }]);
|
|
1564
|
+
expect(result).toEqual([]);
|
|
1565
|
+
});
|
|
1566
|
+
|
|
1567
|
+
it('returns base record entries unchanged', () => {
|
|
1568
|
+
const { bridge } = createBridge();
|
|
1569
|
+
const result = callFilter(bridge, [{ id: BASE_KEY }]);
|
|
1570
|
+
expect(result).toEqual([{ id: BASE_KEY }]);
|
|
1571
|
+
});
|
|
1572
|
+
|
|
1573
|
+
it('synthesizes a base record entry for a field entry with no direct base entry', () => {
|
|
1574
|
+
const { bridge } = createBridge();
|
|
1575
|
+
const result = callFilter(bridge, [{ id: FIELD_KEY }]);
|
|
1576
|
+
expect(result).toEqual([{ id: BASE_KEY }]);
|
|
1577
|
+
});
|
|
1578
|
+
|
|
1579
|
+
it('does not synthesize a duplicate base entry when a direct base entry is already present', () => {
|
|
1580
|
+
const { bridge } = createBridge();
|
|
1581
|
+
const result = callFilter(bridge, [{ id: BASE_KEY }, { id: FIELD_KEY }]);
|
|
1582
|
+
expect(result).toHaveLength(1);
|
|
1583
|
+
expect(result).toEqual([{ id: BASE_KEY }]);
|
|
1584
|
+
});
|
|
1585
|
+
|
|
1586
|
+
it('synthesizes exactly one base entry for multiple field entries sharing the same base', () => {
|
|
1587
|
+
const { bridge } = createBridge();
|
|
1588
|
+
const result = callFilter(bridge, [{ id: FIELD_KEY_2A }, { id: FIELD_KEY_2B }]);
|
|
1589
|
+
expect(result).toHaveLength(1);
|
|
1590
|
+
expect(result).toEqual([{ id: BASE_KEY_2 }]);
|
|
1591
|
+
});
|
|
1592
|
+
|
|
1593
|
+
it('returns base entries and synthesizes entries for fields whose base is not directly present', () => {
|
|
1594
|
+
const { bridge } = createBridge();
|
|
1595
|
+
const entries = [
|
|
1596
|
+
{ id: BASE_KEY },
|
|
1597
|
+
{ id: FIELD_KEY },
|
|
1598
|
+
{ id: FIELD_KEY_2A },
|
|
1599
|
+
{ id: FIELD_KEY_2B },
|
|
1600
|
+
];
|
|
1601
|
+
const result = callFilter(bridge, entries);
|
|
1602
|
+
expect(result).toHaveLength(2);
|
|
1603
|
+
expect(result).toContainEqual({ id: BASE_KEY });
|
|
1604
|
+
expect(result).toContainEqual({ id: BASE_KEY_2 });
|
|
1605
|
+
});
|
|
1606
|
+
|
|
1607
|
+
it('filters out non-record entries while still processing valid entries', () => {
|
|
1608
|
+
const { bridge } = createBridge();
|
|
1609
|
+
const result = callFilter(bridge, [
|
|
1610
|
+
{ id: NON_RECORD_KEY },
|
|
1611
|
+
{ id: BASE_KEY },
|
|
1612
|
+
{ id: FIELD_KEY_2A },
|
|
1613
|
+
]);
|
|
1614
|
+
expect(result).toHaveLength(2);
|
|
1615
|
+
expect(result).toContainEqual({ id: BASE_KEY });
|
|
1616
|
+
expect(result).toContainEqual({ id: BASE_KEY_2 });
|
|
1617
|
+
});
|
|
1618
|
+
});
|
|
1619
|
+
|
|
1386
1620
|
describe('isDMOEntity', () => {
|
|
1387
1621
|
it('should return true for DMO record', () => {
|
|
1388
1622
|
const record = createRecord({
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"apiName": "Case",
|
|
3
|
+
"childRelationships": {},
|
|
4
|
+
"eTag": "",
|
|
5
|
+
"fields": {
|
|
6
|
+
"Id": {
|
|
7
|
+
"displayValue": null,
|
|
8
|
+
"value": "500xx000000boDJAAY"
|
|
9
|
+
},
|
|
10
|
+
"RecordTypeId": {
|
|
11
|
+
"displayValue": null,
|
|
12
|
+
"value": "012000000000000AAA"
|
|
13
|
+
},
|
|
14
|
+
"IsEscalated": {
|
|
15
|
+
"displayValue": null,
|
|
16
|
+
"value": false
|
|
17
|
+
},
|
|
18
|
+
"MasterRecordId": {
|
|
19
|
+
"displayValue": null,
|
|
20
|
+
"value": null
|
|
21
|
+
},
|
|
22
|
+
"SystemModstamp": {
|
|
23
|
+
"displayValue": null,
|
|
24
|
+
"value": "2026-04-11T00:46:56.000Z"
|
|
25
|
+
},
|
|
26
|
+
"CaseNumber": {
|
|
27
|
+
"displayValue": null,
|
|
28
|
+
"value": "00001026"
|
|
29
|
+
},
|
|
30
|
+
"Priority": {
|
|
31
|
+
"displayValue": "Medium",
|
|
32
|
+
"value": "Medium"
|
|
33
|
+
},
|
|
34
|
+
"Status": {
|
|
35
|
+
"displayValue": "New",
|
|
36
|
+
"value": "New"
|
|
37
|
+
},
|
|
38
|
+
"Subject": {
|
|
39
|
+
"displayValue": null,
|
|
40
|
+
"value": "Fixture subject"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"id": "500xx000000boDJAAY",
|
|
44
|
+
"lastModifiedById": "005xx000001X8gHAAS",
|
|
45
|
+
"lastModifiedDate": "2026-04-13T20:50:18.000Z",
|
|
46
|
+
"recordTypeId": "012000000000000AAA",
|
|
47
|
+
"recordTypeInfo": null,
|
|
48
|
+
"systemModstamp": "2026-04-13T20:50:18.000Z",
|
|
49
|
+
"weakEtag": 1776113418000
|
|
50
|
+
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"apiName": "Task",
|
|
4
|
+
"childRelationships": {},
|
|
5
|
+
"eTag": "2d24f5c26414c4f90731ba59a83c8d71",
|
|
6
|
+
"fields": {
|
|
7
|
+
"ActivityDate": {
|
|
8
|
+
"displayValue": null,
|
|
9
|
+
"value": null
|
|
10
|
+
},
|
|
11
|
+
"Case__c": {
|
|
12
|
+
"displayValue": null,
|
|
13
|
+
"value": "500xx000000boDJAAY"
|
|
14
|
+
},
|
|
15
|
+
"Case__r": {
|
|
16
|
+
"displayValue": "00001027",
|
|
17
|
+
"value": {
|
|
18
|
+
"apiName": "Case",
|
|
19
|
+
"childRelationships": {},
|
|
20
|
+
"eTag": "f9130f3feb968a9bb7e0c222a78ef1dc",
|
|
21
|
+
"fields": {
|
|
22
|
+
"CaseNumber": {
|
|
23
|
+
"displayValue": null,
|
|
24
|
+
"value": "00001027"
|
|
25
|
+
},
|
|
26
|
+
"Id": {
|
|
27
|
+
"displayValue": null,
|
|
28
|
+
"value": "500xx000000boDJAAY"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"id": "500xx000000boDJAAY",
|
|
32
|
+
"lastModifiedById": "005xx000001X8gHAAS",
|
|
33
|
+
"lastModifiedDate": "2026-04-13T20:50:18.000Z",
|
|
34
|
+
"recordTypeId": "012000000000000AAA",
|
|
35
|
+
"recordTypeInfo": null,
|
|
36
|
+
"systemModstamp": "2026-04-13T20:50:18.000Z",
|
|
37
|
+
"weakEtag": 1776113418000
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"CreatedBy": {
|
|
41
|
+
"displayValue": "Ethan Chan",
|
|
42
|
+
"value": {
|
|
43
|
+
"apiName": "User",
|
|
44
|
+
"childRelationships": {},
|
|
45
|
+
"eTag": "2ca65e8b3117284d9253195c82c5f03d",
|
|
46
|
+
"fields": {
|
|
47
|
+
"FirstName": {
|
|
48
|
+
"displayValue": null,
|
|
49
|
+
"value": "Ethan"
|
|
50
|
+
},
|
|
51
|
+
"Id": {
|
|
52
|
+
"displayValue": null,
|
|
53
|
+
"value": "005xx000001X8gHAAS"
|
|
54
|
+
},
|
|
55
|
+
"LastName": {
|
|
56
|
+
"displayValue": null,
|
|
57
|
+
"value": "Chan"
|
|
58
|
+
},
|
|
59
|
+
"Name": {
|
|
60
|
+
"displayValue": null,
|
|
61
|
+
"value": "Ethan Chan"
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"id": "005xx000001X8gHAAS",
|
|
65
|
+
"lastModifiedById": "005xx000001X8gHAAS",
|
|
66
|
+
"lastModifiedDate": "2026-04-13T19:07:57.000Z",
|
|
67
|
+
"recordTypeId": null,
|
|
68
|
+
"recordTypeInfo": null,
|
|
69
|
+
"systemModstamp": "2026-04-13T19:07:57.000Z",
|
|
70
|
+
"weakEtag": 1776107277000
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"CreatedById": {
|
|
74
|
+
"displayValue": null,
|
|
75
|
+
"value": "005xx000001X8gHAAS"
|
|
76
|
+
},
|
|
77
|
+
"CreatedDate": {
|
|
78
|
+
"displayValue": "4/13/2026, 12:09 PM",
|
|
79
|
+
"value": "2026-04-13T19:09:48.000Z"
|
|
80
|
+
},
|
|
81
|
+
"Description": {
|
|
82
|
+
"displayValue": null,
|
|
83
|
+
"value": null
|
|
84
|
+
},
|
|
85
|
+
"Id": {
|
|
86
|
+
"displayValue": null,
|
|
87
|
+
"value": "00Txx000003rIcmEAE"
|
|
88
|
+
},
|
|
89
|
+
"IsClosed": {
|
|
90
|
+
"displayValue": null,
|
|
91
|
+
"value": false
|
|
92
|
+
},
|
|
93
|
+
"LastModifiedBy": {
|
|
94
|
+
"displayValue": "Ethan Chan",
|
|
95
|
+
"value": {
|
|
96
|
+
"apiName": "User",
|
|
97
|
+
"childRelationships": {},
|
|
98
|
+
"eTag": "2ca65e8b3117284d9253195c82c5f03d",
|
|
99
|
+
"fields": {
|
|
100
|
+
"FirstName": {
|
|
101
|
+
"displayValue": null,
|
|
102
|
+
"value": "Ethan"
|
|
103
|
+
},
|
|
104
|
+
"Id": {
|
|
105
|
+
"displayValue": null,
|
|
106
|
+
"value": "005xx000001X8gHAAS"
|
|
107
|
+
},
|
|
108
|
+
"LastName": {
|
|
109
|
+
"displayValue": null,
|
|
110
|
+
"value": "Chan"
|
|
111
|
+
},
|
|
112
|
+
"Name": {
|
|
113
|
+
"displayValue": null,
|
|
114
|
+
"value": "Ethan Chan"
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
"id": "005xx000001X8gHAAS",
|
|
118
|
+
"lastModifiedById": "005xx000001X8gHAAS",
|
|
119
|
+
"lastModifiedDate": "2026-04-13T19:07:57.000Z",
|
|
120
|
+
"recordTypeId": null,
|
|
121
|
+
"recordTypeInfo": null,
|
|
122
|
+
"systemModstamp": "2026-04-13T19:07:57.000Z",
|
|
123
|
+
"weakEtag": 1776107277000
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
"LastModifiedById": {
|
|
127
|
+
"displayValue": null,
|
|
128
|
+
"value": "005xx000001X8gHAAS"
|
|
129
|
+
},
|
|
130
|
+
"LastModifiedDate": {
|
|
131
|
+
"displayValue": "4/13/2026, 1:50 PM",
|
|
132
|
+
"value": "2026-04-13T20:50:05.000Z"
|
|
133
|
+
},
|
|
134
|
+
"Owner": {
|
|
135
|
+
"displayValue": "Ethan Chan",
|
|
136
|
+
"value": {
|
|
137
|
+
"apiName": "Name",
|
|
138
|
+
"childRelationships": {},
|
|
139
|
+
"eTag": "202a115a07830e8af0343368d4d309d4",
|
|
140
|
+
"fields": {
|
|
141
|
+
"FirstName": {
|
|
142
|
+
"displayValue": null,
|
|
143
|
+
"value": "Ethan"
|
|
144
|
+
},
|
|
145
|
+
"Id": {
|
|
146
|
+
"displayValue": null,
|
|
147
|
+
"value": "005xx000001X8gHAAS"
|
|
148
|
+
},
|
|
149
|
+
"LastName": {
|
|
150
|
+
"displayValue": null,
|
|
151
|
+
"value": "Chan"
|
|
152
|
+
},
|
|
153
|
+
"Name": {
|
|
154
|
+
"displayValue": null,
|
|
155
|
+
"value": "Ethan Chan"
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
"id": "005xx000001X8gHAAS",
|
|
159
|
+
"lastModifiedById": null,
|
|
160
|
+
"lastModifiedDate": null,
|
|
161
|
+
"recordTypeId": null,
|
|
162
|
+
"recordTypeInfo": null,
|
|
163
|
+
"systemModstamp": null,
|
|
164
|
+
"weakEtag": 0
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
"OwnerId": {
|
|
168
|
+
"displayValue": null,
|
|
169
|
+
"value": "005xx000001X8gHAAS"
|
|
170
|
+
},
|
|
171
|
+
"Priority": {
|
|
172
|
+
"displayValue": "High",
|
|
173
|
+
"value": "High"
|
|
174
|
+
},
|
|
175
|
+
"Status": {
|
|
176
|
+
"displayValue": "Not Started",
|
|
177
|
+
"value": "Not Started"
|
|
178
|
+
},
|
|
179
|
+
"Subject": {
|
|
180
|
+
"displayValue": null,
|
|
181
|
+
"value": "Call"
|
|
182
|
+
},
|
|
183
|
+
"SystemModstamp": {
|
|
184
|
+
"displayValue": "4/13/2026, 1:50 PM",
|
|
185
|
+
"value": "2026-04-13T20:50:05.000Z"
|
|
186
|
+
},
|
|
187
|
+
"What": {
|
|
188
|
+
"displayValue": "00001027",
|
|
189
|
+
"value": {
|
|
190
|
+
"apiName": "Name",
|
|
191
|
+
"childRelationships": {},
|
|
192
|
+
"eTag": "deffd76450e8f3c8fc116a1cb6a0bb6b",
|
|
193
|
+
"fields": {
|
|
194
|
+
"Id": {
|
|
195
|
+
"displayValue": null,
|
|
196
|
+
"value": "500xx000000boDJAAY"
|
|
197
|
+
},
|
|
198
|
+
"Name": {
|
|
199
|
+
"displayValue": null,
|
|
200
|
+
"value": "00001027"
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
"id": "500xx000000boDJAAY",
|
|
204
|
+
"lastModifiedById": null,
|
|
205
|
+
"lastModifiedDate": null,
|
|
206
|
+
"recordTypeId": null,
|
|
207
|
+
"recordTypeInfo": null,
|
|
208
|
+
"systemModstamp": null,
|
|
209
|
+
"weakEtag": 0
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
"WhatId": {
|
|
213
|
+
"displayValue": null,
|
|
214
|
+
"value": "500xx000000boDJAAY"
|
|
215
|
+
},
|
|
216
|
+
"Who": {
|
|
217
|
+
"displayValue": null,
|
|
218
|
+
"value": null
|
|
219
|
+
},
|
|
220
|
+
"WhoId": {
|
|
221
|
+
"displayValue": null,
|
|
222
|
+
"value": null
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
"id": "00Txx000003rIcmEAE",
|
|
226
|
+
"lastModifiedById": "005xx000001X8gHAAS",
|
|
227
|
+
"lastModifiedDate": "2026-04-13T20:50:05.000Z",
|
|
228
|
+
"recordTypeId": "012000000000000AAA",
|
|
229
|
+
"recordTypeInfo": null,
|
|
230
|
+
"systemModstamp": "2026-04-13T20:50:05.000Z",
|
|
231
|
+
"weakEtag": 1776113405000
|
|
232
|
+
}
|
|
233
|
+
]
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"apiName": "Task",
|
|
4
|
+
"childRelationships": {},
|
|
5
|
+
"eTag": "49e9f990d8b63da61c32917c3a0ef32b",
|
|
6
|
+
"fields": {
|
|
7
|
+
"ActivityDate": {
|
|
8
|
+
"displayValue": null,
|
|
9
|
+
"value": null
|
|
10
|
+
},
|
|
11
|
+
"Case__c": {
|
|
12
|
+
"displayValue": null,
|
|
13
|
+
"value": "500xx000000boDJAAY"
|
|
14
|
+
},
|
|
15
|
+
"Case__r": {
|
|
16
|
+
"displayValue": "00001027",
|
|
17
|
+
"value": {
|
|
18
|
+
"apiName": "Case",
|
|
19
|
+
"childRelationships": {},
|
|
20
|
+
"eTag": "d1912a07365b1f4f276cc2b60f857107",
|
|
21
|
+
"fields": {
|
|
22
|
+
"CaseNumber": {
|
|
23
|
+
"displayValue": null,
|
|
24
|
+
"value": "00001027"
|
|
25
|
+
},
|
|
26
|
+
"Id": {
|
|
27
|
+
"displayValue": null,
|
|
28
|
+
"value": "500xx000000boDJAAY"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"id": "500xx000000boDJAAY",
|
|
32
|
+
"lastModifiedById": "005xx000001X8gHAAS",
|
|
33
|
+
"lastModifiedDate": "2026-04-13T20:54:41.000Z",
|
|
34
|
+
"recordTypeId": "012000000000000AAA",
|
|
35
|
+
"recordTypeInfo": null,
|
|
36
|
+
"systemModstamp": "2026-04-13T20:54:41.000Z",
|
|
37
|
+
"weakEtag": 1776113681000
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"CreatedBy": {
|
|
41
|
+
"displayValue": "Ethan Chan",
|
|
42
|
+
"value": {
|
|
43
|
+
"apiName": "User",
|
|
44
|
+
"childRelationships": {},
|
|
45
|
+
"eTag": "2ca65e8b3117284d9253195c82c5f03d",
|
|
46
|
+
"fields": {
|
|
47
|
+
"FirstName": {
|
|
48
|
+
"displayValue": null,
|
|
49
|
+
"value": "Ethan"
|
|
50
|
+
},
|
|
51
|
+
"Id": {
|
|
52
|
+
"displayValue": null,
|
|
53
|
+
"value": "005xx000001X8gHAAS"
|
|
54
|
+
},
|
|
55
|
+
"LastName": {
|
|
56
|
+
"displayValue": null,
|
|
57
|
+
"value": "Chan"
|
|
58
|
+
},
|
|
59
|
+
"Name": {
|
|
60
|
+
"displayValue": null,
|
|
61
|
+
"value": "Ethan Chan"
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"id": "005xx000001X8gHAAS",
|
|
65
|
+
"lastModifiedById": "005xx000001X8gHAAS",
|
|
66
|
+
"lastModifiedDate": "2026-04-13T19:07:57.000Z",
|
|
67
|
+
"recordTypeId": null,
|
|
68
|
+
"recordTypeInfo": null,
|
|
69
|
+
"systemModstamp": "2026-04-13T19:07:57.000Z",
|
|
70
|
+
"weakEtag": 1776107277000
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"CreatedById": {
|
|
74
|
+
"displayValue": null,
|
|
75
|
+
"value": "005xx000001X8gHAAS"
|
|
76
|
+
},
|
|
77
|
+
"CreatedDate": {
|
|
78
|
+
"displayValue": "4/13/2026, 12:09 PM",
|
|
79
|
+
"value": "2026-04-13T19:09:48.000Z"
|
|
80
|
+
},
|
|
81
|
+
"Description": {
|
|
82
|
+
"displayValue": null,
|
|
83
|
+
"value": null
|
|
84
|
+
},
|
|
85
|
+
"Id": {
|
|
86
|
+
"displayValue": null,
|
|
87
|
+
"value": "00Txx000003rIcmEAE"
|
|
88
|
+
},
|
|
89
|
+
"IsClosed": {
|
|
90
|
+
"displayValue": null,
|
|
91
|
+
"value": true
|
|
92
|
+
},
|
|
93
|
+
"LastModifiedBy": {
|
|
94
|
+
"displayValue": "Ethan Chan",
|
|
95
|
+
"value": {
|
|
96
|
+
"apiName": "User",
|
|
97
|
+
"childRelationships": {},
|
|
98
|
+
"eTag": "2ca65e8b3117284d9253195c82c5f03d",
|
|
99
|
+
"fields": {
|
|
100
|
+
"FirstName": {
|
|
101
|
+
"displayValue": null,
|
|
102
|
+
"value": "Ethan"
|
|
103
|
+
},
|
|
104
|
+
"Id": {
|
|
105
|
+
"displayValue": null,
|
|
106
|
+
"value": "005xx000001X8gHAAS"
|
|
107
|
+
},
|
|
108
|
+
"LastName": {
|
|
109
|
+
"displayValue": null,
|
|
110
|
+
"value": "Chan"
|
|
111
|
+
},
|
|
112
|
+
"Name": {
|
|
113
|
+
"displayValue": null,
|
|
114
|
+
"value": "Ethan Chan"
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
"id": "005xx000001X8gHAAS",
|
|
118
|
+
"lastModifiedById": "005xx000001X8gHAAS",
|
|
119
|
+
"lastModifiedDate": "2026-04-13T19:07:57.000Z",
|
|
120
|
+
"recordTypeId": null,
|
|
121
|
+
"recordTypeInfo": null,
|
|
122
|
+
"systemModstamp": "2026-04-13T19:07:57.000Z",
|
|
123
|
+
"weakEtag": 1776107277000
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
"LastModifiedById": {
|
|
127
|
+
"displayValue": null,
|
|
128
|
+
"value": "005xx000001X8gHAAS"
|
|
129
|
+
},
|
|
130
|
+
"LastModifiedDate": {
|
|
131
|
+
"displayValue": "4/13/2026, 1:54 PM",
|
|
132
|
+
"value": "2026-04-13T20:54:41.000Z"
|
|
133
|
+
},
|
|
134
|
+
"Owner": {
|
|
135
|
+
"displayValue": "Ethan Chan",
|
|
136
|
+
"value": {
|
|
137
|
+
"apiName": "Name",
|
|
138
|
+
"childRelationships": {},
|
|
139
|
+
"eTag": "202a115a07830e8af0343368d4d309d4",
|
|
140
|
+
"fields": {
|
|
141
|
+
"FirstName": {
|
|
142
|
+
"displayValue": null,
|
|
143
|
+
"value": "Ethan"
|
|
144
|
+
},
|
|
145
|
+
"Id": {
|
|
146
|
+
"displayValue": null,
|
|
147
|
+
"value": "005xx000001X8gHAAS"
|
|
148
|
+
},
|
|
149
|
+
"LastName": {
|
|
150
|
+
"displayValue": null,
|
|
151
|
+
"value": "Chan"
|
|
152
|
+
},
|
|
153
|
+
"Name": {
|
|
154
|
+
"displayValue": null,
|
|
155
|
+
"value": "Ethan Chan"
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
"id": "005xx000001X8gHAAS",
|
|
159
|
+
"lastModifiedById": null,
|
|
160
|
+
"lastModifiedDate": null,
|
|
161
|
+
"recordTypeId": null,
|
|
162
|
+
"recordTypeInfo": null,
|
|
163
|
+
"systemModstamp": null,
|
|
164
|
+
"weakEtag": 0
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
"OwnerId": {
|
|
168
|
+
"displayValue": null,
|
|
169
|
+
"value": "005xx000001X8gHAAS"
|
|
170
|
+
},
|
|
171
|
+
"Priority": {
|
|
172
|
+
"displayValue": "High",
|
|
173
|
+
"value": "High"
|
|
174
|
+
},
|
|
175
|
+
"Status": {
|
|
176
|
+
"displayValue": "Completed",
|
|
177
|
+
"value": "Completed"
|
|
178
|
+
},
|
|
179
|
+
"Subject": {
|
|
180
|
+
"displayValue": null,
|
|
181
|
+
"value": "Call"
|
|
182
|
+
},
|
|
183
|
+
"SystemModstamp": {
|
|
184
|
+
"displayValue": "4/13/2026, 1:54 PM",
|
|
185
|
+
"value": "2026-04-13T20:54:41.000Z"
|
|
186
|
+
},
|
|
187
|
+
"What": {
|
|
188
|
+
"displayValue": "00001027",
|
|
189
|
+
"value": {
|
|
190
|
+
"apiName": "Name",
|
|
191
|
+
"childRelationships": {},
|
|
192
|
+
"eTag": "deffd76450e8f3c8fc116a1cb6a0bb6b",
|
|
193
|
+
"fields": {
|
|
194
|
+
"Id": {
|
|
195
|
+
"displayValue": null,
|
|
196
|
+
"value": "500xx000000boDJAAY"
|
|
197
|
+
},
|
|
198
|
+
"Name": {
|
|
199
|
+
"displayValue": null,
|
|
200
|
+
"value": "00001027"
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
"id": "500xx000000boDJAAY",
|
|
204
|
+
"lastModifiedById": null,
|
|
205
|
+
"lastModifiedDate": null,
|
|
206
|
+
"recordTypeId": null,
|
|
207
|
+
"recordTypeInfo": null,
|
|
208
|
+
"systemModstamp": null,
|
|
209
|
+
"weakEtag": 0
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
"WhatId": {
|
|
213
|
+
"displayValue": null,
|
|
214
|
+
"value": "500xx000000boDJAAY"
|
|
215
|
+
},
|
|
216
|
+
"Who": {
|
|
217
|
+
"displayValue": null,
|
|
218
|
+
"value": null
|
|
219
|
+
},
|
|
220
|
+
"WhoId": {
|
|
221
|
+
"displayValue": null,
|
|
222
|
+
"value": null
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
"id": "00Txx000003rIcmEAE",
|
|
226
|
+
"lastModifiedById": "005xx000001X8gHAAS",
|
|
227
|
+
"lastModifiedDate": "2026-04-13T20:54:41.000Z",
|
|
228
|
+
"recordTypeId": "012000000000000AAA",
|
|
229
|
+
"recordTypeInfo": null,
|
|
230
|
+
"systemModstamp": "2026-04-13T20:54:41.000Z",
|
|
231
|
+
"weakEtag": 1776113681000
|
|
232
|
+
}
|
|
233
|
+
]
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"apiName": "Task",
|
|
4
|
+
"childRelationships": {},
|
|
5
|
+
"eTag": "e339e322e2906b92f0faa82d0b0987e2",
|
|
6
|
+
"fields": {
|
|
7
|
+
"ActivityDate": {
|
|
8
|
+
"displayValue": null,
|
|
9
|
+
"value": null
|
|
10
|
+
},
|
|
11
|
+
"Case__c": {
|
|
12
|
+
"displayValue": null,
|
|
13
|
+
"value": "500xx000000boDJAAY"
|
|
14
|
+
},
|
|
15
|
+
"Case__r": {
|
|
16
|
+
"displayValue": "00001027",
|
|
17
|
+
"value": {
|
|
18
|
+
"apiName": "Case",
|
|
19
|
+
"childRelationships": {},
|
|
20
|
+
"eTag": "1e500759421709c0bff030ea20a7485d",
|
|
21
|
+
"fields": {
|
|
22
|
+
"CaseNumber": {
|
|
23
|
+
"displayValue": null,
|
|
24
|
+
"value": "00001027"
|
|
25
|
+
},
|
|
26
|
+
"Id": {
|
|
27
|
+
"displayValue": null,
|
|
28
|
+
"value": "500xx000000boDJAAY"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"id": "500xx000000boDJAAY",
|
|
32
|
+
"lastModifiedById": null,
|
|
33
|
+
"lastModifiedDate": null,
|
|
34
|
+
"recordTypeId": "012000000000000AAA",
|
|
35
|
+
"recordTypeInfo": null,
|
|
36
|
+
"systemModstamp": null,
|
|
37
|
+
"weakEtag": 0
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"CreatedBy": {
|
|
41
|
+
"displayValue": "Ethan Chan",
|
|
42
|
+
"value": {
|
|
43
|
+
"apiName": "User",
|
|
44
|
+
"childRelationships": {},
|
|
45
|
+
"eTag": "6209fb69d2a4b5c09799b1bd5f93bcca",
|
|
46
|
+
"fields": {
|
|
47
|
+
"FirstName": {
|
|
48
|
+
"displayValue": null,
|
|
49
|
+
"value": "Ethan"
|
|
50
|
+
},
|
|
51
|
+
"Id": {
|
|
52
|
+
"displayValue": null,
|
|
53
|
+
"value": "005xx000001X8gHAAS"
|
|
54
|
+
},
|
|
55
|
+
"LastName": {
|
|
56
|
+
"displayValue": null,
|
|
57
|
+
"value": "Chan"
|
|
58
|
+
},
|
|
59
|
+
"Name": {
|
|
60
|
+
"displayValue": null,
|
|
61
|
+
"value": "Ethan Chan"
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"id": "005xx000001X8gHAAS",
|
|
65
|
+
"lastModifiedById": null,
|
|
66
|
+
"lastModifiedDate": null,
|
|
67
|
+
"recordTypeId": null,
|
|
68
|
+
"recordTypeInfo": null,
|
|
69
|
+
"systemModstamp": null,
|
|
70
|
+
"weakEtag": 0
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"CreatedById": {
|
|
74
|
+
"displayValue": null,
|
|
75
|
+
"value": "005xx000001X8gHAAS"
|
|
76
|
+
},
|
|
77
|
+
"CreatedDate": {
|
|
78
|
+
"displayValue": "4/13/2026, 12:09 PM",
|
|
79
|
+
"value": "2026-04-13T19:09:48.000Z"
|
|
80
|
+
},
|
|
81
|
+
"Description": {
|
|
82
|
+
"displayValue": null,
|
|
83
|
+
"value": null
|
|
84
|
+
},
|
|
85
|
+
"Id": {
|
|
86
|
+
"displayValue": null,
|
|
87
|
+
"value": "00Txx000003rIcmEAE"
|
|
88
|
+
},
|
|
89
|
+
"IsClosed": {
|
|
90
|
+
"displayValue": null,
|
|
91
|
+
"value": true
|
|
92
|
+
},
|
|
93
|
+
"LastModifiedBy": {
|
|
94
|
+
"displayValue": "Ethan Chan",
|
|
95
|
+
"value": {
|
|
96
|
+
"apiName": "User",
|
|
97
|
+
"childRelationships": {},
|
|
98
|
+
"eTag": "6209fb69d2a4b5c09799b1bd5f93bcca",
|
|
99
|
+
"fields": {
|
|
100
|
+
"FirstName": {
|
|
101
|
+
"displayValue": null,
|
|
102
|
+
"value": "Ethan"
|
|
103
|
+
},
|
|
104
|
+
"Id": {
|
|
105
|
+
"displayValue": null,
|
|
106
|
+
"value": "005xx000001X8gHAAS"
|
|
107
|
+
},
|
|
108
|
+
"LastName": {
|
|
109
|
+
"displayValue": null,
|
|
110
|
+
"value": "Chan"
|
|
111
|
+
},
|
|
112
|
+
"Name": {
|
|
113
|
+
"displayValue": null,
|
|
114
|
+
"value": "Ethan Chan"
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
"id": "005xx000001X8gHAAS",
|
|
118
|
+
"lastModifiedById": null,
|
|
119
|
+
"lastModifiedDate": null,
|
|
120
|
+
"recordTypeId": null,
|
|
121
|
+
"recordTypeInfo": null,
|
|
122
|
+
"systemModstamp": null,
|
|
123
|
+
"weakEtag": 0
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
"LastModifiedById": {
|
|
127
|
+
"displayValue": null,
|
|
128
|
+
"value": "005xx000001X8gHAAS"
|
|
129
|
+
},
|
|
130
|
+
"LastModifiedDate": {
|
|
131
|
+
"displayValue": "4/13/2026, 1:54 PM",
|
|
132
|
+
"value": "2026-04-13T20:54:41.000Z"
|
|
133
|
+
},
|
|
134
|
+
"Owner": {
|
|
135
|
+
"displayValue": "Ethan Chan",
|
|
136
|
+
"value": {
|
|
137
|
+
"apiName": "Name",
|
|
138
|
+
"childRelationships": {},
|
|
139
|
+
"eTag": "202a115a07830e8af0343368d4d309d4",
|
|
140
|
+
"fields": {
|
|
141
|
+
"FirstName": {
|
|
142
|
+
"displayValue": null,
|
|
143
|
+
"value": "Ethan"
|
|
144
|
+
},
|
|
145
|
+
"Id": {
|
|
146
|
+
"displayValue": null,
|
|
147
|
+
"value": "005xx000001X8gHAAS"
|
|
148
|
+
},
|
|
149
|
+
"LastName": {
|
|
150
|
+
"displayValue": null,
|
|
151
|
+
"value": "Chan"
|
|
152
|
+
},
|
|
153
|
+
"Name": {
|
|
154
|
+
"displayValue": null,
|
|
155
|
+
"value": "Ethan Chan"
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
"id": "005xx000001X8gHAAS",
|
|
159
|
+
"lastModifiedById": null,
|
|
160
|
+
"lastModifiedDate": null,
|
|
161
|
+
"recordTypeId": null,
|
|
162
|
+
"recordTypeInfo": null,
|
|
163
|
+
"systemModstamp": null,
|
|
164
|
+
"weakEtag": 0
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
"OwnerId": {
|
|
168
|
+
"displayValue": null,
|
|
169
|
+
"value": "005xx000001X8gHAAS"
|
|
170
|
+
},
|
|
171
|
+
"Priority": {
|
|
172
|
+
"displayValue": "High",
|
|
173
|
+
"value": "High"
|
|
174
|
+
},
|
|
175
|
+
"Status": {
|
|
176
|
+
"displayValue": "Completed",
|
|
177
|
+
"value": "Completed"
|
|
178
|
+
},
|
|
179
|
+
"Subject": {
|
|
180
|
+
"displayValue": null,
|
|
181
|
+
"value": "Call"
|
|
182
|
+
},
|
|
183
|
+
"SystemModstamp": {
|
|
184
|
+
"displayValue": "4/13/2026, 1:54 PM",
|
|
185
|
+
"value": "2026-04-13T20:54:41.000Z"
|
|
186
|
+
},
|
|
187
|
+
"What": {
|
|
188
|
+
"displayValue": "00001027",
|
|
189
|
+
"value": {
|
|
190
|
+
"apiName": "Name",
|
|
191
|
+
"childRelationships": {},
|
|
192
|
+
"eTag": "deffd76450e8f3c8fc116a1cb6a0bb6b",
|
|
193
|
+
"fields": {
|
|
194
|
+
"Id": {
|
|
195
|
+
"displayValue": null,
|
|
196
|
+
"value": "500xx000000boDJAAY"
|
|
197
|
+
},
|
|
198
|
+
"Name": {
|
|
199
|
+
"displayValue": null,
|
|
200
|
+
"value": "00001027"
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
"id": "500xx000000boDJAAY",
|
|
204
|
+
"lastModifiedById": null,
|
|
205
|
+
"lastModifiedDate": null,
|
|
206
|
+
"recordTypeId": null,
|
|
207
|
+
"recordTypeInfo": null,
|
|
208
|
+
"systemModstamp": null,
|
|
209
|
+
"weakEtag": 0
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
"WhatId": {
|
|
213
|
+
"displayValue": null,
|
|
214
|
+
"value": "500xx000000boDJAAY"
|
|
215
|
+
},
|
|
216
|
+
"Who": {
|
|
217
|
+
"displayValue": null,
|
|
218
|
+
"value": null
|
|
219
|
+
},
|
|
220
|
+
"WhoId": {
|
|
221
|
+
"displayValue": null,
|
|
222
|
+
"value": null
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
"id": "00Txx000003rIcmEAE",
|
|
226
|
+
"lastModifiedById": "005xx000001X8gHAAS",
|
|
227
|
+
"lastModifiedDate": "2026-04-13T20:54:41.000Z",
|
|
228
|
+
"recordTypeId": "012000000000000AAA",
|
|
229
|
+
"recordTypeInfo": null,
|
|
230
|
+
"systemModstamp": "2026-04-13T20:54:41.000Z",
|
|
231
|
+
"weakEtag": 1776113681000
|
|
232
|
+
}
|
|
233
|
+
]
|
package/src/ads-bridge.ts
CHANGED
|
@@ -107,6 +107,10 @@ function isStoreKeyRecordId(key: string) {
|
|
|
107
107
|
return key.indexOf(RECORD_ID_PREFIX) > -1 && key.indexOf(RECORD_FIELDS_KEY_JUNCTION) === -1;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
function isStoreKeyRecordField(key: string) {
|
|
111
|
+
return key.indexOf(RECORD_ID_PREFIX) > -1 && key.indexOf(RECORD_FIELDS_KEY_JUNCTION) > -1;
|
|
112
|
+
}
|
|
113
|
+
|
|
110
114
|
/**
|
|
111
115
|
* Returns a shallow copy of a record with its field values if it is a scalar and a reference and a
|
|
112
116
|
* a RecordRepresentation with no field if the value if a spanning record.
|
|
@@ -488,6 +492,37 @@ export default class AdsBridge {
|
|
|
488
492
|
}
|
|
489
493
|
}
|
|
490
494
|
|
|
495
|
+
private filterAndSynthesizeBaseIds(updatedEntries: { id: string }[]): { id: string }[] {
|
|
496
|
+
// Collect base record IDs that are directly present in the entries.
|
|
497
|
+
const directBaseIds = new Set<string>();
|
|
498
|
+
for (let i = 0; i < updatedEntries.length; i++) {
|
|
499
|
+
if (isStoreKeyRecordId(updatedEntries[i].id)) {
|
|
500
|
+
directBaseIds.add(updatedEntries[i].id);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// For field entries whose base record ID has no direct entry, synthesize one.
|
|
505
|
+
const syntheticBaseIds = new Set<string>();
|
|
506
|
+
for (let i = 0; i < updatedEntries.length; i++) {
|
|
507
|
+
if (isStoreKeyRecordField(updatedEntries[i].id)) {
|
|
508
|
+
const baseId = updatedEntries[i].id.split(RECORD_FIELDS_KEY_JUNCTION)[0];
|
|
509
|
+
if (!directBaseIds.has(baseId)) {
|
|
510
|
+
syntheticBaseIds.add(baseId);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
// Exclude all the store record ids not matching with the record id pattern.
|
|
515
|
+
// Note: FieldValueRepresentation have the same prefix than RecordRepresentation so we
|
|
516
|
+
// need to filter them out.
|
|
517
|
+
const filteredUpdatedEntries: { id: string }[] = updatedEntries.filter((entry) =>
|
|
518
|
+
isStoreKeyRecordId(entry.id)
|
|
519
|
+
);
|
|
520
|
+
syntheticBaseIds.forEach((baseId) => {
|
|
521
|
+
ArrayPrototypePush.call(filteredUpdatedEntries, { id: baseId });
|
|
522
|
+
});
|
|
523
|
+
return filteredUpdatedEntries;
|
|
524
|
+
}
|
|
525
|
+
|
|
491
526
|
/**
|
|
492
527
|
* This method retrieves queries the store with with passed record ids to retrieve their
|
|
493
528
|
* associated records and object info. Note that the passed ids are not Salesforce record id
|
|
@@ -504,16 +539,11 @@ export default class AdsBridge {
|
|
|
504
539
|
const adsRecordMap: AdsRecordMap = {};
|
|
505
540
|
const adsObjectMap: AdsObjectMetadataMap = {};
|
|
506
541
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
// need to filter them out.
|
|
513
|
-
if (!isStoreKeyRecordId(storeRecordId)) {
|
|
514
|
-
continue;
|
|
515
|
-
}
|
|
516
|
-
|
|
542
|
+
// W-21715343
|
|
543
|
+
// Context for change: https://docs.google.com/document/d/1iF6M9jldEX_K9FOpdttLqVwO9uESUUrlyWi2mWnXtAQ/edit?usp=sharing
|
|
544
|
+
const filteredUpdatedEntries = this.filterAndSynthesizeBaseIds(updatedEntries);
|
|
545
|
+
for (let i = 0; i < filteredUpdatedEntries.length; i++) {
|
|
546
|
+
const storeRecordId = filteredUpdatedEntries[i].id;
|
|
517
547
|
const record =
|
|
518
548
|
this.recordRepresentationIngestOverride !== undefined
|
|
519
549
|
? getShallowRecordDenormalized(luvio, storeRecordId)
|