@salesforce/lds-network-nimbus 1.124.2 → 1.124.3

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
@@ -7,776 +7,776 @@
7
7
  import { idleDetector } from 'o11y/client';
8
8
  import { HttpStatusCode } from '@luvio/engine';
9
9
 
10
- const { keys, create, assign, entries } = Object;
11
- const { stringify, parse } = JSON;
12
- const { push, join, slice } = Array.prototype;
10
+ const { keys, create, assign, entries } = Object;
11
+ const { stringify, parse } = JSON;
12
+ const { push, join, slice } = Array.prototype;
13
13
  const { isArray, from } = Array;
14
14
 
15
- function ldsParamsToString(params) {
16
- const returnParams = create(null);
17
- const keys$1 = keys(params);
18
- for (let i = 0, len = keys$1.length; i < len; i++) {
19
- const key = keys$1[i];
20
- const value = params[key];
21
- if (value === undefined) {
22
- // filter out params that have no value
23
- continue;
24
- }
25
- if (isArray(value)) {
26
- // filter out empty arrays
27
- if (value.length > 0) {
28
- returnParams[key] = value.join(',');
29
- }
30
- }
31
- else {
32
- returnParams[key] = `${value}`;
33
- }
34
- if (isObject(value) === true && keys(value).length > 0) {
35
- returnParams[key] = stringify(value);
36
- }
37
- }
38
- return returnParams;
39
- }
40
- function statusTextFromStatusCode(status) {
41
- switch (status) {
42
- case HttpStatusCode.Ok:
43
- return 'OK';
44
- case HttpStatusCode.NotModified:
45
- return 'Not Modified';
46
- case HttpStatusCode.NotFound:
47
- return 'Not Found';
48
- case HttpStatusCode.BadRequest:
49
- return 'Bad Request';
50
- case HttpStatusCode.ServerError:
51
- return 'Server Error';
52
- default:
53
- return `Unexpected HTTP Status Code: ${status}`;
54
- }
55
- }
56
- function methodFromResourceRequestMethod(method) {
57
- switch (method.toLowerCase()) {
58
- case 'get':
59
- return 'GET';
60
- case 'put':
61
- return 'PUT';
62
- case 'post':
63
- return 'POST';
64
- case 'delete':
65
- return 'DELETE';
66
- case 'patch':
67
- return 'PATCH';
68
- default:
69
- throw Error(`Unexpected method ${method}`);
70
- }
71
- }
72
- function priorityFromResourceRequest(request) {
73
- switch (request.priority) {
74
- case 'background':
75
- return 'background';
76
- case 'high':
77
- return 'high';
78
- case 'normal':
79
- default:
80
- return 'normal';
81
- }
82
- }
83
- function isStatusOk(status) {
84
- return status >= 200 && status <= 299;
85
- }
86
- // adapted from adapter-utils untrustedIsObject
87
- function isObject(value) {
88
- return typeof value === 'object' && value !== null && isArray(value) === false;
89
- }
90
- function stringifyIfPresent(value) {
91
- if (value === undefined || value === null) {
92
- return null;
93
- }
94
- return stringify(value);
95
- }
96
- function parseIfPresent(value) {
97
- if (value === undefined || value === null || value === '') {
98
- return null;
99
- }
100
- return parse(value);
101
- }
102
- function buildNimbusNetworkPluginRequest(resourceRequest, resourceRequestContext) {
103
- const { basePath, baseUri, method, headers, queryParams, body } = resourceRequest;
104
- let observabilityContext = null;
105
- if (resourceRequestContext !== undefined &&
106
- resourceRequestContext.requestCorrelator !== undefined &&
107
- resourceRequestContext.requestCorrelator.observabilityContext !==
108
- undefined) {
109
- ({ observabilityContext = null } =
110
- resourceRequestContext.requestCorrelator);
111
- }
112
- return {
113
- method: methodFromResourceRequestMethod(method),
114
- body: stringifyIfPresent(body),
115
- headers,
116
- queryParams: ldsParamsToString(queryParams),
117
- path: `${baseUri}${basePath}`,
118
- priority: priorityFromResourceRequest(resourceRequest),
119
- observabilityContext,
120
- };
121
- }
122
- function buildLdsResponse(response) {
123
- const { body: responseBody, headers, status } = response;
124
- const statusText = statusTextFromStatusCode(status);
125
- return {
126
- statusText,
127
- status,
128
- body: parseIfPresent(responseBody),
129
- headers,
130
- ok: isStatusOk(status),
131
- };
15
+ function ldsParamsToString(params) {
16
+ const returnParams = create(null);
17
+ const keys$1 = keys(params);
18
+ for (let i = 0, len = keys$1.length; i < len; i++) {
19
+ const key = keys$1[i];
20
+ const value = params[key];
21
+ if (value === undefined) {
22
+ // filter out params that have no value
23
+ continue;
24
+ }
25
+ if (isArray(value)) {
26
+ // filter out empty arrays
27
+ if (value.length > 0) {
28
+ returnParams[key] = value.join(',');
29
+ }
30
+ }
31
+ else {
32
+ returnParams[key] = `${value}`;
33
+ }
34
+ if (isObject(value) === true && keys(value).length > 0) {
35
+ returnParams[key] = stringify(value);
36
+ }
37
+ }
38
+ return returnParams;
39
+ }
40
+ function statusTextFromStatusCode(status) {
41
+ switch (status) {
42
+ case HttpStatusCode.Ok:
43
+ return 'OK';
44
+ case HttpStatusCode.NotModified:
45
+ return 'Not Modified';
46
+ case HttpStatusCode.NotFound:
47
+ return 'Not Found';
48
+ case HttpStatusCode.BadRequest:
49
+ return 'Bad Request';
50
+ case HttpStatusCode.ServerError:
51
+ return 'Server Error';
52
+ default:
53
+ return `Unexpected HTTP Status Code: ${status}`;
54
+ }
55
+ }
56
+ function methodFromResourceRequestMethod(method) {
57
+ switch (method.toLowerCase()) {
58
+ case 'get':
59
+ return 'GET';
60
+ case 'put':
61
+ return 'PUT';
62
+ case 'post':
63
+ return 'POST';
64
+ case 'delete':
65
+ return 'DELETE';
66
+ case 'patch':
67
+ return 'PATCH';
68
+ default:
69
+ throw Error(`Unexpected method ${method}`);
70
+ }
71
+ }
72
+ function priorityFromResourceRequest(request) {
73
+ switch (request.priority) {
74
+ case 'background':
75
+ return 'background';
76
+ case 'high':
77
+ return 'high';
78
+ case 'normal':
79
+ default:
80
+ return 'normal';
81
+ }
82
+ }
83
+ function isStatusOk(status) {
84
+ return status >= 200 && status <= 299;
85
+ }
86
+ // adapted from adapter-utils untrustedIsObject
87
+ function isObject(value) {
88
+ return typeof value === 'object' && value !== null && isArray(value) === false;
89
+ }
90
+ function stringifyIfPresent(value) {
91
+ if (value === undefined || value === null) {
92
+ return null;
93
+ }
94
+ return stringify(value);
95
+ }
96
+ function parseIfPresent(value) {
97
+ if (value === undefined || value === null || value === '') {
98
+ return null;
99
+ }
100
+ return parse(value);
101
+ }
102
+ function buildNimbusNetworkPluginRequest(resourceRequest, resourceRequestContext) {
103
+ const { basePath, baseUri, method, headers, queryParams, body } = resourceRequest;
104
+ let observabilityContext = null;
105
+ if (resourceRequestContext !== undefined &&
106
+ resourceRequestContext.requestCorrelator !== undefined &&
107
+ resourceRequestContext.requestCorrelator.observabilityContext !==
108
+ undefined) {
109
+ ({ observabilityContext = null } =
110
+ resourceRequestContext.requestCorrelator);
111
+ }
112
+ return {
113
+ method: methodFromResourceRequestMethod(method),
114
+ body: stringifyIfPresent(body),
115
+ headers,
116
+ queryParams: ldsParamsToString(queryParams),
117
+ path: `${baseUri}${basePath}`,
118
+ priority: priorityFromResourceRequest(resourceRequest),
119
+ observabilityContext,
120
+ };
121
+ }
122
+ function buildLdsResponse(response) {
123
+ const { body: responseBody, headers, status } = response;
124
+ const statusText = statusTextFromStatusCode(status);
125
+ return {
126
+ statusText,
127
+ status,
128
+ body: parseIfPresent(responseBody),
129
+ headers,
130
+ ok: isStatusOk(status),
131
+ };
132
132
  }
133
133
 
134
- // so eslint doesn't complain about nimbus
135
- const tasker = idleDetector.declareNotifierTaskMulti('NimbusNetworkAdapter');
136
- const NimbusNetworkAdapter = (request, resourceRequestContext) => {
137
- tasker.add();
138
- return new Promise((resolve, reject) => {
139
- try {
140
- __nimbus.plugins.LdsNetworkAdapter.sendRequest(buildNimbusNetworkPluginRequest(request, resourceRequestContext), (response) => {
141
- try {
142
- resolve(buildLdsResponse(response));
143
- }
144
- catch (error) {
145
- // don't leave promise hanging, catch any errors (eg: if native side
146
- // returns malformed response) and call reject
147
- reject(error);
148
- }
149
- }, (error) => {
150
- reject(new Error(`type: ${error.type}, message: ${error.message}`));
151
- });
152
- }
153
- catch (error) {
154
- // don't leave promise hanging, catch any errors (eg: if native side
155
- // fails to parse the request), and call reject
156
- reject(error);
157
- }
158
- }).finally(() => tasker.done());
134
+ // so eslint doesn't complain about nimbus
135
+ const tasker = idleDetector.declareNotifierTaskMulti('NimbusNetworkAdapter');
136
+ const NimbusNetworkAdapter = (request, resourceRequestContext) => {
137
+ tasker.add();
138
+ return new Promise((resolve, reject) => {
139
+ try {
140
+ __nimbus.plugins.LdsNetworkAdapter.sendRequest(buildNimbusNetworkPluginRequest(request, resourceRequestContext), (response) => {
141
+ try {
142
+ resolve(buildLdsResponse(response));
143
+ }
144
+ catch (error) {
145
+ // don't leave promise hanging, catch any errors (eg: if native side
146
+ // returns malformed response) and call reject
147
+ reject(error);
148
+ }
149
+ }, (error) => {
150
+ reject(new Error(`type: ${error.type}, message: ${error.message}`));
151
+ });
152
+ }
153
+ catch (error) {
154
+ // don't leave promise hanging, catch any errors (eg: if native side
155
+ // fails to parse the request), and call reject
156
+ reject(error);
157
+ }
158
+ }).finally(() => tasker.done());
159
159
  };
160
160
 
161
- /**
162
- * related-list-records/batch fields and optional fields could be like below.
163
- * /ui-api/related-list-records/batch/001R0000006l1xKIAQ/Contacts,Opportunities?
164
- * optionalFields=Contacts:Contact.Id,Contact.Name;Opportunities:Opportunity.Id
165
- *
166
- * the pattern is [{relativeListId}:{[fields]};{relativeListId}:{[fields]}]
167
- */
168
- const SEPARATOR_BETWEEN_SCOPES = ';';
169
- const SEPARATOR_BETWEEN_SCOPE_AND_FIELDS = ':';
170
- const SEPARATOR_BETWEEN_FIELDS = ',';
171
- const UNSCOPED_IDENTIFIER = 'unscoped';
172
- class ScopedFields {
173
- constructor(scope, fields) {
174
- this.fields = {};
175
- this.scope = scope;
176
- for (let i = 0, len = fields.length; i < len; i += 1) {
177
- this.fields[fields[i]] = true;
178
- }
179
- }
180
- isUnScoped() {
181
- return this.scope === UNSCOPED_IDENTIFIER;
182
- }
183
- addField(field) {
184
- this.fields[field] = true;
185
- }
186
- addFields(fields) {
187
- fields.forEach(this.addField, this);
188
- }
189
- toQueryParameterValue() {
190
- const joinedFields = join.call(Object.keys(this.fields), SEPARATOR_BETWEEN_FIELDS);
191
- return this.isUnScoped()
192
- ? joinedFields
193
- : join.call([this.scope, joinedFields], SEPARATOR_BETWEEN_SCOPE_AND_FIELDS);
194
- }
195
- toQueryParams() {
196
- return this.isUnScoped() ? Object.keys(this.fields) : this.toQueryParameterValue();
197
- }
198
- /**
199
- * parse Contacts:Contact.Id,Contact.Name into a QueryFields
200
- */
201
- static fromQueryParameterValue(paramValue) {
202
- if (paramValue === null || paramValue === '')
203
- return;
204
- const scopeToFields = paramValue.split(SEPARATOR_BETWEEN_SCOPE_AND_FIELDS);
205
- if (scopeToFields.length === 1) {
206
- //unscoped
207
- return new ScopedFields(UNSCOPED_IDENTIFIER, scopeToFields[0].split(SEPARATOR_BETWEEN_FIELDS));
208
- }
209
- else if (scopeToFields.length === 2) {
210
- const scope = scopeToFields[0];
211
- const fields = scopeToFields[1];
212
- if (scope === undefined || fields === null)
213
- return;
214
- return new ScopedFields(scope, fields.split(SEPARATOR_BETWEEN_FIELDS));
215
- }
216
- else {
217
- return;
218
- }
219
- }
220
- }
221
- class ScopedFieldsCollection {
222
- constructor() {
223
- this.listIdToFieldsMap = {};
224
- }
225
- /**
226
- * merge the from ScopedFieldsCollection into current one
227
- * @param from
228
- */
229
- merge(from) {
230
- const { listIdToFieldsMap } = from;
231
- const scopes = Object.keys(listIdToFieldsMap);
232
- for (let i = 0, len = scopes.length; i < len; i += 1) {
233
- const scope = scopes[i];
234
- const scopedFields = listIdToFieldsMap[scope];
235
- const existingScopedFields = this.listIdToFieldsMap[scope];
236
- if (existingScopedFields) {
237
- existingScopedFields.addFields(Object.keys(scopedFields.fields));
238
- }
239
- else {
240
- this.listIdToFieldsMap[scope] = scopedFields;
241
- }
242
- }
243
- }
244
- toQueryParams() {
245
- return this.countOfUnScoped() > 0
246
- ? this.listIdToFieldsMap[UNSCOPED_IDENTIFIER].toQueryParams()
247
- : this.toQueryParameterValue();
248
- }
249
- /**
250
- * convert to query parameter value
251
- * @returns
252
- */
253
- toQueryParameterValue() {
254
- let result = [];
255
- const scopes = Object.keys(this.listIdToFieldsMap);
256
- for (let i = 0, len = scopes.length; i < len; i += 1) {
257
- const chunk = this.listIdToFieldsMap[scopes[i]].toQueryParameterValue();
258
- if (chunk !== undefined)
259
- result.push(chunk);
260
- }
261
- return join.call(result, SEPARATOR_BETWEEN_SCOPES);
262
- }
263
- /**
264
- * split the ScopedFields into multiple ScopedFields
265
- * which there max fields list length will not exceeded the specified maxLength
266
- * @param maxLength
267
- */
268
- split(maxLength = MAX_STRING_LENGTH_PER_CHUNK) {
269
- const size = this.size();
270
- if (size > maxLength) {
271
- const fieldsArray = [];
272
- const scopes = Object.keys(this.listIdToFieldsMap);
273
- for (let i = 0, len = scopes.length; i < len; i += 1) {
274
- const { scope, fields } = this.listIdToFieldsMap[scopes[i]];
275
- Object.keys(fields).forEach((field) => {
276
- fieldsArray.push([scope, field]);
277
- });
278
- }
279
- // Formula: # of fields per chunk = floor( max length per chunk / avg field length)
280
- const averageFieldStringLength = size / fieldsArray.length;
281
- const fieldsPerChunk = Math.floor(maxLength / averageFieldStringLength);
282
- let j = fieldsPerChunk;
283
- let result = [];
284
- let current = null;
285
- for (let i = 0, len = fieldsArray.length; i < len; i += 1) {
286
- const scope = fieldsArray[i][0];
287
- const field = fieldsArray[i][1];
288
- if (current === null || !current.listIdToFieldsMap[scope]) {
289
- j = fieldsPerChunk;
290
- current = new ScopedFieldsCollection();
291
- current.listIdToFieldsMap[scope] = new ScopedFields(scope, [field]);
292
- result.push(current);
293
- }
294
- else {
295
- current.listIdToFieldsMap[scope].addField(field);
296
- }
297
- j--;
298
- if (j === 0) {
299
- current = null;
300
- }
301
- }
302
- return result;
303
- }
304
- else if (size > 0) {
305
- return [this];
306
- }
307
- else {
308
- return [];
309
- }
310
- }
311
- size() {
312
- return this.toQueryParameterValue().length;
313
- }
314
- countOfUnScoped() {
315
- let count = 0;
316
- const fieldsArray = Object.values(this.listIdToFieldsMap);
317
- for (let i = 0, len = fieldsArray.length; i < len; i += 1) {
318
- if (fieldsArray[i].isUnScoped()) {
319
- count++;
320
- }
321
- }
322
- return count;
323
- }
324
- countOfScoped() {
325
- return Object.keys.length - this.countOfUnScoped();
326
- }
327
- isUnScopedMixedWithScoped() {
328
- return this.countOfUnScoped() > 0 && this.countOfScoped() > 0;
329
- }
330
- /**
331
- *
332
- * @param paramValue like Contacts:Contact.Id,Contact.Name;Opportunities:Opportunity.Id
333
- * @returns
334
- */
335
- static fromQueryParameterValue(paramValue) {
336
- const result = new ScopedFieldsCollection();
337
- if (paramValue) {
338
- const relativeListChunks = paramValue.split(SEPARATOR_BETWEEN_SCOPES);
339
- if (relativeListChunks.length === 0)
340
- return result;
341
- for (let i = 0, len = relativeListChunks.length; i < len; i += 1) {
342
- const parsed = ScopedFields.fromQueryParameterValue(relativeListChunks[i]);
343
- if (parsed) {
344
- result.listIdToFieldsMap[parsed.scope] = parsed;
345
- }
346
- }
347
- if (result.isUnScopedMixedWithScoped()) {
348
- throw Error('mixing scoped and unscoped field list is not allowed.');
349
- }
350
- }
351
- return result;
352
- }
161
+ /**
162
+ * related-list-records/batch fields and optional fields could be like below.
163
+ * /ui-api/related-list-records/batch/001R0000006l1xKIAQ/Contacts,Opportunities?
164
+ * optionalFields=Contacts:Contact.Id,Contact.Name;Opportunities:Opportunity.Id
165
+ *
166
+ * the pattern is [{relativeListId}:{[fields]};{relativeListId}:{[fields]}]
167
+ */
168
+ const SEPARATOR_BETWEEN_SCOPES = ';';
169
+ const SEPARATOR_BETWEEN_SCOPE_AND_FIELDS = ':';
170
+ const SEPARATOR_BETWEEN_FIELDS = ',';
171
+ const UNSCOPED_IDENTIFIER = 'unscoped';
172
+ class ScopedFields {
173
+ constructor(scope, fields) {
174
+ this.fields = {};
175
+ this.scope = scope;
176
+ for (let i = 0, len = fields.length; i < len; i += 1) {
177
+ this.fields[fields[i]] = true;
178
+ }
179
+ }
180
+ isUnScoped() {
181
+ return this.scope === UNSCOPED_IDENTIFIER;
182
+ }
183
+ addField(field) {
184
+ this.fields[field] = true;
185
+ }
186
+ addFields(fields) {
187
+ fields.forEach(this.addField, this);
188
+ }
189
+ toQueryParameterValue() {
190
+ const joinedFields = join.call(Object.keys(this.fields), SEPARATOR_BETWEEN_FIELDS);
191
+ return this.isUnScoped()
192
+ ? joinedFields
193
+ : join.call([this.scope, joinedFields], SEPARATOR_BETWEEN_SCOPE_AND_FIELDS);
194
+ }
195
+ toQueryParams() {
196
+ return this.isUnScoped() ? Object.keys(this.fields) : this.toQueryParameterValue();
197
+ }
198
+ /**
199
+ * parse Contacts:Contact.Id,Contact.Name into a QueryFields
200
+ */
201
+ static fromQueryParameterValue(paramValue) {
202
+ if (paramValue === null || paramValue === '')
203
+ return;
204
+ const scopeToFields = paramValue.split(SEPARATOR_BETWEEN_SCOPE_AND_FIELDS);
205
+ if (scopeToFields.length === 1) {
206
+ //unscoped
207
+ return new ScopedFields(UNSCOPED_IDENTIFIER, scopeToFields[0].split(SEPARATOR_BETWEEN_FIELDS));
208
+ }
209
+ else if (scopeToFields.length === 2) {
210
+ const scope = scopeToFields[0];
211
+ const fields = scopeToFields[1];
212
+ if (scope === undefined || fields === null)
213
+ return;
214
+ return new ScopedFields(scope, fields.split(SEPARATOR_BETWEEN_FIELDS));
215
+ }
216
+ else {
217
+ return;
218
+ }
219
+ }
220
+ }
221
+ class ScopedFieldsCollection {
222
+ constructor() {
223
+ this.listIdToFieldsMap = {};
224
+ }
225
+ /**
226
+ * merge the from ScopedFieldsCollection into current one
227
+ * @param from
228
+ */
229
+ merge(from) {
230
+ const { listIdToFieldsMap } = from;
231
+ const scopes = Object.keys(listIdToFieldsMap);
232
+ for (let i = 0, len = scopes.length; i < len; i += 1) {
233
+ const scope = scopes[i];
234
+ const scopedFields = listIdToFieldsMap[scope];
235
+ const existingScopedFields = this.listIdToFieldsMap[scope];
236
+ if (existingScopedFields) {
237
+ existingScopedFields.addFields(Object.keys(scopedFields.fields));
238
+ }
239
+ else {
240
+ this.listIdToFieldsMap[scope] = scopedFields;
241
+ }
242
+ }
243
+ }
244
+ toQueryParams() {
245
+ return this.countOfUnScoped() > 0
246
+ ? this.listIdToFieldsMap[UNSCOPED_IDENTIFIER].toQueryParams()
247
+ : this.toQueryParameterValue();
248
+ }
249
+ /**
250
+ * convert to query parameter value
251
+ * @returns
252
+ */
253
+ toQueryParameterValue() {
254
+ let result = [];
255
+ const scopes = Object.keys(this.listIdToFieldsMap);
256
+ for (let i = 0, len = scopes.length; i < len; i += 1) {
257
+ const chunk = this.listIdToFieldsMap[scopes[i]].toQueryParameterValue();
258
+ if (chunk !== undefined)
259
+ result.push(chunk);
260
+ }
261
+ return join.call(result, SEPARATOR_BETWEEN_SCOPES);
262
+ }
263
+ /**
264
+ * split the ScopedFields into multiple ScopedFields
265
+ * which there max fields list length will not exceeded the specified maxLength
266
+ * @param maxLength
267
+ */
268
+ split(maxLength = MAX_STRING_LENGTH_PER_CHUNK) {
269
+ const size = this.size();
270
+ if (size > maxLength) {
271
+ const fieldsArray = [];
272
+ const scopes = Object.keys(this.listIdToFieldsMap);
273
+ for (let i = 0, len = scopes.length; i < len; i += 1) {
274
+ const { scope, fields } = this.listIdToFieldsMap[scopes[i]];
275
+ Object.keys(fields).forEach((field) => {
276
+ fieldsArray.push([scope, field]);
277
+ });
278
+ }
279
+ // Formula: # of fields per chunk = floor( max length per chunk / avg field length)
280
+ const averageFieldStringLength = size / fieldsArray.length;
281
+ const fieldsPerChunk = Math.floor(maxLength / averageFieldStringLength);
282
+ let j = fieldsPerChunk;
283
+ let result = [];
284
+ let current = null;
285
+ for (let i = 0, len = fieldsArray.length; i < len; i += 1) {
286
+ const scope = fieldsArray[i][0];
287
+ const field = fieldsArray[i][1];
288
+ if (current === null || !current.listIdToFieldsMap[scope]) {
289
+ j = fieldsPerChunk;
290
+ current = new ScopedFieldsCollection();
291
+ current.listIdToFieldsMap[scope] = new ScopedFields(scope, [field]);
292
+ result.push(current);
293
+ }
294
+ else {
295
+ current.listIdToFieldsMap[scope].addField(field);
296
+ }
297
+ j--;
298
+ if (j === 0) {
299
+ current = null;
300
+ }
301
+ }
302
+ return result;
303
+ }
304
+ else if (size > 0) {
305
+ return [this];
306
+ }
307
+ else {
308
+ return [];
309
+ }
310
+ }
311
+ size() {
312
+ return this.toQueryParameterValue().length;
313
+ }
314
+ countOfUnScoped() {
315
+ let count = 0;
316
+ const fieldsArray = Object.values(this.listIdToFieldsMap);
317
+ for (let i = 0, len = fieldsArray.length; i < len; i += 1) {
318
+ if (fieldsArray[i].isUnScoped()) {
319
+ count++;
320
+ }
321
+ }
322
+ return count;
323
+ }
324
+ countOfScoped() {
325
+ return Object.keys.length - this.countOfUnScoped();
326
+ }
327
+ isUnScopedMixedWithScoped() {
328
+ return this.countOfUnScoped() > 0 && this.countOfScoped() > 0;
329
+ }
330
+ /**
331
+ *
332
+ * @param paramValue like Contacts:Contact.Id,Contact.Name;Opportunities:Opportunity.Id
333
+ * @returns
334
+ */
335
+ static fromQueryParameterValue(paramValue) {
336
+ const result = new ScopedFieldsCollection();
337
+ if (paramValue) {
338
+ const relativeListChunks = paramValue.split(SEPARATOR_BETWEEN_SCOPES);
339
+ if (relativeListChunks.length === 0)
340
+ return result;
341
+ for (let i = 0, len = relativeListChunks.length; i < len; i += 1) {
342
+ const parsed = ScopedFields.fromQueryParameterValue(relativeListChunks[i]);
343
+ if (parsed) {
344
+ result.listIdToFieldsMap[parsed.scope] = parsed;
345
+ }
346
+ }
347
+ if (result.isUnScopedMixedWithScoped()) {
348
+ throw Error('mixing scoped and unscoped field list is not allowed.');
349
+ }
350
+ }
351
+ return result;
352
+ }
353
353
  }
354
354
 
355
- const MAX_STRING_LENGTH_PER_CHUNK = 10000;
356
- const PARSE_ERROR = 'PARSE_AGGREGATE_UI_RESPONSE_ERROR';
357
- function isErrorResponse(response) {
358
- return response.httpStatusCode >= 400;
359
- }
360
- /**
361
- * merge the aggregate ui child responses into a single object representation
362
- * @param response
363
- * @param mergeFunc the function used to define how the records should be merged together
364
- * @returns the merged record
365
- */
366
- function mergeAggregateUiResponse(response, mergeFunc) {
367
- const { body } = response;
368
- try {
369
- if (body === null ||
370
- body === undefined ||
371
- body.compositeResponse === undefined ||
372
- body.compositeResponse.length === 0) {
373
- // We shouldn't even get into this state - a 200 with no body?
374
- // eslint-disable-next-line @salesforce/lds/no-error-in-production
375
- throw new Error('No response body in executeAggregateUi found');
376
- }
377
- // if the body has any non-2xx statuses then that's an error and we return
378
- // the network response with that error
379
- const error = body.compositeResponse.find(isErrorResponse);
380
- if (error !== undefined) {
381
- const { httpStatusCode, body: errorBody } = error;
382
- const statusText = errorBody.length > 0 ? errorBody[0].errorCode : '';
383
- return {
384
- ...response,
385
- ok: false,
386
- status: httpStatusCode,
387
- statusText,
388
- body: errorBody,
389
- };
390
- }
391
- // if we got here there are no errors in body, cast as such
392
- const responses = body.compositeResponse;
393
- const merged = responses.reduce((seed, resp) => {
394
- if (seed === null) {
395
- return resp.body;
396
- }
397
- return mergeFunc(seed, resp.body);
398
- }, null);
399
- return {
400
- ...response,
401
- body: merged,
402
- };
403
- }
404
- catch (error) {
405
- return {
406
- ...response,
407
- ok: false,
408
- status: HttpStatusCode.ServerError,
409
- statusText: PARSE_ERROR,
410
- body: [
411
- {
412
- errorCode: PARSE_ERROR,
413
- message: error.toString(),
414
- },
415
- ],
416
- };
417
- }
418
- }
419
- function buildAggregateUiUrl(params, resourceRequest) {
420
- const { fields, optionalFields } = params;
421
- const mergedParams = {
422
- ...resourceRequest.queryParams,
423
- fields,
424
- optionalFields,
425
- };
426
- const queryString = [];
427
- for (const [key, value] of entries(mergedParams)) {
428
- if (value !== undefined) {
429
- queryString.push(`${key}=${isArray(value) ? value.join(',') : value}`);
430
- }
431
- }
432
- return `${resourceRequest.baseUri}${resourceRequest.basePath}?${join.call(queryString, '&')}`;
433
- }
434
- function shouldUseAggregateUiForFields(fieldsArray, optionalFieldsArray) {
435
- return fieldsArray.length + optionalFieldsArray.length >= MAX_STRING_LENGTH_PER_CHUNK;
436
- }
437
- function isSpanningRecord(fieldValue) {
438
- return fieldValue !== null && typeof fieldValue === 'object';
439
- }
440
- function mergeRecordFields(first, second) {
441
- const { fields: targetFields } = first;
442
- const { fields: sourceFields } = second;
443
- const fieldNames = keys(sourceFields);
444
- for (let i = 0, len = fieldNames.length; i < len; i += 1) {
445
- const fieldName = fieldNames[i];
446
- const sourceField = sourceFields[fieldName];
447
- const targetField = targetFields[fieldName];
448
- if (isSpanningRecord(sourceField.value)) {
449
- if (targetField === undefined) {
450
- targetFields[fieldName] = sourceField;
451
- continue;
452
- }
453
- mergeRecordFields(targetField.value, sourceField.value);
454
- continue;
455
- }
456
- targetFields[fieldName] = sourceField;
457
- }
458
- return first;
459
- }
460
- function mergeBatchRecordsFields(first, second, collectionMergeFunc) {
461
- const { results: targetResults } = first;
462
- const { results: sourceResults } = second;
463
- for (let i = 0, len = targetResults.length; i < len; i += 1) {
464
- const targetResult = targetResults[i];
465
- const sourceResult = sourceResults[i];
466
- if (targetResult.statusCode !== HttpStatusCode.Ok)
467
- continue;
468
- if (sourceResult.statusCode !== HttpStatusCode.Ok) {
469
- targetResults[i] = sourceResult;
470
- continue;
471
- }
472
- collectionMergeFunc(targetResult.result, sourceResult.result);
473
- }
474
- return first;
475
- }
476
- /**
477
- * Check to see if we have fields that are > max allowed characters long
478
- * @param resourceRequest resource request to check
479
- * @param endpoint Regular Expression to check the endpoint to aggregate
480
- * @returns undefined if we should not aggregate. object if we should.
481
- */
482
- function createAggregateBatchRequestInfo(resourceRequest, endpoint) {
483
- // only handle GETs on the given endpoint regex
484
- if (!isGetRequestForEndpoint(endpoint, resourceRequest)) {
485
- return undefined;
486
- }
487
- const { queryParams: { fields, optionalFields }, } = resourceRequest;
488
- // only handle requests with fields or optional fields
489
- if (fields === undefined && optionalFields === undefined) {
490
- return undefined;
491
- }
492
- const fieldsArray = arrayOrEmpty(fields);
493
- const optionalFieldsArray = arrayOrEmpty(optionalFields);
494
- // if fields and optional fields are empty delegate request
495
- if (fieldsArray.length === 0 && optionalFieldsArray.length === 0) {
496
- return undefined;
497
- }
498
- const fieldsString = fieldsArray.join(',');
499
- const optionalFieldsString = optionalFieldsArray.join(',');
500
- const shouldUseAggregate = shouldUseAggregateUiForFields(fieldsString, optionalFieldsString);
501
- if (!shouldUseAggregate) {
502
- return undefined;
503
- }
504
- const fieldCollection = ScopedFieldsCollection.fromQueryParameterValue(fieldsString).split(MAX_STRING_LENGTH_PER_CHUNK);
505
- const optionalFieldCollection = ScopedFieldsCollection.fromQueryParameterValue(optionalFieldsString).split(MAX_STRING_LENGTH_PER_CHUNK);
506
- return {
507
- fieldCollection,
508
- optionalFieldCollection,
509
- };
510
- }
511
- function createAggregateUiRequest(resourceRequest, compositeRequest) {
512
- const aggregateUiPostBody = { compositeRequest };
513
- const aggregateResourceRequest = {
514
- method: 'post',
515
- baseUri: resourceRequest.baseUri,
516
- basePath: '/ui-api/aggregate-ui',
517
- body: aggregateUiPostBody,
518
- priority: resourceRequest.priority,
519
- queryParams: {},
520
- headers: {},
521
- urlParams: {},
522
- };
523
- return aggregateResourceRequest;
524
- }
525
- function buildCompositeRequestByFields(referenceId, resourceRequest, recordsCompositeRequest) {
526
- const { fieldCollection, optionalFieldCollection } = recordsCompositeRequest;
527
- const compositeRequest = [];
528
- if (fieldCollection !== undefined) {
529
- for (let i = 0, len = fieldCollection.length; i < len; i += 1) {
530
- const fieldChunk = fieldCollection[i].toQueryParams();
531
- if (fieldChunk.length === 0) {
532
- continue;
533
- }
534
- const url = buildAggregateUiUrl({
535
- fields: fieldChunk,
536
- }, resourceRequest);
537
- push.call(compositeRequest, {
538
- url,
539
- referenceId: `${referenceId}_fields_${i}`,
540
- });
541
- }
542
- }
543
- if (optionalFieldCollection !== undefined) {
544
- for (let i = 0, len = optionalFieldCollection.length; i < len; i += 1) {
545
- const fieldChunk = optionalFieldCollection[i].toQueryParams();
546
- if (fieldChunk.length === 0) {
547
- continue;
548
- }
549
- const url = buildAggregateUiUrl({
550
- optionalFields: fieldChunk,
551
- }, resourceRequest);
552
- push.call(compositeRequest, {
553
- url,
554
- referenceId: `${referenceId}_optionalFields_${i}`,
555
- });
556
- }
557
- }
558
- return compositeRequest;
559
- }
560
- /**
561
- * Checks if a resource request is a GET method on the given endpoint
562
- * @param endpoint Regular Expression of the endpoint
563
- * @param request the resource request
564
- */
565
- function isGetRequestForEndpoint(endpoint, request) {
566
- const { basePath, method } = request;
567
- return endpoint.test(basePath) && method === 'get';
568
- }
569
- /**
570
- * Checks if any is an array and returns it as an array.
571
- * if not an array it returns an empty array.
572
- * @param array the item to check is an array
573
- * @returns the array or an empty array
574
- */
575
- function arrayOrEmpty(array) {
576
- return array !== undefined && isArray(array) ? array : [];
355
+ const MAX_STRING_LENGTH_PER_CHUNK = 10000;
356
+ const PARSE_ERROR = 'PARSE_AGGREGATE_UI_RESPONSE_ERROR';
357
+ function isErrorResponse(response) {
358
+ return response.httpStatusCode >= 400;
359
+ }
360
+ /**
361
+ * merge the aggregate ui child responses into a single object representation
362
+ * @param response
363
+ * @param mergeFunc the function used to define how the records should be merged together
364
+ * @returns the merged record
365
+ */
366
+ function mergeAggregateUiResponse(response, mergeFunc) {
367
+ const { body } = response;
368
+ try {
369
+ if (body === null ||
370
+ body === undefined ||
371
+ body.compositeResponse === undefined ||
372
+ body.compositeResponse.length === 0) {
373
+ // We shouldn't even get into this state - a 200 with no body?
374
+ // eslint-disable-next-line @salesforce/lds/no-error-in-production
375
+ throw new Error('No response body in executeAggregateUi found');
376
+ }
377
+ // if the body has any non-2xx statuses then that's an error and we return
378
+ // the network response with that error
379
+ const error = body.compositeResponse.find(isErrorResponse);
380
+ if (error !== undefined) {
381
+ const { httpStatusCode, body: errorBody } = error;
382
+ const statusText = errorBody.length > 0 ? errorBody[0].errorCode : '';
383
+ return {
384
+ ...response,
385
+ ok: false,
386
+ status: httpStatusCode,
387
+ statusText,
388
+ body: errorBody,
389
+ };
390
+ }
391
+ // if we got here there are no errors in body, cast as such
392
+ const responses = body.compositeResponse;
393
+ const merged = responses.reduce((seed, resp) => {
394
+ if (seed === null) {
395
+ return resp.body;
396
+ }
397
+ return mergeFunc(seed, resp.body);
398
+ }, null);
399
+ return {
400
+ ...response,
401
+ body: merged,
402
+ };
403
+ }
404
+ catch (error) {
405
+ return {
406
+ ...response,
407
+ ok: false,
408
+ status: HttpStatusCode.ServerError,
409
+ statusText: PARSE_ERROR,
410
+ body: [
411
+ {
412
+ errorCode: PARSE_ERROR,
413
+ message: error.toString(),
414
+ },
415
+ ],
416
+ };
417
+ }
418
+ }
419
+ function buildAggregateUiUrl(params, resourceRequest) {
420
+ const { fields, optionalFields } = params;
421
+ const mergedParams = {
422
+ ...resourceRequest.queryParams,
423
+ fields,
424
+ optionalFields,
425
+ };
426
+ const queryString = [];
427
+ for (const [key, value] of entries(mergedParams)) {
428
+ if (value !== undefined) {
429
+ queryString.push(`${key}=${isArray(value) ? value.join(',') : value}`);
430
+ }
431
+ }
432
+ return `${resourceRequest.baseUri}${resourceRequest.basePath}?${join.call(queryString, '&')}`;
433
+ }
434
+ function shouldUseAggregateUiForFields(fieldsArray, optionalFieldsArray) {
435
+ return fieldsArray.length + optionalFieldsArray.length >= MAX_STRING_LENGTH_PER_CHUNK;
436
+ }
437
+ function isSpanningRecord(fieldValue) {
438
+ return fieldValue !== null && typeof fieldValue === 'object';
439
+ }
440
+ function mergeRecordFields(first, second) {
441
+ const { fields: targetFields } = first;
442
+ const { fields: sourceFields } = second;
443
+ const fieldNames = keys(sourceFields);
444
+ for (let i = 0, len = fieldNames.length; i < len; i += 1) {
445
+ const fieldName = fieldNames[i];
446
+ const sourceField = sourceFields[fieldName];
447
+ const targetField = targetFields[fieldName];
448
+ if (isSpanningRecord(sourceField.value)) {
449
+ if (targetField === undefined) {
450
+ targetFields[fieldName] = sourceField;
451
+ continue;
452
+ }
453
+ mergeRecordFields(targetField.value, sourceField.value);
454
+ continue;
455
+ }
456
+ targetFields[fieldName] = sourceField;
457
+ }
458
+ return first;
459
+ }
460
+ function mergeBatchRecordsFields(first, second, collectionMergeFunc) {
461
+ const { results: targetResults } = first;
462
+ const { results: sourceResults } = second;
463
+ for (let i = 0, len = targetResults.length; i < len; i += 1) {
464
+ const targetResult = targetResults[i];
465
+ const sourceResult = sourceResults[i];
466
+ if (targetResult.statusCode !== HttpStatusCode.Ok)
467
+ continue;
468
+ if (sourceResult.statusCode !== HttpStatusCode.Ok) {
469
+ targetResults[i] = sourceResult;
470
+ continue;
471
+ }
472
+ collectionMergeFunc(targetResult.result, sourceResult.result);
473
+ }
474
+ return first;
475
+ }
476
+ /**
477
+ * Check to see if we have fields that are > max allowed characters long
478
+ * @param resourceRequest resource request to check
479
+ * @param endpoint Regular Expression to check the endpoint to aggregate
480
+ * @returns undefined if we should not aggregate. object if we should.
481
+ */
482
+ function createAggregateBatchRequestInfo(resourceRequest, endpoint) {
483
+ // only handle GETs on the given endpoint regex
484
+ if (!isGetRequestForEndpoint(endpoint, resourceRequest)) {
485
+ return undefined;
486
+ }
487
+ const { queryParams: { fields, optionalFields }, } = resourceRequest;
488
+ // only handle requests with fields or optional fields
489
+ if (fields === undefined && optionalFields === undefined) {
490
+ return undefined;
491
+ }
492
+ const fieldsArray = arrayOrEmpty(fields);
493
+ const optionalFieldsArray = arrayOrEmpty(optionalFields);
494
+ // if fields and optional fields are empty delegate request
495
+ if (fieldsArray.length === 0 && optionalFieldsArray.length === 0) {
496
+ return undefined;
497
+ }
498
+ const fieldsString = fieldsArray.join(',');
499
+ const optionalFieldsString = optionalFieldsArray.join(',');
500
+ const shouldUseAggregate = shouldUseAggregateUiForFields(fieldsString, optionalFieldsString);
501
+ if (!shouldUseAggregate) {
502
+ return undefined;
503
+ }
504
+ const fieldCollection = ScopedFieldsCollection.fromQueryParameterValue(fieldsString).split(MAX_STRING_LENGTH_PER_CHUNK);
505
+ const optionalFieldCollection = ScopedFieldsCollection.fromQueryParameterValue(optionalFieldsString).split(MAX_STRING_LENGTH_PER_CHUNK);
506
+ return {
507
+ fieldCollection,
508
+ optionalFieldCollection,
509
+ };
510
+ }
511
+ function createAggregateUiRequest(resourceRequest, compositeRequest) {
512
+ const aggregateUiPostBody = { compositeRequest };
513
+ const aggregateResourceRequest = {
514
+ method: 'post',
515
+ baseUri: resourceRequest.baseUri,
516
+ basePath: '/ui-api/aggregate-ui',
517
+ body: aggregateUiPostBody,
518
+ priority: resourceRequest.priority,
519
+ queryParams: {},
520
+ headers: {},
521
+ urlParams: {},
522
+ };
523
+ return aggregateResourceRequest;
524
+ }
525
+ function buildCompositeRequestByFields(referenceId, resourceRequest, recordsCompositeRequest) {
526
+ const { fieldCollection, optionalFieldCollection } = recordsCompositeRequest;
527
+ const compositeRequest = [];
528
+ if (fieldCollection !== undefined) {
529
+ for (let i = 0, len = fieldCollection.length; i < len; i += 1) {
530
+ const fieldChunk = fieldCollection[i].toQueryParams();
531
+ if (fieldChunk.length === 0) {
532
+ continue;
533
+ }
534
+ const url = buildAggregateUiUrl({
535
+ fields: fieldChunk,
536
+ }, resourceRequest);
537
+ push.call(compositeRequest, {
538
+ url,
539
+ referenceId: `${referenceId}_fields_${i}`,
540
+ });
541
+ }
542
+ }
543
+ if (optionalFieldCollection !== undefined) {
544
+ for (let i = 0, len = optionalFieldCollection.length; i < len; i += 1) {
545
+ const fieldChunk = optionalFieldCollection[i].toQueryParams();
546
+ if (fieldChunk.length === 0) {
547
+ continue;
548
+ }
549
+ const url = buildAggregateUiUrl({
550
+ optionalFields: fieldChunk,
551
+ }, resourceRequest);
552
+ push.call(compositeRequest, {
553
+ url,
554
+ referenceId: `${referenceId}_optionalFields_${i}`,
555
+ });
556
+ }
557
+ }
558
+ return compositeRequest;
559
+ }
560
+ /**
561
+ * Checks if a resource request is a GET method on the given endpoint
562
+ * @param endpoint Regular Expression of the endpoint
563
+ * @param request the resource request
564
+ */
565
+ function isGetRequestForEndpoint(endpoint, request) {
566
+ const { basePath, method } = request;
567
+ return endpoint.test(basePath) && method === 'get';
568
+ }
569
+ /**
570
+ * Checks if any is an array and returns it as an array.
571
+ * if not an array it returns an empty array.
572
+ * @param array the item to check is an array
573
+ * @returns the array or an empty array
574
+ */
575
+ function arrayOrEmpty(array) {
576
+ return array !== undefined && isArray(array) ? array : [];
577
577
  }
578
578
 
579
- const RECORD_ENDPOINT_REGEX = /^\/ui-api\/records\/?(([a-zA-Z0-9]+))?$/;
580
- const referenceId$3 = 'LDS_Records_AggregateUi';
581
- /**
582
- * Export to facilitate unit tests
583
- * Merge the second getRecord result into the first one.
584
- * If any is error response, merged result is error response.
585
- * If both are sucesses, due to they are from same records,
586
- * fields sub node will be merged recursively
587
- */
588
- function mergeGetRecordResult(first, second) {
589
- // return the error if first is error.
590
- if (isArray(first) && !isArray(second))
591
- return first;
592
- // return the error if second is error.
593
- if (!isArray(first) && isArray(second))
594
- return second;
595
- // concat the error array if both are error
596
- if (isArray(first) && isArray(second)) {
597
- return [...first, ...second];
598
- }
599
- mergeRecordFields(first, second);
600
- return first;
601
- }
602
- function makeNetworkChunkFieldsGetRecord(networkAdapter) {
603
- return (resourceRequest, resourceRequestContext) => {
604
- const batchRequestInfo = createAggregateBatchRequestInfo(resourceRequest, RECORD_ENDPOINT_REGEX);
605
- if (batchRequestInfo === undefined) {
606
- return networkAdapter(resourceRequest, resourceRequestContext);
607
- }
608
- const compositeRequest = buildCompositeRequestByFields(referenceId$3, resourceRequest, batchRequestInfo);
609
- const aggregateRequest = createAggregateUiRequest(resourceRequest, compositeRequest);
610
- return networkAdapter(aggregateRequest, resourceRequestContext).then((response) => {
611
- return mergeAggregateUiResponse(response, mergeGetRecordResult);
612
- });
613
- };
579
+ const RECORD_ENDPOINT_REGEX = /^\/ui-api\/records\/?(([a-zA-Z0-9]+))?$/;
580
+ const referenceId$3 = 'LDS_Records_AggregateUi';
581
+ /**
582
+ * Export to facilitate unit tests
583
+ * Merge the second getRecord result into the first one.
584
+ * If any is error response, merged result is error response.
585
+ * If both are sucesses, due to they are from same records,
586
+ * fields sub node will be merged recursively
587
+ */
588
+ function mergeGetRecordResult(first, second) {
589
+ // return the error if first is error.
590
+ if (isArray(first) && !isArray(second))
591
+ return first;
592
+ // return the error if second is error.
593
+ if (!isArray(first) && isArray(second))
594
+ return second;
595
+ // concat the error array if both are error
596
+ if (isArray(first) && isArray(second)) {
597
+ return [...first, ...second];
598
+ }
599
+ mergeRecordFields(first, second);
600
+ return first;
601
+ }
602
+ function makeNetworkChunkFieldsGetRecord(networkAdapter) {
603
+ return (resourceRequest, resourceRequestContext) => {
604
+ const batchRequestInfo = createAggregateBatchRequestInfo(resourceRequest, RECORD_ENDPOINT_REGEX);
605
+ if (batchRequestInfo === undefined) {
606
+ return networkAdapter(resourceRequest, resourceRequestContext);
607
+ }
608
+ const compositeRequest = buildCompositeRequestByFields(referenceId$3, resourceRequest, batchRequestInfo);
609
+ const aggregateRequest = createAggregateUiRequest(resourceRequest, compositeRequest);
610
+ return networkAdapter(aggregateRequest, resourceRequestContext).then((response) => {
611
+ return mergeAggregateUiResponse(response, mergeGetRecordResult);
612
+ });
613
+ };
614
614
  }
615
615
 
616
- const RECORDS_BATCH_ENDPOINT_REGEX = /^\/ui-api\/records\/batch\/?(([a-zA-Z0-9|,]+))?$/;
617
- const referenceId$2 = 'LDS_Records_Batch_AggregateUi';
618
- function makeNetworkChunkFieldsGetRecordsBatch(networkAdapter) {
619
- return (resourceRequest, resourceRequestContext) => {
620
- const batchRequestInfo = createAggregateBatchRequestInfo(resourceRequest, RECORDS_BATCH_ENDPOINT_REGEX);
621
- if (batchRequestInfo === undefined) {
622
- return networkAdapter(resourceRequest, resourceRequestContext);
623
- }
624
- const compositeRequest = buildCompositeRequestByFields(referenceId$2, resourceRequest, batchRequestInfo);
625
- const aggregateRequest = createAggregateUiRequest(resourceRequest, compositeRequest);
626
- return networkAdapter(aggregateRequest, resourceRequestContext).then((response) => {
627
- return mergeAggregateUiResponse(response, (first, second) => {
628
- return mergeBatchRecordsFields(first, second, (a, b) => {
629
- return mergeRecordFields(a, b);
630
- });
631
- });
632
- });
633
- };
616
+ const RECORDS_BATCH_ENDPOINT_REGEX = /^\/ui-api\/records\/batch\/?(([a-zA-Z0-9|,]+))?$/;
617
+ const referenceId$2 = 'LDS_Records_Batch_AggregateUi';
618
+ function makeNetworkChunkFieldsGetRecordsBatch(networkAdapter) {
619
+ return (resourceRequest, resourceRequestContext) => {
620
+ const batchRequestInfo = createAggregateBatchRequestInfo(resourceRequest, RECORDS_BATCH_ENDPOINT_REGEX);
621
+ if (batchRequestInfo === undefined) {
622
+ return networkAdapter(resourceRequest, resourceRequestContext);
623
+ }
624
+ const compositeRequest = buildCompositeRequestByFields(referenceId$2, resourceRequest, batchRequestInfo);
625
+ const aggregateRequest = createAggregateUiRequest(resourceRequest, compositeRequest);
626
+ return networkAdapter(aggregateRequest, resourceRequestContext).then((response) => {
627
+ return mergeAggregateUiResponse(response, (first, second) => {
628
+ return mergeBatchRecordsFields(first, second, (a, b) => {
629
+ return mergeRecordFields(a, b);
630
+ });
631
+ });
632
+ });
633
+ };
634
634
  }
635
635
 
636
- const RELATED_LIST_RECORDS_ENDPOINT_REGEX = /^\/ui-api\/related-list-records\/?(([a-zA-Z0-9]+))?\/?(([a-zA-Z0-9]+))?$/;
637
- const referenceId$1 = 'LDS_Related_List_Records_AggregateUi';
638
- const QUERY_KEY_FIELDS = 'fields';
639
- const QUERY_KEY_OPTIONAL_FIELDS = 'optionalFields';
640
- /**
641
- * Merge the second related list record collection into first one and return it.
642
- * It checks both collections should have exaction same records, otherwise error.
643
- * Exports it for unit tests
644
- */
645
- function mergeRelatedRecordsFields(first, second) {
646
- const { records: targetRecords } = first;
647
- const { records: sourceRecords } = second;
648
- if (sourceRecords.length === targetRecords.length &&
649
- recordIdsAllMatch(targetRecords, sourceRecords)) {
650
- first.fields = first.fields.concat(second.fields);
651
- first.optionalFields = first.optionalFields.concat(second.optionalFields);
652
- for (let i = 0, len = sourceRecords.length; i < len; i += 1) {
653
- const targetRecord = targetRecords[i];
654
- const sourceRecord = sourceRecords[i];
655
- mergeRecordFields(targetRecord, sourceRecord);
656
- }
657
- mergePageUrls(first, second);
658
- return first;
659
- }
660
- else {
661
- // Throw error due to two collection are about different set of records
662
- // eslint-disable-next-line @salesforce/lds/no-error-in-production
663
- throw new Error('Aggregate UI response is invalid');
664
- }
665
- }
666
- function makeNetworkChunkFieldsGetRelatedListRecords(networkAdapter) {
667
- return (resourceRequest, resourceRequestContext) => {
668
- const batchRequestInfo = createAggregateBatchRequestInfo(resourceRequest, RELATED_LIST_RECORDS_ENDPOINT_REGEX);
669
- if (batchRequestInfo === undefined) {
670
- return networkAdapter(resourceRequest, resourceRequestContext);
671
- }
672
- const compositeRequest = buildCompositeRequestByFields(referenceId$1, resourceRequest, batchRequestInfo);
673
- const aggregateRequest = createAggregateUiRequest(resourceRequest, compositeRequest);
674
- return networkAdapter(aggregateRequest, resourceRequestContext).then((response) => {
675
- return mergeAggregateUiResponse(response, mergeRelatedRecordsFields);
676
- });
677
- };
678
- }
679
- /**
680
- * merge the second related list record collection into first one and return it
681
- */
682
- function mergePageUrls(first, second) {
683
- first.currentPageUrl = mergeUrl(first.currentPageUrl, second.currentPageUrl);
684
- first.previousPageUrl = mergeUrl(first.previousPageUrl, second.previousPageUrl);
685
- first.nextPageUrl = mergeUrl(first.nextPageUrl, second.nextPageUrl);
686
- }
687
- /**
688
- * merge to paging url with different set of fields or optional fields as combined one
689
- * the paging url is like
690
- * /services/data/v58.0/ui-api/related-list-records/001R0000006l1xKIAQ/Contacts
691
- * ?fields=Id%2CName&optionalFields=Contact.Id%2CContact.Name&pageSize=50&pageToken=0
692
- * @param path1 url path and query parmeter without domain
693
- * @param path2 url path and query parmeter without domain
694
- *
695
- * Export to unit test
696
- */
697
- function mergeUrl(path1, path2) {
698
- if (path1 === null)
699
- return path2;
700
- if (path2 === null)
701
- return path1;
702
- // new Url(...) need the path1, path2 to be prefix-ed with this fake domain
703
- const domain = 'http://c.com';
704
- const url1 = new URL(domain + path1);
705
- const url2 = new URL(domain + path2);
706
- const searchParams1 = url1.searchParams;
707
- const fields = mergeFields(url1, url2, QUERY_KEY_FIELDS);
708
- if (fields && searchParams1.get(QUERY_KEY_FIELDS) !== fields) {
709
- searchParams1.set(QUERY_KEY_FIELDS, fields);
710
- }
711
- const optionalFields = mergeFields(url1, url2, QUERY_KEY_OPTIONAL_FIELDS);
712
- if (optionalFields && searchParams1.get(QUERY_KEY_OPTIONAL_FIELDS) !== optionalFields) {
713
- searchParams1.set(QUERY_KEY_OPTIONAL_FIELDS, optionalFields);
714
- }
715
- from(searchParams1.keys())
716
- .sort()
717
- .forEach((key) => {
718
- const value = searchParams1.get(key);
719
- searchParams1.delete(key);
720
- searchParams1.append(key, value);
721
- });
722
- return url1.toString().substr(domain.length);
723
- }
724
- function mergeFields(url1, url2, name) {
725
- const fields1 = ScopedFieldsCollection.fromQueryParameterValue(url1.searchParams.get(name));
726
- const fields2 = ScopedFieldsCollection.fromQueryParameterValue(url2.searchParams.get(name));
727
- fields1.merge(fields2);
728
- return fields1.toQueryParameterValue();
729
- }
730
- /**
731
- * Checks that all records ids exist in both arrays
732
- * @param first batch of first array or records
733
- * @param second batch of second array or records
734
- * @returns
735
- */
736
- function recordIdsAllMatch(first, second) {
737
- const firstIds = first.map((record) => record.id);
738
- const secondIds = second.map((record) => record.id);
739
- return firstIds.every((id) => secondIds.includes(id));
636
+ const RELATED_LIST_RECORDS_ENDPOINT_REGEX = /^\/ui-api\/related-list-records\/?(([a-zA-Z0-9]+))?\/?(([a-zA-Z0-9]+))?$/;
637
+ const referenceId$1 = 'LDS_Related_List_Records_AggregateUi';
638
+ const QUERY_KEY_FIELDS = 'fields';
639
+ const QUERY_KEY_OPTIONAL_FIELDS = 'optionalFields';
640
+ /**
641
+ * Merge the second related list record collection into first one and return it.
642
+ * It checks both collections should have exaction same records, otherwise error.
643
+ * Exports it for unit tests
644
+ */
645
+ function mergeRelatedRecordsFields(first, second) {
646
+ const { records: targetRecords } = first;
647
+ const { records: sourceRecords } = second;
648
+ if (sourceRecords.length === targetRecords.length &&
649
+ recordIdsAllMatch(targetRecords, sourceRecords)) {
650
+ first.fields = first.fields.concat(second.fields);
651
+ first.optionalFields = first.optionalFields.concat(second.optionalFields);
652
+ for (let i = 0, len = sourceRecords.length; i < len; i += 1) {
653
+ const targetRecord = targetRecords[i];
654
+ const sourceRecord = sourceRecords[i];
655
+ mergeRecordFields(targetRecord, sourceRecord);
656
+ }
657
+ mergePageUrls(first, second);
658
+ return first;
659
+ }
660
+ else {
661
+ // Throw error due to two collection are about different set of records
662
+ // eslint-disable-next-line @salesforce/lds/no-error-in-production
663
+ throw new Error('Aggregate UI response is invalid');
664
+ }
665
+ }
666
+ function makeNetworkChunkFieldsGetRelatedListRecords(networkAdapter) {
667
+ return (resourceRequest, resourceRequestContext) => {
668
+ const batchRequestInfo = createAggregateBatchRequestInfo(resourceRequest, RELATED_LIST_RECORDS_ENDPOINT_REGEX);
669
+ if (batchRequestInfo === undefined) {
670
+ return networkAdapter(resourceRequest, resourceRequestContext);
671
+ }
672
+ const compositeRequest = buildCompositeRequestByFields(referenceId$1, resourceRequest, batchRequestInfo);
673
+ const aggregateRequest = createAggregateUiRequest(resourceRequest, compositeRequest);
674
+ return networkAdapter(aggregateRequest, resourceRequestContext).then((response) => {
675
+ return mergeAggregateUiResponse(response, mergeRelatedRecordsFields);
676
+ });
677
+ };
678
+ }
679
+ /**
680
+ * merge the second related list record collection into first one and return it
681
+ */
682
+ function mergePageUrls(first, second) {
683
+ first.currentPageUrl = mergeUrl(first.currentPageUrl, second.currentPageUrl);
684
+ first.previousPageUrl = mergeUrl(first.previousPageUrl, second.previousPageUrl);
685
+ first.nextPageUrl = mergeUrl(first.nextPageUrl, second.nextPageUrl);
686
+ }
687
+ /**
688
+ * merge to paging url with different set of fields or optional fields as combined one
689
+ * the paging url is like
690
+ * /services/data/v58.0/ui-api/related-list-records/001R0000006l1xKIAQ/Contacts
691
+ * ?fields=Id%2CName&optionalFields=Contact.Id%2CContact.Name&pageSize=50&pageToken=0
692
+ * @param path1 url path and query parmeter without domain
693
+ * @param path2 url path and query parmeter without domain
694
+ *
695
+ * Export to unit test
696
+ */
697
+ function mergeUrl(path1, path2) {
698
+ if (path1 === null)
699
+ return path2;
700
+ if (path2 === null)
701
+ return path1;
702
+ // new Url(...) need the path1, path2 to be prefix-ed with this fake domain
703
+ const domain = 'http://c.com';
704
+ const url1 = new URL(domain + path1);
705
+ const url2 = new URL(domain + path2);
706
+ const searchParams1 = url1.searchParams;
707
+ const fields = mergeFields(url1, url2, QUERY_KEY_FIELDS);
708
+ if (fields && searchParams1.get(QUERY_KEY_FIELDS) !== fields) {
709
+ searchParams1.set(QUERY_KEY_FIELDS, fields);
710
+ }
711
+ const optionalFields = mergeFields(url1, url2, QUERY_KEY_OPTIONAL_FIELDS);
712
+ if (optionalFields && searchParams1.get(QUERY_KEY_OPTIONAL_FIELDS) !== optionalFields) {
713
+ searchParams1.set(QUERY_KEY_OPTIONAL_FIELDS, optionalFields);
714
+ }
715
+ from(searchParams1.keys())
716
+ .sort()
717
+ .forEach((key) => {
718
+ const value = searchParams1.get(key);
719
+ searchParams1.delete(key);
720
+ searchParams1.append(key, value);
721
+ });
722
+ return url1.toString().substr(domain.length);
723
+ }
724
+ function mergeFields(url1, url2, name) {
725
+ const fields1 = ScopedFieldsCollection.fromQueryParameterValue(url1.searchParams.get(name));
726
+ const fields2 = ScopedFieldsCollection.fromQueryParameterValue(url2.searchParams.get(name));
727
+ fields1.merge(fields2);
728
+ return fields1.toQueryParameterValue();
729
+ }
730
+ /**
731
+ * Checks that all records ids exist in both arrays
732
+ * @param first batch of first array or records
733
+ * @param second batch of second array or records
734
+ * @returns
735
+ */
736
+ function recordIdsAllMatch(first, second) {
737
+ const firstIds = first.map((record) => record.id);
738
+ const secondIds = second.map((record) => record.id);
739
+ return firstIds.every((id) => secondIds.includes(id));
740
740
  }
741
741
 
742
- const RELATED_LIST_RECORDS_BATCH_ENDPOINT_REGEX = /^\/ui-api\/related-list-records\/batch\/?(([a-zA-Z0-9]+))?\//;
743
- const referenceId = 'LDS_Related_List_Records_AggregateUi';
744
- function makeNetworkChunkFieldsGetRelatedListRecordsBatch(networkAdapter) {
745
- return (resourceRequest, resourceRequestContext) => {
746
- const batchRequestInfo = createAggregateBatchRequestInfo(resourceRequest, RELATED_LIST_RECORDS_BATCH_ENDPOINT_REGEX);
747
- if (batchRequestInfo === undefined) {
748
- return networkAdapter(resourceRequest, resourceRequestContext);
749
- }
750
- const compositeRequest = buildCompositeRequestByFields(referenceId, resourceRequest, batchRequestInfo);
751
- const aggregateRequest = createAggregateUiRequest(resourceRequest, compositeRequest);
752
- return networkAdapter(aggregateRequest, resourceRequestContext).then((response) => {
753
- return mergeAggregateUiResponse(response, (first, second) => {
754
- return mergeBatchRecordsFields(first, second, (a, b) => {
755
- return mergeRelatedRecordsFields(a, b);
756
- });
757
- });
758
- });
759
- };
742
+ const RELATED_LIST_RECORDS_BATCH_ENDPOINT_REGEX = /^\/ui-api\/related-list-records\/batch\/?(([a-zA-Z0-9]+))?\//;
743
+ const referenceId = 'LDS_Related_List_Records_AggregateUi';
744
+ function makeNetworkChunkFieldsGetRelatedListRecordsBatch(networkAdapter) {
745
+ return (resourceRequest, resourceRequestContext) => {
746
+ const batchRequestInfo = createAggregateBatchRequestInfo(resourceRequest, RELATED_LIST_RECORDS_BATCH_ENDPOINT_REGEX);
747
+ if (batchRequestInfo === undefined) {
748
+ return networkAdapter(resourceRequest, resourceRequestContext);
749
+ }
750
+ const compositeRequest = buildCompositeRequestByFields(referenceId, resourceRequest, batchRequestInfo);
751
+ const aggregateRequest = createAggregateUiRequest(resourceRequest, compositeRequest);
752
+ return networkAdapter(aggregateRequest, resourceRequestContext).then((response) => {
753
+ return mergeAggregateUiResponse(response, (first, second) => {
754
+ return mergeBatchRecordsFields(first, second, (a, b) => {
755
+ return mergeRelatedRecordsFields(a, b);
756
+ });
757
+ });
758
+ });
759
+ };
760
760
  }
761
761
 
762
- /**
763
- * Higher order function that accepts a network adapter and returns a new network adapter
764
- * that is capable of performing field batching to ensure that URL length limits are respected
765
- * when hitting record endpoints that accept a field list
766
- *
767
- * @param networkAdapter the network adapter to do the call.
768
- */
769
- function makeNetworkAdapterChunkRecordFields(networkAdapter) {
770
- // endpoint handlers that support aggregate-ui field batching
771
- const batchHandlers = [
772
- makeNetworkChunkFieldsGetRecord,
773
- makeNetworkChunkFieldsGetRecordsBatch,
774
- makeNetworkChunkFieldsGetRelatedListRecords,
775
- makeNetworkChunkFieldsGetRelatedListRecordsBatch,
776
- ];
777
- return batchHandlers.reduce((network, handler) => {
778
- return handler(network);
779
- }, networkAdapter);
762
+ /**
763
+ * Higher order function that accepts a network adapter and returns a new network adapter
764
+ * that is capable of performing field batching to ensure that URL length limits are respected
765
+ * when hitting record endpoints that accept a field list
766
+ *
767
+ * @param networkAdapter the network adapter to do the call.
768
+ */
769
+ function makeNetworkAdapterChunkRecordFields(networkAdapter) {
770
+ // endpoint handlers that support aggregate-ui field batching
771
+ const batchHandlers = [
772
+ makeNetworkChunkFieldsGetRecord,
773
+ makeNetworkChunkFieldsGetRecordsBatch,
774
+ makeNetworkChunkFieldsGetRelatedListRecords,
775
+ makeNetworkChunkFieldsGetRelatedListRecordsBatch,
776
+ ];
777
+ return batchHandlers.reduce((network, handler) => {
778
+ return handler(network);
779
+ }, networkAdapter);
780
780
  }
781
781
 
782
782
  export { NimbusNetworkAdapter, makeNetworkAdapterChunkRecordFields };