@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 'UrlValue';
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
- for (const batch of batches) {
15413
- const queuedTime = Date.now();
15414
- this.networkWorkerPool.push({
15415
- workFn: (abortController) => {
15416
- const workTime = Date.now();
15417
- this.emit('batch-starting', { queuedTime: workTime - queuedTime });
15418
- return this.recordLoader
15419
- .fetchRecordData(batch, abortController)
15420
- .then(async (result) => {
15421
- if (abortController.aborted) {
15422
- return;
15423
- }
15424
- this.emit('batch-fetched', {
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
- duration: Date.now() - workTime,
15436
+ code: 'canceled',
15437
+ message: `batch canceled`,
15427
15438
  });
15428
- if (result.ok === false) {
15429
- const { error } = result;
15430
- const primingError = error === 'network-error' ? 'service-unavailable' : 'unknown';
15431
- this.emit('error', {
15432
- ids: result.missingIds,
15433
- code: primingError,
15434
- message: `${result.messages.join(',')}`,
15435
- });
15436
- return;
15437
- }
15438
- const { missingIds } = result;
15439
- if (missingIds.length > 0) {
15440
- this.emit('error', {
15441
- ids: missingIds,
15442
- code: 'not-found',
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
- if (errors.length > 0) {
15464
- errors.forEach(({ ids, message }) => {
15465
- this.emit('error', {
15466
- ids,
15467
- code: 'unknown',
15468
- message: message,
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
- // now that the records are persisted, emit the primed event
15473
- if (written.length > 0) {
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
- cancelFn: () => {
15489
- this.emit('error', {
15490
- ids: batch.ids,
15491
- code: 'canceled',
15492
- message: `batch canceled`,
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
- const { data, errors } = rep;
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: batch.ids,
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: batch.ids,
15635
+ missingIds: batchInput.ids,
15567
15636
  };
15568
15637
  }
15569
- const seenRecords = new Set(batch.ids);
15570
- const records = data.uiapi.query[batch.type].edges.map((edge) => {
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: `/services/data/${apiVersion}/graphql`,
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('No body returned from graphql endpoint'));
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.156.1-7842b5df6
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.156.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": "280 kB",
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": "280 kB",
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 'UrlValue';
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
- for (const batch of batches) {
15413
- const queuedTime = Date.now();
15414
- this.networkWorkerPool.push({
15415
- workFn: (abortController) => {
15416
- const workTime = Date.now();
15417
- this.emit('batch-starting', { queuedTime: workTime - queuedTime });
15418
- return this.recordLoader
15419
- .fetchRecordData(batch, abortController)
15420
- .then(async (result) => {
15421
- if (abortController.aborted) {
15422
- return;
15423
- }
15424
- this.emit('batch-fetched', {
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
- duration: Date.now() - workTime,
15436
+ code: 'canceled',
15437
+ message: `batch canceled`,
15427
15438
  });
15428
- if (result.ok === false) {
15429
- const { error } = result;
15430
- const primingError = error === 'network-error' ? 'service-unavailable' : 'unknown';
15431
- this.emit('error', {
15432
- ids: result.missingIds,
15433
- code: primingError,
15434
- message: `${result.messages.join(',')}`,
15435
- });
15436
- return;
15437
- }
15438
- const { missingIds } = result;
15439
- if (missingIds.length > 0) {
15440
- this.emit('error', {
15441
- ids: missingIds,
15442
- code: 'not-found',
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
- if (errors.length > 0) {
15464
- errors.forEach(({ ids, message }) => {
15465
- this.emit('error', {
15466
- ids,
15467
- code: 'unknown',
15468
- message: message,
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
- // now that the records are persisted, emit the primed event
15473
- if (written.length > 0) {
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
- cancelFn: () => {
15489
- this.emit('error', {
15490
- ids: batch.ids,
15491
- code: 'canceled',
15492
- message: `batch canceled`,
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
- const { data, errors } = rep;
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: batch.ids,
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: batch.ids,
15635
+ missingIds: batchInput.ids,
15567
15636
  };
15568
15637
  }
15569
- const seenRecords = new Set(batch.ids);
15570
- const records = data.uiapi.query[batch.type].edges.map((edge) => {
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: `/services/data/${apiVersion}/graphql`,
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('No body returned from graphql endpoint'));
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.156.1-7842b5df6
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
  }