@salesforce/lds-runtime-mobile 1.156.1 → 1.157.1
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
CHANGED
|
@@ -31,6 +31,7 @@ import formattingOptions from 'lightning/i18nCldrOptions';
|
|
|
31
31
|
import eagerEvalValidAt from '@salesforce/gate/lds.eagerEvalValidAt';
|
|
32
32
|
import eagerEvalStaleWhileRevalidate from '@salesforce/gate/lds.eagerEvalStaleWhileRevalidate';
|
|
33
33
|
import eagerEvalDefaultCachePolicy from '@salesforce/gate/lds.eagerEvalDefaultCachePolicy';
|
|
34
|
+
import ldsPrimingGraphqlBatch from '@salesforce/gate/lds.primingGraphqlBatch';
|
|
34
35
|
|
|
35
36
|
/**
|
|
36
37
|
* Copyright (c) 2022, Salesforce, Inc.,
|
|
@@ -9052,7 +9053,7 @@ function dataTypeToType(objectInfoDataType, apiName) {
|
|
|
9052
9053
|
case 'Url':
|
|
9053
9054
|
return 'UrlValue';
|
|
9054
9055
|
case 'Picklist':
|
|
9055
|
-
return '
|
|
9056
|
+
return 'PicklistValue';
|
|
9056
9057
|
case 'MultiPicklist':
|
|
9057
9058
|
return 'MultiPicklistValue';
|
|
9058
9059
|
case 'Percent':
|
|
@@ -15365,16 +15366,19 @@ function generateTypedBatches(work, batchSize) {
|
|
|
15365
15366
|
|
|
15366
15367
|
const DEFAULT_BATCH_SIZE = 500;
|
|
15367
15368
|
const DEFAULT_CONCURRENCY = 6;
|
|
15369
|
+
const DEFAULT_GQL_QUERY_BATCH_SIZE = 5;
|
|
15368
15370
|
class PrimingSession extends EventEmitter {
|
|
15369
15371
|
constructor(config) {
|
|
15370
15372
|
var _a, _b;
|
|
15371
15373
|
super();
|
|
15374
|
+
this.useBatchGQL = false;
|
|
15372
15375
|
this.batchSize = (_a = config.batchSize) !== null && _a !== void 0 ? _a : DEFAULT_BATCH_SIZE;
|
|
15373
15376
|
this.concurrency = (_b = config.concurrency) !== null && _b !== void 0 ? _b : DEFAULT_CONCURRENCY;
|
|
15374
15377
|
this.recordLoader = config.recordLoader;
|
|
15375
15378
|
this.recordIngestor = config.recordIngestor;
|
|
15376
15379
|
this.objectInfoLoader = config.objectInfoLoader;
|
|
15377
15380
|
this.networkWorkerPool = new AsyncWorkerPool(this.concurrency);
|
|
15381
|
+
this.useBatchGQL = ldsPrimingGraphqlBatch.isOpen({ fallback: false });
|
|
15378
15382
|
}
|
|
15379
15383
|
// function that enqueues priming work
|
|
15380
15384
|
async enqueue(work) {
|
|
@@ -15409,91 +15413,129 @@ class PrimingSession extends EventEmitter {
|
|
|
15409
15413
|
}
|
|
15410
15414
|
// parallelizes batches of priming work
|
|
15411
15415
|
enqueueBatches(batches) {
|
|
15412
|
-
|
|
15413
|
-
const
|
|
15414
|
-
|
|
15415
|
-
|
|
15416
|
-
|
|
15417
|
-
|
|
15418
|
-
|
|
15419
|
-
.
|
|
15420
|
-
|
|
15421
|
-
|
|
15422
|
-
|
|
15423
|
-
|
|
15424
|
-
|
|
15416
|
+
if (this.useBatchGQL === false) {
|
|
15417
|
+
for (const batch of batches) {
|
|
15418
|
+
const queuedTime = Date.now();
|
|
15419
|
+
this.networkWorkerPool.push({
|
|
15420
|
+
workFn: (abortController) => {
|
|
15421
|
+
const workTime = Date.now();
|
|
15422
|
+
this.emit('batch-starting', { queuedTime: workTime - queuedTime });
|
|
15423
|
+
return this.recordLoader
|
|
15424
|
+
.fetchRecordData(batch, abortController)
|
|
15425
|
+
.then(async (result) => {
|
|
15426
|
+
this.emit('batch-fetched', {
|
|
15427
|
+
ids: batch.ids,
|
|
15428
|
+
duration: Date.now() - workTime,
|
|
15429
|
+
});
|
|
15430
|
+
this.processFetchedRecords(result, abortController);
|
|
15431
|
+
});
|
|
15432
|
+
},
|
|
15433
|
+
cancelFn: () => {
|
|
15434
|
+
this.emit('error', {
|
|
15425
15435
|
ids: batch.ids,
|
|
15426
|
-
|
|
15436
|
+
code: 'canceled',
|
|
15437
|
+
message: `batch canceled`,
|
|
15427
15438
|
});
|
|
15428
|
-
|
|
15429
|
-
|
|
15430
|
-
|
|
15431
|
-
|
|
15432
|
-
|
|
15433
|
-
|
|
15434
|
-
|
|
15435
|
-
|
|
15436
|
-
|
|
15437
|
-
|
|
15438
|
-
const
|
|
15439
|
-
|
|
15440
|
-
|
|
15441
|
-
|
|
15442
|
-
|
|
15443
|
-
message: `could not find records: ${missingIds.join(', ')}`,
|
|
15444
|
-
});
|
|
15445
|
-
}
|
|
15446
|
-
const { records } = result;
|
|
15447
|
-
const beforeWrite = Date.now();
|
|
15448
|
-
// dispatch the write but DO NOT wait on it to unblock the network pool
|
|
15449
|
-
this.recordIngestor
|
|
15450
|
-
.insertRecords(records)
|
|
15451
|
-
.then(({ written, conflicted, errors }) => {
|
|
15452
|
-
this.emit('batch-written', {
|
|
15453
|
-
written,
|
|
15454
|
-
conflicted,
|
|
15455
|
-
errors: errors
|
|
15456
|
-
.map((e) => e.ids)
|
|
15457
|
-
.reduce((a, b) => a.concat(b), []),
|
|
15458
|
-
duration: Date.now() - beforeWrite,
|
|
15459
|
-
});
|
|
15439
|
+
},
|
|
15440
|
+
});
|
|
15441
|
+
}
|
|
15442
|
+
}
|
|
15443
|
+
else {
|
|
15444
|
+
const chucks = chunk(batches, DEFAULT_GQL_QUERY_BATCH_SIZE);
|
|
15445
|
+
for (const batchChuck of chucks) {
|
|
15446
|
+
const queuedTime = Date.now();
|
|
15447
|
+
this.networkWorkerPool.push({
|
|
15448
|
+
workFn: (abortController) => {
|
|
15449
|
+
const workTime = Date.now();
|
|
15450
|
+
this.emit('batch-starting', { queuedTime: workTime - queuedTime });
|
|
15451
|
+
return this.recordLoader
|
|
15452
|
+
.batchFetchRecordData(batchChuck, abortController)
|
|
15453
|
+
.then(async (results) => {
|
|
15460
15454
|
if (abortController.aborted) {
|
|
15461
15455
|
return;
|
|
15462
15456
|
}
|
|
15463
|
-
|
|
15464
|
-
|
|
15465
|
-
|
|
15466
|
-
|
|
15467
|
-
|
|
15468
|
-
|
|
15469
|
-
});
|
|
15457
|
+
const duration = Date.now() - workTime;
|
|
15458
|
+
// For each query within the Batch gql query, result returns at the same time
|
|
15459
|
+
for (let i = 0; i < results.length; i++) {
|
|
15460
|
+
this.emit('batch-fetched', {
|
|
15461
|
+
ids: batchChuck[i].ids,
|
|
15462
|
+
duration,
|
|
15470
15463
|
});
|
|
15471
15464
|
}
|
|
15472
|
-
|
|
15473
|
-
|
|
15474
|
-
this.emit('primed', Array.from(written));
|
|
15475
|
-
}
|
|
15476
|
-
// TODO [W-12436213]: implement conflict resolution
|
|
15477
|
-
if (conflicted.length > 0) {
|
|
15478
|
-
// for now emit conlicts as errors
|
|
15479
|
-
this.emit('error', {
|
|
15480
|
-
ids: Array.from(conflicted),
|
|
15481
|
-
code: 'unknown',
|
|
15482
|
-
message: 'conflict when persisting record',
|
|
15483
|
-
});
|
|
15465
|
+
for (let i = 0; i < results.length; i++) {
|
|
15466
|
+
this.processFetchedRecords(results[i], abortController);
|
|
15484
15467
|
}
|
|
15485
15468
|
});
|
|
15486
|
-
}
|
|
15487
|
-
|
|
15488
|
-
|
|
15489
|
-
|
|
15490
|
-
|
|
15491
|
-
|
|
15492
|
-
|
|
15493
|
-
|
|
15494
|
-
|
|
15469
|
+
},
|
|
15470
|
+
cancelFn: () => {
|
|
15471
|
+
const chuckIds = batchChuck
|
|
15472
|
+
.map((batch) => batch.ids)
|
|
15473
|
+
.reduce((prev, curr) => prev.concat(curr), []);
|
|
15474
|
+
this.emit('error', {
|
|
15475
|
+
ids: chuckIds,
|
|
15476
|
+
code: 'canceled',
|
|
15477
|
+
message: `batch canceled`,
|
|
15478
|
+
});
|
|
15479
|
+
},
|
|
15480
|
+
});
|
|
15481
|
+
}
|
|
15482
|
+
}
|
|
15483
|
+
}
|
|
15484
|
+
processFetchedRecords(result, abortController) {
|
|
15485
|
+
if (result.ok === false) {
|
|
15486
|
+
const { error } = result;
|
|
15487
|
+
const primingError = error === 'network-error' ? 'service-unavailable' : 'unknown';
|
|
15488
|
+
this.emit('error', {
|
|
15489
|
+
ids: result.missingIds,
|
|
15490
|
+
code: primingError,
|
|
15491
|
+
message: `${result.messages.join(',')}`,
|
|
15492
|
+
});
|
|
15493
|
+
return;
|
|
15494
|
+
}
|
|
15495
|
+
const { missingIds } = result;
|
|
15496
|
+
if (missingIds.length > 0) {
|
|
15497
|
+
this.emit('error', {
|
|
15498
|
+
ids: missingIds,
|
|
15499
|
+
code: 'not-found',
|
|
15500
|
+
message: `could not find records: ${missingIds.join(', ')}`,
|
|
15495
15501
|
});
|
|
15496
15502
|
}
|
|
15503
|
+
const { records } = result;
|
|
15504
|
+
const beforeWrite = Date.now();
|
|
15505
|
+
// dispatch the write but DO NOT wait on it to unblock the network pool
|
|
15506
|
+
this.recordIngestor.insertRecords(records).then(({ written, conflicted, errors }) => {
|
|
15507
|
+
this.emit('batch-written', {
|
|
15508
|
+
written,
|
|
15509
|
+
conflicted,
|
|
15510
|
+
errors: errors.map((e) => e.ids).reduce((a, b) => a.concat(b), []),
|
|
15511
|
+
duration: Date.now() - beforeWrite,
|
|
15512
|
+
});
|
|
15513
|
+
if (abortController.aborted) {
|
|
15514
|
+
return;
|
|
15515
|
+
}
|
|
15516
|
+
if (errors.length > 0) {
|
|
15517
|
+
errors.forEach(({ ids, message }) => {
|
|
15518
|
+
this.emit('error', {
|
|
15519
|
+
ids,
|
|
15520
|
+
code: 'unknown',
|
|
15521
|
+
message: message,
|
|
15522
|
+
});
|
|
15523
|
+
});
|
|
15524
|
+
}
|
|
15525
|
+
// now that the records are persisted, emit the primed event
|
|
15526
|
+
if (written.length > 0) {
|
|
15527
|
+
this.emit('primed', Array.from(written));
|
|
15528
|
+
}
|
|
15529
|
+
// TODO [W-12436213]: implement conflict resolution
|
|
15530
|
+
if (conflicted.length > 0) {
|
|
15531
|
+
// for now emit conlicts as errors
|
|
15532
|
+
this.emit('error', {
|
|
15533
|
+
ids: Array.from(conflicted),
|
|
15534
|
+
code: 'unknown',
|
|
15535
|
+
message: 'conflict when persisting record',
|
|
15536
|
+
});
|
|
15537
|
+
}
|
|
15538
|
+
});
|
|
15497
15539
|
}
|
|
15498
15540
|
async fetchMetadata(batches) {
|
|
15499
15541
|
const apiNames = Array.from(batches.reduce((acc, x) => {
|
|
@@ -15546,12 +15588,39 @@ class RecordLoaderGraphQL {
|
|
|
15546
15588
|
missingIds: batch.ids,
|
|
15547
15589
|
};
|
|
15548
15590
|
}
|
|
15549
|
-
|
|
15591
|
+
return this.generateFetchResult(rep, batch);
|
|
15592
|
+
}
|
|
15593
|
+
async batchFetchRecordData(batchs, abortController) {
|
|
15594
|
+
let reps;
|
|
15595
|
+
try {
|
|
15596
|
+
reps = await this.callBatchGraphQL(batchs, abortController);
|
|
15597
|
+
}
|
|
15598
|
+
catch (e) {
|
|
15599
|
+
const missingIds = batchs
|
|
15600
|
+
.map((batch) => batch.ids)
|
|
15601
|
+
.reduce((prev, curr) => prev.concat(curr), []);
|
|
15602
|
+
return [
|
|
15603
|
+
{
|
|
15604
|
+
ok: false,
|
|
15605
|
+
error: 'network-error',
|
|
15606
|
+
messages: ['Network Error'],
|
|
15607
|
+
missingIds,
|
|
15608
|
+
},
|
|
15609
|
+
];
|
|
15610
|
+
}
|
|
15611
|
+
const recordFetchResults = [];
|
|
15612
|
+
for (let i = 0; i < reps.length; i++) {
|
|
15613
|
+
recordFetchResults.push(this.generateFetchResult(reps[i], batchs[i]));
|
|
15614
|
+
}
|
|
15615
|
+
return recordFetchResults;
|
|
15616
|
+
}
|
|
15617
|
+
generateFetchResult(repResult, batchInput) {
|
|
15618
|
+
const { data, errors } = repResult;
|
|
15550
15619
|
if (errors !== undefined && errors.length > 0) {
|
|
15551
15620
|
// right now if there are any errors in the batch we throw out the entire batch
|
|
15552
15621
|
// for now this is ok all errors will originate on the same node so there shouldn't be a mix of errors and data
|
|
15553
15622
|
return {
|
|
15554
|
-
missingIds:
|
|
15623
|
+
missingIds: batchInput.ids,
|
|
15555
15624
|
ok: false,
|
|
15556
15625
|
error: 'request-error',
|
|
15557
15626
|
messages: errors.map((x) => x.message),
|
|
@@ -15563,11 +15632,11 @@ class RecordLoaderGraphQL {
|
|
|
15563
15632
|
ok: false,
|
|
15564
15633
|
error: 'unknown',
|
|
15565
15634
|
messages: ['unexpected response retrieved from graphql endpoint'],
|
|
15566
|
-
missingIds:
|
|
15635
|
+
missingIds: batchInput.ids,
|
|
15567
15636
|
};
|
|
15568
15637
|
}
|
|
15569
|
-
const seenRecords = new Set(
|
|
15570
|
-
const records = data.uiapi.query[
|
|
15638
|
+
const seenRecords = new Set(batchInput.ids);
|
|
15639
|
+
const records = data.uiapi.query[batchInput.type].edges.map((edge) => {
|
|
15571
15640
|
const record = this.generateDurableRecordRepresentation(edge.node);
|
|
15572
15641
|
seenRecords.delete(record.id);
|
|
15573
15642
|
return record;
|
|
@@ -15582,6 +15651,15 @@ class RecordLoaderGraphQL {
|
|
|
15582
15651
|
const query = this.generateGraphQLQuery(batch.type, batch.fields);
|
|
15583
15652
|
return this.networkAdapter.postGraphQL(query, { ids: batch.ids, first: batch.ids.length }, abortController);
|
|
15584
15653
|
}
|
|
15654
|
+
callBatchGraphQL(batches, abortController) {
|
|
15655
|
+
const gqlInput = batches.map((batch) => {
|
|
15656
|
+
return {
|
|
15657
|
+
query: this.generateGraphQLQuery(batch.type, batch.fields),
|
|
15658
|
+
variables: { ids: batch.ids, first: batch.ids.length },
|
|
15659
|
+
};
|
|
15660
|
+
});
|
|
15661
|
+
return this.networkAdapter.postBatchGraphQL(gqlInput, abortController);
|
|
15662
|
+
}
|
|
15585
15663
|
generateGraphQLQuery(type, fields) {
|
|
15586
15664
|
const fieldList = Object.keys(requiredFieldMap)
|
|
15587
15665
|
.map((field) => {
|
|
@@ -15705,7 +15783,56 @@ function instrumentPrimingSession(session) {
|
|
|
15705
15783
|
/* global __nimbus */
|
|
15706
15784
|
// note this is automatically incremented by scripts/release/bump-api-version.js at each release
|
|
15707
15785
|
const apiVersion = `v59.0`;
|
|
15786
|
+
const batchEndPointPath = `/services/data/${apiVersion}/graphql/batch`;
|
|
15787
|
+
const endPointPath = `/services/data/${apiVersion}/graphql`;
|
|
15708
15788
|
class NimbusPrimingNetworkAdapter {
|
|
15789
|
+
postBatchGraphQL(configs, abortController) {
|
|
15790
|
+
return new Promise((resolve, reject) => {
|
|
15791
|
+
let listener;
|
|
15792
|
+
const unregisterListener = () => {
|
|
15793
|
+
if (listener) {
|
|
15794
|
+
abortController.removeEventListener(listener);
|
|
15795
|
+
}
|
|
15796
|
+
};
|
|
15797
|
+
__nimbus.plugins.LdsNetworkAdapter
|
|
15798
|
+
.sendRequest({
|
|
15799
|
+
method: 'POST',
|
|
15800
|
+
path: batchEndPointPath,
|
|
15801
|
+
body: JSON.stringify({
|
|
15802
|
+
batchQuery: configs,
|
|
15803
|
+
}),
|
|
15804
|
+
headers: {},
|
|
15805
|
+
queryParams: {},
|
|
15806
|
+
priority: 'background',
|
|
15807
|
+
observabilityContext: {},
|
|
15808
|
+
}, (response) => {
|
|
15809
|
+
unregisterListener();
|
|
15810
|
+
const { body } = response;
|
|
15811
|
+
if (body) {
|
|
15812
|
+
const { results } = JSON.parse(body);
|
|
15813
|
+
if (results) {
|
|
15814
|
+
const gqlResults = results.map((compositeGqlResult) => compositeGqlResult.result);
|
|
15815
|
+
resolve(gqlResults);
|
|
15816
|
+
}
|
|
15817
|
+
else {
|
|
15818
|
+
reject(new Error(`No body returned from ${batchEndPointPath} endpoint`));
|
|
15819
|
+
}
|
|
15820
|
+
}
|
|
15821
|
+
else {
|
|
15822
|
+
reject(new Error(`No body returned from ${batchEndPointPath} endpoint`));
|
|
15823
|
+
}
|
|
15824
|
+
}, (error) => {
|
|
15825
|
+
unregisterListener();
|
|
15826
|
+
reject(error);
|
|
15827
|
+
})
|
|
15828
|
+
.then((cancellationToken) => {
|
|
15829
|
+
listener = () => {
|
|
15830
|
+
__nimbus.plugins.LdsNetworkAdapter.cancelRequest(cancellationToken);
|
|
15831
|
+
};
|
|
15832
|
+
abortController.addEventListener(listener);
|
|
15833
|
+
});
|
|
15834
|
+
});
|
|
15835
|
+
}
|
|
15709
15836
|
postGraphQL(query, variables, abortController) {
|
|
15710
15837
|
return new Promise((resolve, reject) => {
|
|
15711
15838
|
let listener;
|
|
@@ -15717,7 +15844,7 @@ class NimbusPrimingNetworkAdapter {
|
|
|
15717
15844
|
__nimbus.plugins.LdsNetworkAdapter
|
|
15718
15845
|
.sendRequest({
|
|
15719
15846
|
method: 'POST',
|
|
15720
|
-
path:
|
|
15847
|
+
path: endPointPath,
|
|
15721
15848
|
body: JSON.stringify({
|
|
15722
15849
|
query,
|
|
15723
15850
|
variables,
|
|
@@ -15733,7 +15860,7 @@ class NimbusPrimingNetworkAdapter {
|
|
|
15733
15860
|
resolve(JSON.parse(body));
|
|
15734
15861
|
}
|
|
15735
15862
|
else {
|
|
15736
|
-
reject(new Error(
|
|
15863
|
+
reject(new Error(`No body returned from ${endPointPath} endpoint`));
|
|
15737
15864
|
}
|
|
15738
15865
|
}, (error) => {
|
|
15739
15866
|
unregisterListener();
|
|
@@ -15984,4 +16111,4 @@ register({
|
|
|
15984
16111
|
});
|
|
15985
16112
|
|
|
15986
16113
|
export { getRuntime, registerReportObserver, reportGraphqlQueryParseError };
|
|
15987
|
-
// version: 1.
|
|
16114
|
+
// version: 1.157.1-a0df85006
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { PrimingNetworkAdapter } from '@salesforce/lds-priming';
|
|
2
|
-
import type { GraphQLRepresentation } from '@salesforce/lds-adapters-uiapi';
|
|
2
|
+
import type { GraphQLRepresentation, GraphQLInputRepresentation } from '@salesforce/lds-adapters-uiapi';
|
|
3
3
|
import type { LdsAbortController } from '@salesforce/lds-utils-adapters';
|
|
4
4
|
export declare class NimbusPrimingNetworkAdapter implements PrimingNetworkAdapter {
|
|
5
|
+
postBatchGraphQL(configs: GraphQLInputRepresentation[], abortController: LdsAbortController): Promise<GraphQLRepresentation[]>;
|
|
5
6
|
postGraphQL(query: string, variables: Record<string, any>, abortController: LdsAbortController): Promise<GraphQLRepresentation>;
|
|
6
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/lds-runtime-mobile",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.157.1",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
5
5
|
"description": "LDS runtime for mobile/hybrid environments.",
|
|
6
6
|
"main": "dist/main.js",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"path": "./dist/main.js",
|
|
59
59
|
"maxSize": {
|
|
60
60
|
"none": "700 kB",
|
|
61
|
-
"min": "
|
|
61
|
+
"min": "282 kB",
|
|
62
62
|
"compressed": "110 kB"
|
|
63
63
|
}
|
|
64
64
|
},
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"path": "./sfdc/main.js",
|
|
67
67
|
"maxSize": {
|
|
68
68
|
"none": "700 kB",
|
|
69
|
-
"min": "
|
|
69
|
+
"min": "282 kB",
|
|
70
70
|
"compressed": "110 kB"
|
|
71
71
|
}
|
|
72
72
|
}
|
package/sfdc/main.js
CHANGED
|
@@ -31,6 +31,7 @@ import formattingOptions from 'lightning/i18nCldrOptions';
|
|
|
31
31
|
import eagerEvalValidAt from '@salesforce/gate/lds.eagerEvalValidAt';
|
|
32
32
|
import eagerEvalStaleWhileRevalidate from '@salesforce/gate/lds.eagerEvalStaleWhileRevalidate';
|
|
33
33
|
import eagerEvalDefaultCachePolicy from '@salesforce/gate/lds.eagerEvalDefaultCachePolicy';
|
|
34
|
+
import ldsPrimingGraphqlBatch from '@salesforce/gate/lds.primingGraphqlBatch';
|
|
34
35
|
|
|
35
36
|
/**
|
|
36
37
|
* Copyright (c) 2022, Salesforce, Inc.,
|
|
@@ -9052,7 +9053,7 @@ function dataTypeToType(objectInfoDataType, apiName) {
|
|
|
9052
9053
|
case 'Url':
|
|
9053
9054
|
return 'UrlValue';
|
|
9054
9055
|
case 'Picklist':
|
|
9055
|
-
return '
|
|
9056
|
+
return 'PicklistValue';
|
|
9056
9057
|
case 'MultiPicklist':
|
|
9057
9058
|
return 'MultiPicklistValue';
|
|
9058
9059
|
case 'Percent':
|
|
@@ -15365,16 +15366,19 @@ function generateTypedBatches(work, batchSize) {
|
|
|
15365
15366
|
|
|
15366
15367
|
const DEFAULT_BATCH_SIZE = 500;
|
|
15367
15368
|
const DEFAULT_CONCURRENCY = 6;
|
|
15369
|
+
const DEFAULT_GQL_QUERY_BATCH_SIZE = 5;
|
|
15368
15370
|
class PrimingSession extends EventEmitter {
|
|
15369
15371
|
constructor(config) {
|
|
15370
15372
|
var _a, _b;
|
|
15371
15373
|
super();
|
|
15374
|
+
this.useBatchGQL = false;
|
|
15372
15375
|
this.batchSize = (_a = config.batchSize) !== null && _a !== void 0 ? _a : DEFAULT_BATCH_SIZE;
|
|
15373
15376
|
this.concurrency = (_b = config.concurrency) !== null && _b !== void 0 ? _b : DEFAULT_CONCURRENCY;
|
|
15374
15377
|
this.recordLoader = config.recordLoader;
|
|
15375
15378
|
this.recordIngestor = config.recordIngestor;
|
|
15376
15379
|
this.objectInfoLoader = config.objectInfoLoader;
|
|
15377
15380
|
this.networkWorkerPool = new AsyncWorkerPool(this.concurrency);
|
|
15381
|
+
this.useBatchGQL = ldsPrimingGraphqlBatch.isOpen({ fallback: false });
|
|
15378
15382
|
}
|
|
15379
15383
|
// function that enqueues priming work
|
|
15380
15384
|
async enqueue(work) {
|
|
@@ -15409,91 +15413,129 @@ class PrimingSession extends EventEmitter {
|
|
|
15409
15413
|
}
|
|
15410
15414
|
// parallelizes batches of priming work
|
|
15411
15415
|
enqueueBatches(batches) {
|
|
15412
|
-
|
|
15413
|
-
const
|
|
15414
|
-
|
|
15415
|
-
|
|
15416
|
-
|
|
15417
|
-
|
|
15418
|
-
|
|
15419
|
-
.
|
|
15420
|
-
|
|
15421
|
-
|
|
15422
|
-
|
|
15423
|
-
|
|
15424
|
-
|
|
15416
|
+
if (this.useBatchGQL === false) {
|
|
15417
|
+
for (const batch of batches) {
|
|
15418
|
+
const queuedTime = Date.now();
|
|
15419
|
+
this.networkWorkerPool.push({
|
|
15420
|
+
workFn: (abortController) => {
|
|
15421
|
+
const workTime = Date.now();
|
|
15422
|
+
this.emit('batch-starting', { queuedTime: workTime - queuedTime });
|
|
15423
|
+
return this.recordLoader
|
|
15424
|
+
.fetchRecordData(batch, abortController)
|
|
15425
|
+
.then(async (result) => {
|
|
15426
|
+
this.emit('batch-fetched', {
|
|
15427
|
+
ids: batch.ids,
|
|
15428
|
+
duration: Date.now() - workTime,
|
|
15429
|
+
});
|
|
15430
|
+
this.processFetchedRecords(result, abortController);
|
|
15431
|
+
});
|
|
15432
|
+
},
|
|
15433
|
+
cancelFn: () => {
|
|
15434
|
+
this.emit('error', {
|
|
15425
15435
|
ids: batch.ids,
|
|
15426
|
-
|
|
15436
|
+
code: 'canceled',
|
|
15437
|
+
message: `batch canceled`,
|
|
15427
15438
|
});
|
|
15428
|
-
|
|
15429
|
-
|
|
15430
|
-
|
|
15431
|
-
|
|
15432
|
-
|
|
15433
|
-
|
|
15434
|
-
|
|
15435
|
-
|
|
15436
|
-
|
|
15437
|
-
|
|
15438
|
-
const
|
|
15439
|
-
|
|
15440
|
-
|
|
15441
|
-
|
|
15442
|
-
|
|
15443
|
-
message: `could not find records: ${missingIds.join(', ')}`,
|
|
15444
|
-
});
|
|
15445
|
-
}
|
|
15446
|
-
const { records } = result;
|
|
15447
|
-
const beforeWrite = Date.now();
|
|
15448
|
-
// dispatch the write but DO NOT wait on it to unblock the network pool
|
|
15449
|
-
this.recordIngestor
|
|
15450
|
-
.insertRecords(records)
|
|
15451
|
-
.then(({ written, conflicted, errors }) => {
|
|
15452
|
-
this.emit('batch-written', {
|
|
15453
|
-
written,
|
|
15454
|
-
conflicted,
|
|
15455
|
-
errors: errors
|
|
15456
|
-
.map((e) => e.ids)
|
|
15457
|
-
.reduce((a, b) => a.concat(b), []),
|
|
15458
|
-
duration: Date.now() - beforeWrite,
|
|
15459
|
-
});
|
|
15439
|
+
},
|
|
15440
|
+
});
|
|
15441
|
+
}
|
|
15442
|
+
}
|
|
15443
|
+
else {
|
|
15444
|
+
const chucks = chunk(batches, DEFAULT_GQL_QUERY_BATCH_SIZE);
|
|
15445
|
+
for (const batchChuck of chucks) {
|
|
15446
|
+
const queuedTime = Date.now();
|
|
15447
|
+
this.networkWorkerPool.push({
|
|
15448
|
+
workFn: (abortController) => {
|
|
15449
|
+
const workTime = Date.now();
|
|
15450
|
+
this.emit('batch-starting', { queuedTime: workTime - queuedTime });
|
|
15451
|
+
return this.recordLoader
|
|
15452
|
+
.batchFetchRecordData(batchChuck, abortController)
|
|
15453
|
+
.then(async (results) => {
|
|
15460
15454
|
if (abortController.aborted) {
|
|
15461
15455
|
return;
|
|
15462
15456
|
}
|
|
15463
|
-
|
|
15464
|
-
|
|
15465
|
-
|
|
15466
|
-
|
|
15467
|
-
|
|
15468
|
-
|
|
15469
|
-
});
|
|
15457
|
+
const duration = Date.now() - workTime;
|
|
15458
|
+
// For each query within the Batch gql query, result returns at the same time
|
|
15459
|
+
for (let i = 0; i < results.length; i++) {
|
|
15460
|
+
this.emit('batch-fetched', {
|
|
15461
|
+
ids: batchChuck[i].ids,
|
|
15462
|
+
duration,
|
|
15470
15463
|
});
|
|
15471
15464
|
}
|
|
15472
|
-
|
|
15473
|
-
|
|
15474
|
-
this.emit('primed', Array.from(written));
|
|
15475
|
-
}
|
|
15476
|
-
// TODO [W-12436213]: implement conflict resolution
|
|
15477
|
-
if (conflicted.length > 0) {
|
|
15478
|
-
// for now emit conlicts as errors
|
|
15479
|
-
this.emit('error', {
|
|
15480
|
-
ids: Array.from(conflicted),
|
|
15481
|
-
code: 'unknown',
|
|
15482
|
-
message: 'conflict when persisting record',
|
|
15483
|
-
});
|
|
15465
|
+
for (let i = 0; i < results.length; i++) {
|
|
15466
|
+
this.processFetchedRecords(results[i], abortController);
|
|
15484
15467
|
}
|
|
15485
15468
|
});
|
|
15486
|
-
}
|
|
15487
|
-
|
|
15488
|
-
|
|
15489
|
-
|
|
15490
|
-
|
|
15491
|
-
|
|
15492
|
-
|
|
15493
|
-
|
|
15494
|
-
|
|
15469
|
+
},
|
|
15470
|
+
cancelFn: () => {
|
|
15471
|
+
const chuckIds = batchChuck
|
|
15472
|
+
.map((batch) => batch.ids)
|
|
15473
|
+
.reduce((prev, curr) => prev.concat(curr), []);
|
|
15474
|
+
this.emit('error', {
|
|
15475
|
+
ids: chuckIds,
|
|
15476
|
+
code: 'canceled',
|
|
15477
|
+
message: `batch canceled`,
|
|
15478
|
+
});
|
|
15479
|
+
},
|
|
15480
|
+
});
|
|
15481
|
+
}
|
|
15482
|
+
}
|
|
15483
|
+
}
|
|
15484
|
+
processFetchedRecords(result, abortController) {
|
|
15485
|
+
if (result.ok === false) {
|
|
15486
|
+
const { error } = result;
|
|
15487
|
+
const primingError = error === 'network-error' ? 'service-unavailable' : 'unknown';
|
|
15488
|
+
this.emit('error', {
|
|
15489
|
+
ids: result.missingIds,
|
|
15490
|
+
code: primingError,
|
|
15491
|
+
message: `${result.messages.join(',')}`,
|
|
15492
|
+
});
|
|
15493
|
+
return;
|
|
15494
|
+
}
|
|
15495
|
+
const { missingIds } = result;
|
|
15496
|
+
if (missingIds.length > 0) {
|
|
15497
|
+
this.emit('error', {
|
|
15498
|
+
ids: missingIds,
|
|
15499
|
+
code: 'not-found',
|
|
15500
|
+
message: `could not find records: ${missingIds.join(', ')}`,
|
|
15495
15501
|
});
|
|
15496
15502
|
}
|
|
15503
|
+
const { records } = result;
|
|
15504
|
+
const beforeWrite = Date.now();
|
|
15505
|
+
// dispatch the write but DO NOT wait on it to unblock the network pool
|
|
15506
|
+
this.recordIngestor.insertRecords(records).then(({ written, conflicted, errors }) => {
|
|
15507
|
+
this.emit('batch-written', {
|
|
15508
|
+
written,
|
|
15509
|
+
conflicted,
|
|
15510
|
+
errors: errors.map((e) => e.ids).reduce((a, b) => a.concat(b), []),
|
|
15511
|
+
duration: Date.now() - beforeWrite,
|
|
15512
|
+
});
|
|
15513
|
+
if (abortController.aborted) {
|
|
15514
|
+
return;
|
|
15515
|
+
}
|
|
15516
|
+
if (errors.length > 0) {
|
|
15517
|
+
errors.forEach(({ ids, message }) => {
|
|
15518
|
+
this.emit('error', {
|
|
15519
|
+
ids,
|
|
15520
|
+
code: 'unknown',
|
|
15521
|
+
message: message,
|
|
15522
|
+
});
|
|
15523
|
+
});
|
|
15524
|
+
}
|
|
15525
|
+
// now that the records are persisted, emit the primed event
|
|
15526
|
+
if (written.length > 0) {
|
|
15527
|
+
this.emit('primed', Array.from(written));
|
|
15528
|
+
}
|
|
15529
|
+
// TODO [W-12436213]: implement conflict resolution
|
|
15530
|
+
if (conflicted.length > 0) {
|
|
15531
|
+
// for now emit conlicts as errors
|
|
15532
|
+
this.emit('error', {
|
|
15533
|
+
ids: Array.from(conflicted),
|
|
15534
|
+
code: 'unknown',
|
|
15535
|
+
message: 'conflict when persisting record',
|
|
15536
|
+
});
|
|
15537
|
+
}
|
|
15538
|
+
});
|
|
15497
15539
|
}
|
|
15498
15540
|
async fetchMetadata(batches) {
|
|
15499
15541
|
const apiNames = Array.from(batches.reduce((acc, x) => {
|
|
@@ -15546,12 +15588,39 @@ class RecordLoaderGraphQL {
|
|
|
15546
15588
|
missingIds: batch.ids,
|
|
15547
15589
|
};
|
|
15548
15590
|
}
|
|
15549
|
-
|
|
15591
|
+
return this.generateFetchResult(rep, batch);
|
|
15592
|
+
}
|
|
15593
|
+
async batchFetchRecordData(batchs, abortController) {
|
|
15594
|
+
let reps;
|
|
15595
|
+
try {
|
|
15596
|
+
reps = await this.callBatchGraphQL(batchs, abortController);
|
|
15597
|
+
}
|
|
15598
|
+
catch (e) {
|
|
15599
|
+
const missingIds = batchs
|
|
15600
|
+
.map((batch) => batch.ids)
|
|
15601
|
+
.reduce((prev, curr) => prev.concat(curr), []);
|
|
15602
|
+
return [
|
|
15603
|
+
{
|
|
15604
|
+
ok: false,
|
|
15605
|
+
error: 'network-error',
|
|
15606
|
+
messages: ['Network Error'],
|
|
15607
|
+
missingIds,
|
|
15608
|
+
},
|
|
15609
|
+
];
|
|
15610
|
+
}
|
|
15611
|
+
const recordFetchResults = [];
|
|
15612
|
+
for (let i = 0; i < reps.length; i++) {
|
|
15613
|
+
recordFetchResults.push(this.generateFetchResult(reps[i], batchs[i]));
|
|
15614
|
+
}
|
|
15615
|
+
return recordFetchResults;
|
|
15616
|
+
}
|
|
15617
|
+
generateFetchResult(repResult, batchInput) {
|
|
15618
|
+
const { data, errors } = repResult;
|
|
15550
15619
|
if (errors !== undefined && errors.length > 0) {
|
|
15551
15620
|
// right now if there are any errors in the batch we throw out the entire batch
|
|
15552
15621
|
// for now this is ok all errors will originate on the same node so there shouldn't be a mix of errors and data
|
|
15553
15622
|
return {
|
|
15554
|
-
missingIds:
|
|
15623
|
+
missingIds: batchInput.ids,
|
|
15555
15624
|
ok: false,
|
|
15556
15625
|
error: 'request-error',
|
|
15557
15626
|
messages: errors.map((x) => x.message),
|
|
@@ -15563,11 +15632,11 @@ class RecordLoaderGraphQL {
|
|
|
15563
15632
|
ok: false,
|
|
15564
15633
|
error: 'unknown',
|
|
15565
15634
|
messages: ['unexpected response retrieved from graphql endpoint'],
|
|
15566
|
-
missingIds:
|
|
15635
|
+
missingIds: batchInput.ids,
|
|
15567
15636
|
};
|
|
15568
15637
|
}
|
|
15569
|
-
const seenRecords = new Set(
|
|
15570
|
-
const records = data.uiapi.query[
|
|
15638
|
+
const seenRecords = new Set(batchInput.ids);
|
|
15639
|
+
const records = data.uiapi.query[batchInput.type].edges.map((edge) => {
|
|
15571
15640
|
const record = this.generateDurableRecordRepresentation(edge.node);
|
|
15572
15641
|
seenRecords.delete(record.id);
|
|
15573
15642
|
return record;
|
|
@@ -15582,6 +15651,15 @@ class RecordLoaderGraphQL {
|
|
|
15582
15651
|
const query = this.generateGraphQLQuery(batch.type, batch.fields);
|
|
15583
15652
|
return this.networkAdapter.postGraphQL(query, { ids: batch.ids, first: batch.ids.length }, abortController);
|
|
15584
15653
|
}
|
|
15654
|
+
callBatchGraphQL(batches, abortController) {
|
|
15655
|
+
const gqlInput = batches.map((batch) => {
|
|
15656
|
+
return {
|
|
15657
|
+
query: this.generateGraphQLQuery(batch.type, batch.fields),
|
|
15658
|
+
variables: { ids: batch.ids, first: batch.ids.length },
|
|
15659
|
+
};
|
|
15660
|
+
});
|
|
15661
|
+
return this.networkAdapter.postBatchGraphQL(gqlInput, abortController);
|
|
15662
|
+
}
|
|
15585
15663
|
generateGraphQLQuery(type, fields) {
|
|
15586
15664
|
const fieldList = Object.keys(requiredFieldMap)
|
|
15587
15665
|
.map((field) => {
|
|
@@ -15705,7 +15783,56 @@ function instrumentPrimingSession(session) {
|
|
|
15705
15783
|
/* global __nimbus */
|
|
15706
15784
|
// note this is automatically incremented by scripts/release/bump-api-version.js at each release
|
|
15707
15785
|
const apiVersion = `v59.0`;
|
|
15786
|
+
const batchEndPointPath = `/services/data/${apiVersion}/graphql/batch`;
|
|
15787
|
+
const endPointPath = `/services/data/${apiVersion}/graphql`;
|
|
15708
15788
|
class NimbusPrimingNetworkAdapter {
|
|
15789
|
+
postBatchGraphQL(configs, abortController) {
|
|
15790
|
+
return new Promise((resolve, reject) => {
|
|
15791
|
+
let listener;
|
|
15792
|
+
const unregisterListener = () => {
|
|
15793
|
+
if (listener) {
|
|
15794
|
+
abortController.removeEventListener(listener);
|
|
15795
|
+
}
|
|
15796
|
+
};
|
|
15797
|
+
__nimbus.plugins.LdsNetworkAdapter
|
|
15798
|
+
.sendRequest({
|
|
15799
|
+
method: 'POST',
|
|
15800
|
+
path: batchEndPointPath,
|
|
15801
|
+
body: JSON.stringify({
|
|
15802
|
+
batchQuery: configs,
|
|
15803
|
+
}),
|
|
15804
|
+
headers: {},
|
|
15805
|
+
queryParams: {},
|
|
15806
|
+
priority: 'background',
|
|
15807
|
+
observabilityContext: {},
|
|
15808
|
+
}, (response) => {
|
|
15809
|
+
unregisterListener();
|
|
15810
|
+
const { body } = response;
|
|
15811
|
+
if (body) {
|
|
15812
|
+
const { results } = JSON.parse(body);
|
|
15813
|
+
if (results) {
|
|
15814
|
+
const gqlResults = results.map((compositeGqlResult) => compositeGqlResult.result);
|
|
15815
|
+
resolve(gqlResults);
|
|
15816
|
+
}
|
|
15817
|
+
else {
|
|
15818
|
+
reject(new Error(`No body returned from ${batchEndPointPath} endpoint`));
|
|
15819
|
+
}
|
|
15820
|
+
}
|
|
15821
|
+
else {
|
|
15822
|
+
reject(new Error(`No body returned from ${batchEndPointPath} endpoint`));
|
|
15823
|
+
}
|
|
15824
|
+
}, (error) => {
|
|
15825
|
+
unregisterListener();
|
|
15826
|
+
reject(error);
|
|
15827
|
+
})
|
|
15828
|
+
.then((cancellationToken) => {
|
|
15829
|
+
listener = () => {
|
|
15830
|
+
__nimbus.plugins.LdsNetworkAdapter.cancelRequest(cancellationToken);
|
|
15831
|
+
};
|
|
15832
|
+
abortController.addEventListener(listener);
|
|
15833
|
+
});
|
|
15834
|
+
});
|
|
15835
|
+
}
|
|
15709
15836
|
postGraphQL(query, variables, abortController) {
|
|
15710
15837
|
return new Promise((resolve, reject) => {
|
|
15711
15838
|
let listener;
|
|
@@ -15717,7 +15844,7 @@ class NimbusPrimingNetworkAdapter {
|
|
|
15717
15844
|
__nimbus.plugins.LdsNetworkAdapter
|
|
15718
15845
|
.sendRequest({
|
|
15719
15846
|
method: 'POST',
|
|
15720
|
-
path:
|
|
15847
|
+
path: endPointPath,
|
|
15721
15848
|
body: JSON.stringify({
|
|
15722
15849
|
query,
|
|
15723
15850
|
variables,
|
|
@@ -15733,7 +15860,7 @@ class NimbusPrimingNetworkAdapter {
|
|
|
15733
15860
|
resolve(JSON.parse(body));
|
|
15734
15861
|
}
|
|
15735
15862
|
else {
|
|
15736
|
-
reject(new Error(
|
|
15863
|
+
reject(new Error(`No body returned from ${endPointPath} endpoint`));
|
|
15737
15864
|
}
|
|
15738
15865
|
}, (error) => {
|
|
15739
15866
|
unregisterListener();
|
|
@@ -15984,4 +16111,4 @@ register({
|
|
|
15984
16111
|
});
|
|
15985
16112
|
|
|
15986
16113
|
export { getRuntime, registerReportObserver, reportGraphqlQueryParseError };
|
|
15987
|
-
// version: 1.
|
|
16114
|
+
// version: 1.157.1-a0df85006
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { PrimingNetworkAdapter } from '@salesforce/lds-priming';
|
|
2
|
-
import type { GraphQLRepresentation } from '@salesforce/lds-adapters-uiapi';
|
|
2
|
+
import type { GraphQLRepresentation, GraphQLInputRepresentation } from '@salesforce/lds-adapters-uiapi';
|
|
3
3
|
import type { LdsAbortController } from '@salesforce/lds-utils-adapters';
|
|
4
4
|
export declare class NimbusPrimingNetworkAdapter implements PrimingNetworkAdapter {
|
|
5
|
+
postBatchGraphQL(configs: GraphQLInputRepresentation[], abortController: LdsAbortController): Promise<GraphQLRepresentation[]>;
|
|
5
6
|
postGraphQL(query: string, variables: Record<string, any>, abortController: LdsAbortController): Promise<GraphQLRepresentation>;
|
|
6
7
|
}
|