@salesforce/lds-runtime-bridge 1.294.0 → 1.295.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ldsRuntimeBridge.js +42 -2192
- package/package.json +8 -8
package/dist/ldsRuntimeBridge.js
CHANGED
|
@@ -14,12 +14,9 @@
|
|
|
14
14
|
import { setDefaultLuvio } from 'force/ldsEngine';
|
|
15
15
|
import { setBypassDeepFreeze, StoreKeySet, serializeStructuredKey, StringKeyInMemoryStore, Reader, deepFreeze, emitAdapterEvent, InMemoryStore, Environment, Luvio } from 'force/luvioEngine';
|
|
16
16
|
import { instrumentLuvio } from 'force/ldsInstrumentation';
|
|
17
|
-
import { isStoreKeyRecordViewEntity, RECORD_ID_PREFIX, RECORD_FIELDS_KEY_JUNCTION,
|
|
17
|
+
import { isStoreKeyRecordViewEntity, RECORD_ID_PREFIX, RECORD_FIELDS_KEY_JUNCTION, extractRecordIdFromStoreKey, RECORD_VIEW_ENTITY_ID_PREFIX, keyBuilderRecord } from 'native/ldsAdaptersUiapiMobile';
|
|
18
18
|
import '@salesforce/gate/lds.idempotencyWriteDisabled';
|
|
19
19
|
import '@salesforce/gate/lds.backdatingEnabled';
|
|
20
|
-
import { Kind, buildSchema, isObjectType, defaultFieldResolver } from 'force/ldsGraphqlParser';
|
|
21
|
-
import FIRST_DAY_OF_WEEK from '@salesforce/i18n/firstDayOfWeek';
|
|
22
|
-
import excludeStaleRecordsGate from '@salesforce/gate/lds.graphqlEvalExcludeStaleRecords';
|
|
23
20
|
import networkAdapter from 'force/ldsNetwork';
|
|
24
21
|
import ldsEngineCreator from 'force/ldsEngineCreator';
|
|
25
22
|
|
|
@@ -45,7 +42,7 @@ const RedirectDurableSegment = 'REDIRECT_KEYS';
|
|
|
45
42
|
const MessagingDurableSegment = 'MESSAGING';
|
|
46
43
|
const MessageNotifyStoreUpdateAvailable = 'notifyStoreUpdateAvailable';
|
|
47
44
|
|
|
48
|
-
const { keys: keys$
|
|
45
|
+
const { keys: keys$2, create: create$2, assign: assign$2, freeze: freeze$1 } = Object;
|
|
49
46
|
|
|
50
47
|
//Durable store error instrumentation key
|
|
51
48
|
const DURABLE_STORE_ERROR = 'durable-store-error';
|
|
@@ -95,7 +92,7 @@ function publishDurableStoreEntries(durableRecords, put, publishMetadata) {
|
|
|
95
92
|
if (durableRecords === undefined) {
|
|
96
93
|
return { revivedKeys, hadUnexpectedShape };
|
|
97
94
|
}
|
|
98
|
-
const durableKeys = keys$
|
|
95
|
+
const durableKeys = keys$2(durableRecords);
|
|
99
96
|
if (durableKeys.length === 0) {
|
|
100
97
|
// no records to revive
|
|
101
98
|
return { revivedKeys, hadUnexpectedShape };
|
|
@@ -280,7 +277,7 @@ class DurableTTLStore {
|
|
|
280
277
|
overrides,
|
|
281
278
|
};
|
|
282
279
|
}
|
|
283
|
-
const keys$1 = keys$
|
|
280
|
+
const keys$1 = keys$2(entries);
|
|
284
281
|
for (let i = 0, len = keys$1.length; i < len; i++) {
|
|
285
282
|
const key = keys$1[i];
|
|
286
283
|
const entry = entries[key];
|
|
@@ -302,14 +299,14 @@ class DurableTTLStore {
|
|
|
302
299
|
}
|
|
303
300
|
|
|
304
301
|
function flushInMemoryStoreValuesToDurableStore(store, durableStore, durableStoreErrorHandler, redirects, additionalDurableStoreOperations = [], enableDurableMetadataRefresh = false) {
|
|
305
|
-
const durableRecords = create$
|
|
306
|
-
const refreshedDurableRecords = create$
|
|
307
|
-
const evictedRecords = create$
|
|
302
|
+
const durableRecords = create$2(null);
|
|
303
|
+
const refreshedDurableRecords = create$2(null);
|
|
304
|
+
const evictedRecords = create$2(null);
|
|
308
305
|
const { visitedIds, refreshedIds } = store.fallbackStringKeyInMemoryStore;
|
|
309
306
|
// TODO: W-8909393 Once metadata is stored in its own segment we need to
|
|
310
307
|
// call setEntries for the visitedIds on default segment and call setEntries
|
|
311
308
|
// on the metadata segment for the refreshedIds
|
|
312
|
-
const keys$1 = keys$
|
|
309
|
+
const keys$1 = keys$2({ ...visitedIds, ...refreshedIds });
|
|
313
310
|
for (let i = 0, len = keys$1.length; i < len; i += 1) {
|
|
314
311
|
const key = keys$1[i];
|
|
315
312
|
const canonicalKey = store.getCanonicalRecordId(key);
|
|
@@ -332,7 +329,7 @@ function flushInMemoryStoreValuesToDurableStore(store, durableStore, durableStor
|
|
|
332
329
|
setRecordTo(entries, key, record, metadata);
|
|
333
330
|
}
|
|
334
331
|
const durableStoreOperations = additionalDurableStoreOperations;
|
|
335
|
-
const recordKeys = keys$
|
|
332
|
+
const recordKeys = keys$2(durableRecords);
|
|
336
333
|
if (recordKeys.length > 0) {
|
|
337
334
|
// publishes with data
|
|
338
335
|
durableStoreOperations.push({
|
|
@@ -341,7 +338,7 @@ function flushInMemoryStoreValuesToDurableStore(store, durableStore, durableStor
|
|
|
341
338
|
segment: DefaultDurableSegment,
|
|
342
339
|
});
|
|
343
340
|
}
|
|
344
|
-
const refreshKeys = keys$
|
|
341
|
+
const refreshKeys = keys$2(refreshedDurableRecords);
|
|
345
342
|
if (refreshKeys.length > 0) {
|
|
346
343
|
// publishes with only metadata updates
|
|
347
344
|
durableStoreOperations.push({
|
|
@@ -363,7 +360,7 @@ function flushInMemoryStoreValuesToDurableStore(store, durableStore, durableStor
|
|
|
363
360
|
});
|
|
364
361
|
});
|
|
365
362
|
// evicts
|
|
366
|
-
const evictedKeys = keys$
|
|
363
|
+
const evictedKeys = keys$2(evictedRecords);
|
|
367
364
|
if (evictedKeys.length > 0) {
|
|
368
365
|
durableStoreOperations.push({
|
|
369
366
|
type: 'evictEntries',
|
|
@@ -467,7 +464,7 @@ function buildRevivingStagingStore(upstreamStore) {
|
|
|
467
464
|
// A reviving store is only "active" during a call to `environment.storeLookup`, and will
|
|
468
465
|
// be used by the reader attempting to build an L1 snapshot. Immediately after the L1 rebuild
|
|
469
466
|
// the reviving store becomes inactive other than receiving change notifications.
|
|
470
|
-
return create$
|
|
467
|
+
return create$2(upstreamStore, {
|
|
471
468
|
readEntry: { value: readEntry },
|
|
472
469
|
markStale: { value: markStale },
|
|
473
470
|
clearStale: { value: clearStale },
|
|
@@ -479,7 +476,7 @@ const AdapterContextSegment = 'ADAPTER-CONTEXT';
|
|
|
479
476
|
const ADAPTER_CONTEXT_ID_SUFFIX = '__NAMED_CONTEXT';
|
|
480
477
|
async function reviveOrCreateContext(adapterId, durableStore, durableStoreErrorHandler, contextStores, pendingContextStoreKeys, onContextLoaded) {
|
|
481
478
|
// initialize empty context store
|
|
482
|
-
contextStores[adapterId] = create$
|
|
479
|
+
contextStores[adapterId] = create$2(null);
|
|
483
480
|
const context = {
|
|
484
481
|
set(key, value) {
|
|
485
482
|
contextStores[adapterId][key] = value;
|
|
@@ -544,7 +541,7 @@ function makeDurable(environment, { durableStore, instrumentation, useRevivingSt
|
|
|
544
541
|
const revivingStores = new Set();
|
|
545
542
|
// redirects that need to be flushed to the durable store
|
|
546
543
|
const pendingStoreRedirects = new Map();
|
|
547
|
-
const contextStores = create$
|
|
544
|
+
const contextStores = create$2(null);
|
|
548
545
|
let initializationPromise = new Promise((resolve) => {
|
|
549
546
|
const finish = () => {
|
|
550
547
|
resolve();
|
|
@@ -621,7 +618,7 @@ function makeDurable(environment, { durableStore, instrumentation, useRevivingSt
|
|
|
621
618
|
try {
|
|
622
619
|
const entries = await durableStore.getEntries(adapterContextKeysFromDifferentInstance, AdapterContextSegment);
|
|
623
620
|
if (entries !== undefined) {
|
|
624
|
-
const entryKeys = keys$
|
|
621
|
+
const entryKeys = keys$2(entries);
|
|
625
622
|
for (let i = 0, len = entryKeys.length; i < len; i++) {
|
|
626
623
|
const entryKey = entryKeys[i];
|
|
627
624
|
const entry = entries[entryKey];
|
|
@@ -656,7 +653,7 @@ function makeDurable(environment, { durableStore, instrumentation, useRevivingSt
|
|
|
656
653
|
if (filteredKeys.length > 0) {
|
|
657
654
|
const entries = await durableStore.getMetadata(filteredKeys, DefaultDurableSegment);
|
|
658
655
|
if (entries !== undefined) {
|
|
659
|
-
const entryKeys = keys$
|
|
656
|
+
const entryKeys = keys$2(entries);
|
|
660
657
|
for (let i = 0, len = entryKeys.length; i < len; i++) {
|
|
661
658
|
const entryKey = entryKeys[i];
|
|
662
659
|
const { metadata } = entries[entryKey];
|
|
@@ -1028,7 +1025,7 @@ function makeDurable(environment, { durableStore, instrumentation, useRevivingSt
|
|
|
1028
1025
|
validateNotDisposed();
|
|
1029
1026
|
const entryKeys = keys$1.map(serializeStructuredKey);
|
|
1030
1027
|
const entries = await durableStore.getEntries(entryKeys, DefaultDurableSegment);
|
|
1031
|
-
if (entries === undefined || keys$
|
|
1028
|
+
if (entries === undefined || keys$2(entries).length === 0) {
|
|
1032
1029
|
return environment.notifyStoreUpdateAvailable(keys$1);
|
|
1033
1030
|
}
|
|
1034
1031
|
const now = Date.now();
|
|
@@ -1080,7 +1077,7 @@ function makeDurable(environment, { durableStore, instrumentation, useRevivingSt
|
|
|
1080
1077
|
type: 'stale-while-revalidate',
|
|
1081
1078
|
staleDurationSeconds: Number.MAX_SAFE_INTEGER,
|
|
1082
1079
|
});
|
|
1083
|
-
return create$
|
|
1080
|
+
return create$2(environment, {
|
|
1084
1081
|
publishStoreMetadata: { value: publishStoreMetadata },
|
|
1085
1082
|
storeIngest: { value: storeIngest },
|
|
1086
1083
|
storeIngestError: { value: storeIngestError },
|
|
@@ -1110,8 +1107,8 @@ function makeDurable(environment, { durableStore, instrumentation, useRevivingSt
|
|
|
1110
1107
|
});
|
|
1111
1108
|
}
|
|
1112
1109
|
|
|
1113
|
-
const { keys: keys$
|
|
1114
|
-
const { stringify
|
|
1110
|
+
const { keys: keys$1, create: create$1, assign: assign$1, entries: entries$1, values: values$1 } = Object;
|
|
1111
|
+
const { stringify, parse } = JSON;
|
|
1115
1112
|
|
|
1116
1113
|
function selectColumnsFromTableWhereKeyIn(columnNames, table, keyColumnName, whereIn) {
|
|
1117
1114
|
const paramList = whereIn.map(() => '?').join(',');
|
|
@@ -1152,7 +1149,7 @@ class LdsDataTable {
|
|
|
1152
1149
|
const [key, stringifiedMetadata] = row;
|
|
1153
1150
|
if (stringifiedMetadata !== undefined) {
|
|
1154
1151
|
entries[key] = {
|
|
1155
|
-
metadata: parse
|
|
1152
|
+
metadata: parse(stringifiedMetadata),
|
|
1156
1153
|
};
|
|
1157
1154
|
}
|
|
1158
1155
|
return entries;
|
|
@@ -1177,10 +1174,10 @@ class LdsDataTable {
|
|
|
1177
1174
|
},
|
|
1178
1175
|
conflictColumns: this.conflictColumnNames,
|
|
1179
1176
|
columns: this.columnNames,
|
|
1180
|
-
rows: keys$
|
|
1177
|
+
rows: keys$1(entries).reduce((rows, key) => {
|
|
1181
1178
|
const entry = entries[key];
|
|
1182
1179
|
const { data, metadata } = entry;
|
|
1183
|
-
const row = [key, stringify
|
|
1180
|
+
const row = [key, stringify(data), metadata ? stringify(metadata) : null];
|
|
1184
1181
|
rows.push(row);
|
|
1185
1182
|
return rows;
|
|
1186
1183
|
}, []),
|
|
@@ -1196,9 +1193,9 @@ class LdsDataTable {
|
|
|
1196
1193
|
type: 'setMetadata',
|
|
1197
1194
|
},
|
|
1198
1195
|
columns: [COLUMN_NAME_METADATA$1],
|
|
1199
|
-
values: keys$
|
|
1196
|
+
values: keys$1(entries).reduce((values, key) => {
|
|
1200
1197
|
const { metadata } = entries[key];
|
|
1201
|
-
const row = [metadata ? stringify
|
|
1198
|
+
const row = [metadata ? stringify(metadata) : null];
|
|
1202
1199
|
values[key] = row;
|
|
1203
1200
|
return values;
|
|
1204
1201
|
}, {}),
|
|
@@ -1208,10 +1205,10 @@ class LdsDataTable {
|
|
|
1208
1205
|
return sqliteResult.rows.reduce((entries, row) => {
|
|
1209
1206
|
const [key, stringifiedData, stringifiedMetadata] = row;
|
|
1210
1207
|
const durableStoreEntry = {
|
|
1211
|
-
data: parse
|
|
1208
|
+
data: parse(stringifiedData),
|
|
1212
1209
|
};
|
|
1213
1210
|
if (stringifiedMetadata !== null) {
|
|
1214
|
-
durableStoreEntry.metadata = parse
|
|
1211
|
+
durableStoreEntry.metadata = parse(stringifiedMetadata);
|
|
1215
1212
|
}
|
|
1216
1213
|
entries[key] = durableStoreEntry;
|
|
1217
1214
|
return entries;
|
|
@@ -1261,7 +1258,7 @@ class LdsInternalDataTable {
|
|
|
1261
1258
|
const [key, stringifiedMetadata] = row;
|
|
1262
1259
|
if (stringifiedMetadata !== undefined) {
|
|
1263
1260
|
entries[key] = {
|
|
1264
|
-
metadata: parse
|
|
1261
|
+
metadata: parse(stringifiedMetadata),
|
|
1265
1262
|
};
|
|
1266
1263
|
}
|
|
1267
1264
|
return entries;
|
|
@@ -1286,12 +1283,12 @@ class LdsInternalDataTable {
|
|
|
1286
1283
|
},
|
|
1287
1284
|
conflictColumns: this.conflictColumnNames,
|
|
1288
1285
|
columns: this.columnNames,
|
|
1289
|
-
rows: keys$
|
|
1286
|
+
rows: keys$1(entries).reduce((rows, key) => {
|
|
1290
1287
|
const entry = entries[key];
|
|
1291
1288
|
const { data, metadata } = entry;
|
|
1292
|
-
const row = [key, stringify
|
|
1289
|
+
const row = [key, stringify(data)];
|
|
1293
1290
|
if (metadata) {
|
|
1294
|
-
row.push(stringify
|
|
1291
|
+
row.push(stringify(metadata));
|
|
1295
1292
|
}
|
|
1296
1293
|
else {
|
|
1297
1294
|
row.push(null);
|
|
@@ -1312,21 +1309,21 @@ class LdsInternalDataTable {
|
|
|
1312
1309
|
type: 'setMetadata',
|
|
1313
1310
|
},
|
|
1314
1311
|
columns: [COLUMN_NAME_METADATA],
|
|
1315
|
-
values: keys$
|
|
1312
|
+
values: keys$1(entries).reduce((values, key) => {
|
|
1316
1313
|
const { metadata } = entries[key];
|
|
1317
|
-
const row = [metadata ? stringify
|
|
1314
|
+
const row = [metadata ? stringify(metadata) : null];
|
|
1318
1315
|
values[key] = row;
|
|
1319
1316
|
return values;
|
|
1320
1317
|
}, {}),
|
|
1321
1318
|
};
|
|
1322
1319
|
}
|
|
1323
1320
|
metadataToUpdateSQLQueries(entries, segment) {
|
|
1324
|
-
return keys$
|
|
1321
|
+
return keys$1(entries).reduce((accu, key) => {
|
|
1325
1322
|
const { metadata } = entries[key];
|
|
1326
1323
|
if (metadata !== undefined) {
|
|
1327
1324
|
accu.push({
|
|
1328
1325
|
sql: `UPDATE ${this.tableName} SET ${COLUMN_NAME_METADATA} = ? WHERE (${COLUMN_NAME_KEY$1} IS ? AND ${COLUMN_NAME_NAMESPACE} IS ?)`,
|
|
1329
|
-
params: [stringify
|
|
1326
|
+
params: [stringify(metadata), key, segment],
|
|
1330
1327
|
change: {
|
|
1331
1328
|
ids: [key],
|
|
1332
1329
|
segment,
|
|
@@ -1342,10 +1339,10 @@ class LdsInternalDataTable {
|
|
|
1342
1339
|
return sqliteResult.rows.reduce((entries, row) => {
|
|
1343
1340
|
const [key, stringifiedData, stringifiedMetadata] = row;
|
|
1344
1341
|
const durableStoreEntry = {
|
|
1345
|
-
data: parse
|
|
1342
|
+
data: parse(stringifiedData),
|
|
1346
1343
|
};
|
|
1347
1344
|
if (stringifiedMetadata !== null) {
|
|
1348
|
-
durableStoreEntry.metadata = parse
|
|
1345
|
+
durableStoreEntry.metadata = parse(stringifiedMetadata);
|
|
1349
1346
|
}
|
|
1350
1347
|
entries[key] = durableStoreEntry;
|
|
1351
1348
|
return entries;
|
|
@@ -1392,7 +1389,7 @@ class NimbusSqliteStore {
|
|
|
1392
1389
|
return this.getTable(segment).getAll(segment);
|
|
1393
1390
|
}
|
|
1394
1391
|
setEntries(entries, segment) {
|
|
1395
|
-
if (keys$
|
|
1392
|
+
if (keys$1(entries).length === 0) {
|
|
1396
1393
|
return Promise.resolve();
|
|
1397
1394
|
}
|
|
1398
1395
|
const table = this.getTable(segment);
|
|
@@ -1400,7 +1397,7 @@ class NimbusSqliteStore {
|
|
|
1400
1397
|
return this.batchOperationAsPromise([upsertOperation]);
|
|
1401
1398
|
}
|
|
1402
1399
|
setMetadata(entries, segment) {
|
|
1403
|
-
if (keys$
|
|
1400
|
+
if (keys$1(entries).length === 0) {
|
|
1404
1401
|
return Promise.resolve();
|
|
1405
1402
|
}
|
|
1406
1403
|
const table = this.getTable(segment);
|
|
@@ -1419,13 +1416,13 @@ class NimbusSqliteStore {
|
|
|
1419
1416
|
batchOperations(operations) {
|
|
1420
1417
|
const sqliteOperations = operations.reduce((acc, cur) => {
|
|
1421
1418
|
if (cur.type === 'setEntries') {
|
|
1422
|
-
if (keys$
|
|
1419
|
+
if (keys$1(cur.entries).length > 0) {
|
|
1423
1420
|
const table = this.getTable(cur.segment);
|
|
1424
1421
|
acc.push(table.entriesToUpsertOperations(cur.entries, cur.segment));
|
|
1425
1422
|
}
|
|
1426
1423
|
}
|
|
1427
1424
|
else if (cur.type === 'setMetadata') {
|
|
1428
|
-
if (keys$
|
|
1425
|
+
if (keys$1(cur.entries).length > 0) {
|
|
1429
1426
|
const table = this.getTable(cur.segment);
|
|
1430
1427
|
if (this.supportsBatchUpdates) {
|
|
1431
1428
|
acc.push(table.metadataToUpdateOperations(cur.entries, cur.segment));
|
|
@@ -1662,2151 +1659,6 @@ var DraftQueueOperationType;
|
|
|
1662
1659
|
DraftQueueOperationType["QueueStopped"] = "stopped";
|
|
1663
1660
|
})(DraftQueueOperationType || (DraftQueueOperationType = {}));
|
|
1664
1661
|
|
|
1665
|
-
function serializeFieldArguments(argumentNodes, variables) {
|
|
1666
|
-
const mutableArgumentNodes = Object.assign([], argumentNodes);
|
|
1667
|
-
return `args__(${mutableArgumentNodes
|
|
1668
|
-
.sort((a, b) => {
|
|
1669
|
-
const aName = a.name.value.toUpperCase();
|
|
1670
|
-
const bName = b.name.value.toUpperCase();
|
|
1671
|
-
return aName < bName ? -1 : aName > bName ? 1 : 0;
|
|
1672
|
-
})
|
|
1673
|
-
.map((node) => serializeArgNode(node, variables))
|
|
1674
|
-
.join('::')})`;
|
|
1675
|
-
}
|
|
1676
|
-
function serializeArgNode(argumentNode, variables) {
|
|
1677
|
-
const argName = argumentNode.name.value;
|
|
1678
|
-
return `${argName}:${serializeValueNode(argumentNode.value, variables)}`;
|
|
1679
|
-
}
|
|
1680
|
-
function serializeValueNode(valueNode, variables) {
|
|
1681
|
-
switch (valueNode.kind) {
|
|
1682
|
-
case 'BooleanValue':
|
|
1683
|
-
return valueNode.value + '';
|
|
1684
|
-
case 'IntValue':
|
|
1685
|
-
case 'FloatValue':
|
|
1686
|
-
case 'EnumValue':
|
|
1687
|
-
case 'StringValue':
|
|
1688
|
-
return valueNode.value;
|
|
1689
|
-
case 'ListValue': {
|
|
1690
|
-
const mutableValueNodeList = Object.assign([], valueNode.values);
|
|
1691
|
-
return mutableValueNodeList
|
|
1692
|
-
.sort((a, b) => {
|
|
1693
|
-
const aVal = serializeValueNode(a, variables).toUpperCase();
|
|
1694
|
-
const bVal = serializeValueNode(b, variables).toUpperCase();
|
|
1695
|
-
return aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
|
|
1696
|
-
})
|
|
1697
|
-
.map((val, i) => `${serializeValueNode(val, variables)}[${i}]`)
|
|
1698
|
-
.join(',');
|
|
1699
|
-
}
|
|
1700
|
-
case 'Variable': {
|
|
1701
|
-
const variableValue = variables[valueNode.name.value];
|
|
1702
|
-
return typeof variableValue === 'string'
|
|
1703
|
-
? variableValue
|
|
1704
|
-
: JSON.stringify(variableValue);
|
|
1705
|
-
}
|
|
1706
|
-
case 'NullValue':
|
|
1707
|
-
return 'null';
|
|
1708
|
-
case 'ObjectValue': {
|
|
1709
|
-
const mutableFieldNodeList = Object.assign([], valueNode.fields);
|
|
1710
|
-
return mutableFieldNodeList
|
|
1711
|
-
.sort((a, b) => {
|
|
1712
|
-
const aName = a.name.value.toUpperCase();
|
|
1713
|
-
const bName = b.name.value.toUpperCase();
|
|
1714
|
-
return aName < bName ? -1 : aName > bName ? 1 : 0;
|
|
1715
|
-
})
|
|
1716
|
-
.map((field) => field.name.value + ':' + serializeValueNode(field.value, variables))
|
|
1717
|
-
.join(',');
|
|
1718
|
-
}
|
|
1719
|
-
}
|
|
1720
|
-
}
|
|
1721
|
-
|
|
1722
|
-
function serializeOperationNode(operationNode, variables, fragmentMap) {
|
|
1723
|
-
return `${serializeSelectionSet(operationNode.selectionSet, variables, fragmentMap)}`;
|
|
1724
|
-
}
|
|
1725
|
-
function serializeSelectionSet(selectionSetNode, variables, fragmentMap) {
|
|
1726
|
-
return `${selectionSetNode.selections
|
|
1727
|
-
.map((selection) => serializeSelectionNode(selection, variables, fragmentMap))
|
|
1728
|
-
.join()}`;
|
|
1729
|
-
}
|
|
1730
|
-
/**
|
|
1731
|
-
*
|
|
1732
|
-
* @description This function takes a GraphQL SelectionNode from an AST and serializes it in a stable way, so we can
|
|
1733
|
-
* use it for property names and Store keys.
|
|
1734
|
-
* @param selectionNode
|
|
1735
|
-
* @param variables
|
|
1736
|
-
* @param fragmentMap
|
|
1737
|
-
* @returns string
|
|
1738
|
-
*/
|
|
1739
|
-
function serializeSelectionNode(selectionNode, variables, fragmentMap) {
|
|
1740
|
-
switch (selectionNode.kind) {
|
|
1741
|
-
case 'Field': {
|
|
1742
|
-
const hasArguments = selectionNode.arguments !== undefined && selectionNode.arguments.length > 0;
|
|
1743
|
-
const argumentSuffix = hasArguments
|
|
1744
|
-
? `__${serializeFieldArguments(selectionNode.arguments, variables)}`
|
|
1745
|
-
: '';
|
|
1746
|
-
return `${selectionNode.name.value}${argumentSuffix}`;
|
|
1747
|
-
}
|
|
1748
|
-
case 'FragmentSpread': {
|
|
1749
|
-
const fragment = fragmentMap[selectionNode.name.value];
|
|
1750
|
-
return fragment === undefined
|
|
1751
|
-
? selectionNode.name.value
|
|
1752
|
-
: serializeSelectionSet(fragment.selectionSet, variables, fragmentMap);
|
|
1753
|
-
}
|
|
1754
|
-
case 'InlineFragment':
|
|
1755
|
-
return serializeSelectionSet(selectionNode.selectionSet, variables, fragmentMap);
|
|
1756
|
-
}
|
|
1757
|
-
}
|
|
1758
|
-
function buildQueryTypeStringKey(args) {
|
|
1759
|
-
const { keyPrefix, schemaName, queryTypeName, operationNode, variables, fragmentMap } = args;
|
|
1760
|
-
return `${keyPrefix}::${schemaName}::${queryTypeName}[${serializeOperationNode(operationNode, variables, fragmentMap)}]`;
|
|
1761
|
-
}
|
|
1762
|
-
|
|
1763
|
-
/**
|
|
1764
|
-
* Copyright (c) 2022, Salesforce, Inc.,
|
|
1765
|
-
* All rights reserved.
|
|
1766
|
-
* For full license text, see the LICENSE.txt file
|
|
1767
|
-
*/
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
const { create: create$1, keys: keys$1, values: values$1, entries: entries$1, assign: assign$1 } = Object;
|
|
1771
|
-
const { stringify, parse } = JSON;
|
|
1772
|
-
const { isArray, from } = Array;
|
|
1773
|
-
|
|
1774
|
-
function removeUndefined(array) {
|
|
1775
|
-
return array.reduce((accu, item) => {
|
|
1776
|
-
if (item !== undefined) {
|
|
1777
|
-
accu.push(item);
|
|
1778
|
-
}
|
|
1779
|
-
return accu;
|
|
1780
|
-
}, []);
|
|
1781
|
-
}
|
|
1782
|
-
|
|
1783
|
-
var DateLiteral;
|
|
1784
|
-
(function (DateLiteral) {
|
|
1785
|
-
DateLiteral["NEXT_YEAR"] = "NEXT_YEAR";
|
|
1786
|
-
DateLiteral["LAST_WEEK"] = "LAST_WEEK";
|
|
1787
|
-
DateLiteral["THIS_MONTH"] = "THIS_MONTH";
|
|
1788
|
-
DateLiteral["THIS_QUARTER"] = "THIS_QUARTER";
|
|
1789
|
-
DateLiteral["LAST_MONTH"] = "LAST_MONTH";
|
|
1790
|
-
DateLiteral["LAST_90_DAYS"] = "LAST_90_DAYS";
|
|
1791
|
-
DateLiteral["LAST_QUARTER"] = "LAST_QUARTER";
|
|
1792
|
-
DateLiteral["THIS_YEAR"] = "THIS_YEAR";
|
|
1793
|
-
DateLiteral["NEXT_QUARTER"] = "NEXT_QUARTER";
|
|
1794
|
-
DateLiteral["NEXT_MONTH"] = "NEXT_MONTH";
|
|
1795
|
-
DateLiteral["TOMORROW"] = "TOMORROW";
|
|
1796
|
-
DateLiteral["TODAY"] = "TODAY";
|
|
1797
|
-
DateLiteral["LAST_YEAR"] = "LAST_YEAR";
|
|
1798
|
-
DateLiteral["NEXT_90_DAYS"] = "NEXT_90_DAYS";
|
|
1799
|
-
DateLiteral["THIS_WEEK"] = "THIS_WEEK";
|
|
1800
|
-
DateLiteral["YESTERDAY"] = "YESTERDAY";
|
|
1801
|
-
DateLiteral["NEXT_WEEK"] = "NEXT_WEEK";
|
|
1802
|
-
})(DateLiteral || (DateLiteral = {}));
|
|
1803
|
-
// ranged adjusted to be between now and the end of the day,
|
|
1804
|
-
// you cannot do a straight = comparison in SQL
|
|
1805
|
-
const TODAY_RANGE = {
|
|
1806
|
-
lowerBound: `date('now')`,
|
|
1807
|
-
upperBound: `date('now', '+1 day', '-0.001 seconds')`,
|
|
1808
|
-
};
|
|
1809
|
-
// ranged adjusted to be between today +1 day and +2 days -.001 seconds to be the very end of tomorrows date
|
|
1810
|
-
// you cannot do a straight = comparison in SQL
|
|
1811
|
-
const TOMORROW_RANGE = {
|
|
1812
|
-
lowerBound: `date('now', '+1 day')`,
|
|
1813
|
-
upperBound: `date('now', '+2 day', '-0.001 seconds')`,
|
|
1814
|
-
};
|
|
1815
|
-
const YESTERDAY_RANGE = {
|
|
1816
|
-
lowerBound: `date('now', '-1 day')`,
|
|
1817
|
-
upperBound: `date('now', '-0.001 seconds')`,
|
|
1818
|
-
};
|
|
1819
|
-
const THIS_WEEK_RANGE = {
|
|
1820
|
-
lowerBound: `date('now', 'weekday 0', '-7 days')`,
|
|
1821
|
-
upperBound: `date('now', 'weekday 0', '-1 day')`,
|
|
1822
|
-
};
|
|
1823
|
-
const LAST_WEEK_RANGE = {
|
|
1824
|
-
lowerBound: `date('now', 'weekday 0', '-14 days')`,
|
|
1825
|
-
upperBound: `date('now', 'weekday 0', '-8 days')`,
|
|
1826
|
-
};
|
|
1827
|
-
const NEXT_WEEK_RANGE = {
|
|
1828
|
-
lowerBound: `date('now', 'weekday 0')`,
|
|
1829
|
-
upperBound: `date('now', 'weekday 0', '+6 days')`,
|
|
1830
|
-
};
|
|
1831
|
-
const THIS_MONTH_RANGE = {
|
|
1832
|
-
lowerBound: `date('now', 'start of month')`,
|
|
1833
|
-
upperBound: `date('now', 'start of month', '1 month', '0 day')`,
|
|
1834
|
-
};
|
|
1835
|
-
const LAST_MONTH_RANGE = {
|
|
1836
|
-
lowerBound: `date('now', 'start of month', '-1 month')`,
|
|
1837
|
-
upperBound: `date('now', 'start of month', '0 day')`,
|
|
1838
|
-
};
|
|
1839
|
-
const NEXT_MONTH_RANGE = {
|
|
1840
|
-
lowerBound: `date('now', 'start of month', '+1 month')`,
|
|
1841
|
-
// -1 day is the day before the end of the month
|
|
1842
|
-
// this needs to be 0 day
|
|
1843
|
-
upperBound: `date('now', 'start of month', '+2 month', '0 day')`,
|
|
1844
|
-
};
|
|
1845
|
-
const THIS_QUARTER_RANGE = () => {
|
|
1846
|
-
return computeQuarterDateRange(DateLiteral.THIS_QUARTER);
|
|
1847
|
-
};
|
|
1848
|
-
const LAST_QUARTER_RANGE = () => {
|
|
1849
|
-
return computeQuarterDateRange(DateLiteral.LAST_QUARTER);
|
|
1850
|
-
};
|
|
1851
|
-
const NEXT_QUARTER_RANGE = () => {
|
|
1852
|
-
return computeQuarterDateRange(DateLiteral.NEXT_QUARTER);
|
|
1853
|
-
};
|
|
1854
|
-
const THIS_YEAR_RANGE = {
|
|
1855
|
-
lowerBound: `date('now', 'start of year')`,
|
|
1856
|
-
upperBound: `date('now', 'start of month', '1 year', '-1 day')`,
|
|
1857
|
-
};
|
|
1858
|
-
const LAST_YEAR_RANGE = {
|
|
1859
|
-
lowerBound: `date('now', 'start of year', '-1 year')`,
|
|
1860
|
-
upperBound: `date('now', 'start of year', '-1 day')`,
|
|
1861
|
-
};
|
|
1862
|
-
const NEXT_YEAR_RANGE = {
|
|
1863
|
-
lowerBound: `date('now', 'start of year', '+1 year')`,
|
|
1864
|
-
upperBound: `date('now', 'start of year', '+2 years', '-1 day')`,
|
|
1865
|
-
};
|
|
1866
|
-
const LAST_90_DAYS_RANGE = {
|
|
1867
|
-
lowerBound: `date('now', '-90 days')`,
|
|
1868
|
-
upperBound: `date('now')`,
|
|
1869
|
-
};
|
|
1870
|
-
const NEXT_90_DAYS_RANGE = {
|
|
1871
|
-
lowerBound: `date('now', '+1 day')`,
|
|
1872
|
-
upperBound: `date('now', '+90 days')`,
|
|
1873
|
-
};
|
|
1874
|
-
// querter start date in UTC
|
|
1875
|
-
function quarterStart(date) {
|
|
1876
|
-
let month = Math.floor(date.getUTCMonth() / 3) * 3 + 1;
|
|
1877
|
-
const year = date.getUTCFullYear();
|
|
1878
|
-
return month === 10 ? `${year}-${month}-01` : `${year}-0${month}-01`;
|
|
1879
|
-
}
|
|
1880
|
-
function computeQuarterDateRange(enumType) {
|
|
1881
|
-
const curQuarterStart = quarterStart(new Date());
|
|
1882
|
-
switch (enumType) {
|
|
1883
|
-
case DateLiteral.LAST_QUARTER:
|
|
1884
|
-
return {
|
|
1885
|
-
lowerBound: `date('${curQuarterStart}', '-3 months')`,
|
|
1886
|
-
// -1 days would set the day before the end of the quarter
|
|
1887
|
-
// 0 days sets it to the end of the month
|
|
1888
|
-
upperBound: `date('${curQuarterStart}', '0 days')`,
|
|
1889
|
-
};
|
|
1890
|
-
case DateLiteral.THIS_QUARTER:
|
|
1891
|
-
return {
|
|
1892
|
-
lowerBound: `date('${curQuarterStart}')`,
|
|
1893
|
-
// -1 days would set the day before the end of the quarter
|
|
1894
|
-
// 0 days sets it to the end of the month
|
|
1895
|
-
upperBound: `date('${curQuarterStart}', '3 months', '0 days')`,
|
|
1896
|
-
};
|
|
1897
|
-
case DateLiteral.NEXT_QUARTER:
|
|
1898
|
-
return {
|
|
1899
|
-
lowerBound: `date('${curQuarterStart}', '+3 months')`,
|
|
1900
|
-
// -1 days would set the day before the end of the quarter
|
|
1901
|
-
// 0 days sets it to the end of the month
|
|
1902
|
-
upperBound: `date('${curQuarterStart}', '+6 months', '0 days')`,
|
|
1903
|
-
};
|
|
1904
|
-
}
|
|
1905
|
-
}
|
|
1906
|
-
function dateLiteralRange(literal) {
|
|
1907
|
-
switch (literal) {
|
|
1908
|
-
case DateLiteral.NEXT_YEAR: {
|
|
1909
|
-
return NEXT_YEAR_RANGE;
|
|
1910
|
-
}
|
|
1911
|
-
case DateLiteral.LAST_WEEK: {
|
|
1912
|
-
return LAST_WEEK_RANGE;
|
|
1913
|
-
}
|
|
1914
|
-
case DateLiteral.THIS_MONTH: {
|
|
1915
|
-
return THIS_MONTH_RANGE;
|
|
1916
|
-
}
|
|
1917
|
-
case DateLiteral.THIS_QUARTER: {
|
|
1918
|
-
return THIS_QUARTER_RANGE();
|
|
1919
|
-
}
|
|
1920
|
-
case DateLiteral.LAST_MONTH: {
|
|
1921
|
-
return LAST_MONTH_RANGE;
|
|
1922
|
-
}
|
|
1923
|
-
case DateLiteral.LAST_90_DAYS: {
|
|
1924
|
-
return LAST_90_DAYS_RANGE;
|
|
1925
|
-
}
|
|
1926
|
-
case DateLiteral.LAST_QUARTER: {
|
|
1927
|
-
return LAST_QUARTER_RANGE();
|
|
1928
|
-
}
|
|
1929
|
-
case DateLiteral.THIS_YEAR: {
|
|
1930
|
-
return THIS_YEAR_RANGE;
|
|
1931
|
-
}
|
|
1932
|
-
case DateLiteral.NEXT_QUARTER: {
|
|
1933
|
-
return NEXT_QUARTER_RANGE();
|
|
1934
|
-
}
|
|
1935
|
-
case DateLiteral.NEXT_MONTH: {
|
|
1936
|
-
return NEXT_MONTH_RANGE;
|
|
1937
|
-
}
|
|
1938
|
-
case DateLiteral.TOMORROW: {
|
|
1939
|
-
return TOMORROW_RANGE;
|
|
1940
|
-
}
|
|
1941
|
-
case DateLiteral.TODAY: {
|
|
1942
|
-
return TODAY_RANGE;
|
|
1943
|
-
}
|
|
1944
|
-
case DateLiteral.LAST_YEAR: {
|
|
1945
|
-
return LAST_YEAR_RANGE;
|
|
1946
|
-
}
|
|
1947
|
-
case DateLiteral.NEXT_90_DAYS: {
|
|
1948
|
-
return NEXT_90_DAYS_RANGE;
|
|
1949
|
-
}
|
|
1950
|
-
case DateLiteral.THIS_WEEK: {
|
|
1951
|
-
return THIS_WEEK_RANGE;
|
|
1952
|
-
}
|
|
1953
|
-
case DateLiteral.YESTERDAY: {
|
|
1954
|
-
return YESTERDAY_RANGE;
|
|
1955
|
-
}
|
|
1956
|
-
case DateLiteral.NEXT_WEEK: {
|
|
1957
|
-
return NEXT_WEEK_RANGE;
|
|
1958
|
-
}
|
|
1959
|
-
default: {
|
|
1960
|
-
// eslint-disable-next-line @salesforce/lds/no-error-in-production
|
|
1961
|
-
throw new Error(`${literal} is currently not supported in graphql evaluation`);
|
|
1962
|
-
}
|
|
1963
|
-
}
|
|
1964
|
-
}
|
|
1965
|
-
function dateLiteral(literal, op, field, alias) {
|
|
1966
|
-
let operator = op;
|
|
1967
|
-
let value;
|
|
1968
|
-
let bindings = [];
|
|
1969
|
-
const { lowerBound, upperBound } = dateLiteralRange(literal);
|
|
1970
|
-
switch (operator) {
|
|
1971
|
-
case '=': {
|
|
1972
|
-
operator = 'BETWEEN';
|
|
1973
|
-
value = `${lowerBound} AND ${upperBound}`;
|
|
1974
|
-
break;
|
|
1975
|
-
}
|
|
1976
|
-
case '!=': {
|
|
1977
|
-
operator = 'NOT BETWEEN';
|
|
1978
|
-
value = `${lowerBound} AND ${upperBound}`;
|
|
1979
|
-
break;
|
|
1980
|
-
}
|
|
1981
|
-
case '<=':
|
|
1982
|
-
case '>': {
|
|
1983
|
-
value = upperBound;
|
|
1984
|
-
break;
|
|
1985
|
-
}
|
|
1986
|
-
case '<':
|
|
1987
|
-
case '>=': {
|
|
1988
|
-
value = lowerBound;
|
|
1989
|
-
break;
|
|
1990
|
-
}
|
|
1991
|
-
default: {
|
|
1992
|
-
// eslint-disable-next-line @salesforce/lds/no-error-in-production
|
|
1993
|
-
throw new Error(`Operator ${operator} is not supported in Date Literals for graphql evaluation`);
|
|
1994
|
-
}
|
|
1995
|
-
}
|
|
1996
|
-
return {
|
|
1997
|
-
leftPath: `$.fields.${field.apiName}.value`,
|
|
1998
|
-
operator,
|
|
1999
|
-
value,
|
|
2000
|
-
bindings,
|
|
2001
|
-
type: PredicateType.single,
|
|
2002
|
-
alias,
|
|
2003
|
-
dataType: field.dataType,
|
|
2004
|
-
isCaseSensitive: false,
|
|
2005
|
-
};
|
|
2006
|
-
}
|
|
2007
|
-
|
|
2008
|
-
var DateRange;
|
|
2009
|
-
(function (DateRange) {
|
|
2010
|
-
DateRange["last_n_days"] = "last_n_days";
|
|
2011
|
-
DateRange["next_n_days"] = "next_n_days";
|
|
2012
|
-
DateRange["n_days_ago"] = "n_days_ago";
|
|
2013
|
-
DateRange["last_n_weeks"] = "last_n_weeks";
|
|
2014
|
-
DateRange["next_n_weeks"] = "next_n_weeks";
|
|
2015
|
-
DateRange["n_weeks_ago"] = "n_weeks_ago";
|
|
2016
|
-
DateRange["last_n_months"] = "last_n_months";
|
|
2017
|
-
DateRange["next_n_months"] = "next_n_months";
|
|
2018
|
-
DateRange["n_months_ago"] = "n_months_ago";
|
|
2019
|
-
DateRange["last_n_quarters"] = "last_n_quarters";
|
|
2020
|
-
DateRange["next_n_quarters"] = "next_n_quarters";
|
|
2021
|
-
DateRange["n_quarters_ago"] = "n_quarters_ago";
|
|
2022
|
-
DateRange["last_n_fiscal_quarters"] = "last_n_fiscal_quarters";
|
|
2023
|
-
DateRange["next_n_fiscal_quarters"] = "next_n_fiscal_quarters";
|
|
2024
|
-
DateRange["last_n_years"] = "last_n_years";
|
|
2025
|
-
DateRange["next_n_years"] = "next_n_years";
|
|
2026
|
-
DateRange["n_years_ago"] = "n_years_ago";
|
|
2027
|
-
DateRange["last_n_fiscal_years"] = "last_n_fiscal_years";
|
|
2028
|
-
DateRange["next_n_fiscal_years"] = "next_n_fiscal_years";
|
|
2029
|
-
})(DateRange || (DateRange = {}));
|
|
2030
|
-
function dateTimePredicate(input, operator, field, alias) {
|
|
2031
|
-
const { value, literal, range } = input;
|
|
2032
|
-
const predicate = {
|
|
2033
|
-
leftPath: `$.fields.${field.apiName}.value`,
|
|
2034
|
-
operator,
|
|
2035
|
-
value,
|
|
2036
|
-
alias,
|
|
2037
|
-
dataType: field.dataType,
|
|
2038
|
-
type: PredicateType.single,
|
|
2039
|
-
isCaseSensitive: false,
|
|
2040
|
-
};
|
|
2041
|
-
if (value !== undefined) {
|
|
2042
|
-
if (value === null) {
|
|
2043
|
-
switch (predicate.operator) {
|
|
2044
|
-
case '=':
|
|
2045
|
-
predicate.operator = 'IS';
|
|
2046
|
-
break;
|
|
2047
|
-
case '!=':
|
|
2048
|
-
predicate.operator = 'IS NOT';
|
|
2049
|
-
break;
|
|
2050
|
-
default:
|
|
2051
|
-
// eslint-disable-next-line @salesforce/lds/no-error-in-production
|
|
2052
|
-
throw new Error(`Unsupported operator ${predicate.operator} with null value operand`);
|
|
2053
|
-
}
|
|
2054
|
-
predicate.value = 'NULL';
|
|
2055
|
-
}
|
|
2056
|
-
else {
|
|
2057
|
-
const dateFn = field.dataType === 'Date' ? 'date' : 'datetime';
|
|
2058
|
-
predicate.value = `${dateFn}(?)`;
|
|
2059
|
-
predicate.bindings = [value];
|
|
2060
|
-
}
|
|
2061
|
-
return predicate;
|
|
2062
|
-
}
|
|
2063
|
-
else if (literal !== undefined) {
|
|
2064
|
-
const isAvailableLiteral = values$1(DateLiteral).includes(literal);
|
|
2065
|
-
// eslint-disable-next-line @salesforce/lds/no-error-in-production
|
|
2066
|
-
if (!isAvailableLiteral)
|
|
2067
|
-
throw new Error(`${literal} is not a valid DateLiteral`);
|
|
2068
|
-
return dateLiteral(literal, operator, field, alias);
|
|
2069
|
-
}
|
|
2070
|
-
else if (range !== undefined) {
|
|
2071
|
-
return dateTimeRange(range, operator, field, alias);
|
|
2072
|
-
}
|
|
2073
|
-
// eslint-disable-next-line @salesforce/lds/no-error-in-production
|
|
2074
|
-
throw new Error(`Where filter ${stringify(input)} is not supported`);
|
|
2075
|
-
}
|
|
2076
|
-
function dateTimeRange(input, op, field, alias) {
|
|
2077
|
-
const dateFunction = field.dataType === 'DateTime' ? 'datetime' : 'date';
|
|
2078
|
-
const key = keys$1(input)[0];
|
|
2079
|
-
let operator = op;
|
|
2080
|
-
if (operator === '=')
|
|
2081
|
-
operator = 'BETWEEN';
|
|
2082
|
-
if (operator === '!=')
|
|
2083
|
-
operator = 'NOT BETWEEN';
|
|
2084
|
-
let value;
|
|
2085
|
-
let bindings = [];
|
|
2086
|
-
const { range, binding } = dateRangesFrom(key, input[key], dateFunction);
|
|
2087
|
-
switch (operator) {
|
|
2088
|
-
case '<':
|
|
2089
|
-
case '>=':
|
|
2090
|
-
value = range.lower;
|
|
2091
|
-
bindings = [binding.lower];
|
|
2092
|
-
break;
|
|
2093
|
-
case '>':
|
|
2094
|
-
case '<=':
|
|
2095
|
-
value = range.upper;
|
|
2096
|
-
bindings = [binding.upper];
|
|
2097
|
-
break;
|
|
2098
|
-
default:
|
|
2099
|
-
value = `${range.lower} AND ${range.upper}`;
|
|
2100
|
-
bindings = [binding.lower, binding.upper];
|
|
2101
|
-
break;
|
|
2102
|
-
}
|
|
2103
|
-
return {
|
|
2104
|
-
leftPath: `$.fields.${field.apiName}.value`,
|
|
2105
|
-
operator,
|
|
2106
|
-
value,
|
|
2107
|
-
bindings,
|
|
2108
|
-
type: PredicateType.single,
|
|
2109
|
-
alias,
|
|
2110
|
-
dataType: field.dataType,
|
|
2111
|
-
};
|
|
2112
|
-
}
|
|
2113
|
-
// relative date ranges defined at https://help.salesforce.com/s/articleView?id=sf.filter_dates_relative.htm&type=5
|
|
2114
|
-
function dateRangesFrom(dateRange, input, dateFunction) {
|
|
2115
|
-
// use 'start of day' to ensure the timestamp is 00:00:00 for date time values
|
|
2116
|
-
switch (dateRange) {
|
|
2117
|
-
// next_n_days range starts at tomorrow through n days
|
|
2118
|
-
case DateRange.next_n_days: {
|
|
2119
|
-
return {
|
|
2120
|
-
binding: {
|
|
2121
|
-
upper: `${input} days`,
|
|
2122
|
-
lower: '1 days',
|
|
2123
|
-
},
|
|
2124
|
-
range: {
|
|
2125
|
-
upper: `${dateFunction}('now', 'start of day', ?)`,
|
|
2126
|
-
lower: `${dateFunction}('now', 'start of day', ?)`,
|
|
2127
|
-
},
|
|
2128
|
-
};
|
|
2129
|
-
}
|
|
2130
|
-
// last_n_days starts -(n) days until today at the current second
|
|
2131
|
-
case DateRange.last_n_days: {
|
|
2132
|
-
return {
|
|
2133
|
-
binding: {
|
|
2134
|
-
upper: `0 days`,
|
|
2135
|
-
lower: `-${input} days`,
|
|
2136
|
-
},
|
|
2137
|
-
range: {
|
|
2138
|
-
upper: `${dateFunction}('now', ?)`,
|
|
2139
|
-
lower: `${dateFunction}('now', 'start of day', ?)`,
|
|
2140
|
-
},
|
|
2141
|
-
};
|
|
2142
|
-
}
|
|
2143
|
-
// n_days_ago
|
|
2144
|
-
// starts 12:00:00 am -(n) days from today
|
|
2145
|
-
// ends 11:59:99 pm -(n) days from today
|
|
2146
|
-
case DateRange.n_days_ago: {
|
|
2147
|
-
return {
|
|
2148
|
-
binding: {
|
|
2149
|
-
upper: `${-input + 1} days`,
|
|
2150
|
-
lower: `-${input} days`,
|
|
2151
|
-
},
|
|
2152
|
-
range: {
|
|
2153
|
-
upper: `${dateFunction}('now', 'start of day', ?, '-1 seconds')`,
|
|
2154
|
-
lower: `${dateFunction}('now', 'start of day', ?)`,
|
|
2155
|
-
},
|
|
2156
|
-
};
|
|
2157
|
-
}
|
|
2158
|
-
// next_n_weeks
|
|
2159
|
-
// starts at 00:00:00 the first day of the week n weeks after the current week
|
|
2160
|
-
// ends 23:59:59 of the last day of week before the current
|
|
2161
|
-
case DateRange.next_n_weeks: {
|
|
2162
|
-
const isStartOfWeek = isTodayStartOfWeek();
|
|
2163
|
-
return {
|
|
2164
|
-
binding: {
|
|
2165
|
-
// weekday FIRST_DAY_OF_WEEK goes to the next weekday of FIRST_DAY_OF_WEEK if not that current day
|
|
2166
|
-
// if that current day then it will be that day
|
|
2167
|
-
// so for the upper bound if today is the start of the week we need to add 7 more days to the value to get next week as the start
|
|
2168
|
-
upper: `${input * 7 + (isStartOfWeek ? 7 : 0)} days`,
|
|
2169
|
-
//lower bound starts out 00:00:00 of next week
|
|
2170
|
-
lower: `${isStartOfWeek ? 7 : 0} days`,
|
|
2171
|
-
},
|
|
2172
|
-
range: {
|
|
2173
|
-
upper: `${dateFunction}('now', 'weekday ${FIRST_DAY_OF_WEEK}', 'start of day', ?, '-1 seconds')`,
|
|
2174
|
-
lower: `${dateFunction}('now', 'weekday ${FIRST_DAY_OF_WEEK}', 'start of day', ?)`,
|
|
2175
|
-
},
|
|
2176
|
-
};
|
|
2177
|
-
}
|
|
2178
|
-
// last_n_weeks
|
|
2179
|
-
// starts at 00:00:00 of day 0 n weeks before the current week
|
|
2180
|
-
// ends 23:59:59 at last day of the week before the current week
|
|
2181
|
-
case DateRange.last_n_weeks: {
|
|
2182
|
-
const isStartOfWeek = isTodayStartOfWeek();
|
|
2183
|
-
return {
|
|
2184
|
-
binding: {
|
|
2185
|
-
// ending on at 23:59:59 of the week before the current week
|
|
2186
|
-
// -7 more days if today not start of week
|
|
2187
|
-
upper: `${-(isStartOfWeek ? 0 : 7)} days`,
|
|
2188
|
-
// starting on 00:00:00 of n * weeks before current week
|
|
2189
|
-
// -7 more days if today not the start of the week because weekday FIRST_DAY_OF_WEEK puts us next week day 0
|
|
2190
|
-
lower: `${-input * 7 - (isStartOfWeek ? 0 : 7)} days`,
|
|
2191
|
-
},
|
|
2192
|
-
range: {
|
|
2193
|
-
upper: `${dateFunction}('now', 'weekday ${FIRST_DAY_OF_WEEK}', 'start of day', ?, '-1 seconds')`,
|
|
2194
|
-
lower: `${dateFunction}('now', 'weekday ${FIRST_DAY_OF_WEEK}', 'start of day', ?)`,
|
|
2195
|
-
},
|
|
2196
|
-
};
|
|
2197
|
-
}
|
|
2198
|
-
// n_weeks_ago
|
|
2199
|
-
// starts 00:00:00 on day 0 of n weeks before the current week
|
|
2200
|
-
// ends 7 days after the start
|
|
2201
|
-
case DateRange.n_weeks_ago: {
|
|
2202
|
-
const isStartOfWeek = isTodayStartOfWeek();
|
|
2203
|
-
return {
|
|
2204
|
-
binding: {
|
|
2205
|
-
// end of week n weeks before current week
|
|
2206
|
-
// if today is start of the week then we need to -7 from the days given by n
|
|
2207
|
-
// so add 7 to remove a week from -(n)
|
|
2208
|
-
upper: `${-input * 7 + (isStartOfWeek ? 7 : 0)} days`,
|
|
2209
|
-
// start of the week n weeks from the current week
|
|
2210
|
-
// if today is the start of the week then -(n) will do to be the previous week, but
|
|
2211
|
-
// if today is not the start of the week we need to go back 1 more week since weekday FIRST_DAY_OF_WEEK will put us next week
|
|
2212
|
-
lower: `${-input * 7 - (isStartOfWeek ? 0 : 7)} days`,
|
|
2213
|
-
},
|
|
2214
|
-
range: {
|
|
2215
|
-
upper: `${dateFunction}('now', 'weekday ${FIRST_DAY_OF_WEEK}', 'start of day', ?, '-1 seconds')`,
|
|
2216
|
-
lower: `${dateFunction}('now', 'weekday ${FIRST_DAY_OF_WEEK}', 'start of day', ?)`,
|
|
2217
|
-
},
|
|
2218
|
-
};
|
|
2219
|
-
}
|
|
2220
|
-
// next_n_months
|
|
2221
|
-
// starts the first day of the next month at 00:00:00 and
|
|
2222
|
-
// ends end of the nth month 23:59:59
|
|
2223
|
-
case DateRange.next_n_months: {
|
|
2224
|
-
return {
|
|
2225
|
-
binding: {
|
|
2226
|
-
upper: `${input + 1} months`,
|
|
2227
|
-
lower: `1 months`,
|
|
2228
|
-
},
|
|
2229
|
-
range: {
|
|
2230
|
-
upper: `${dateFunction}('now', 'start of month', ?, '-1 seconds')`,
|
|
2231
|
-
lower: `${dateFunction}('now', 'start of month', ?)`,
|
|
2232
|
-
},
|
|
2233
|
-
};
|
|
2234
|
-
}
|
|
2235
|
-
// last_n_months
|
|
2236
|
-
// starts at 00:00:00 first day of n months before the current month and
|
|
2237
|
-
// ends at 23:59:59 the last day of the month before the current month
|
|
2238
|
-
case DateRange.last_n_months: {
|
|
2239
|
-
return {
|
|
2240
|
-
binding: {
|
|
2241
|
-
upper: `0 months`,
|
|
2242
|
-
lower: `${-input} months`,
|
|
2243
|
-
},
|
|
2244
|
-
range: {
|
|
2245
|
-
upper: `${dateFunction}('now', 'start of month', ?, '-1 seconds')`,
|
|
2246
|
-
lower: `${dateFunction}('now', 'start of month', ?)`,
|
|
2247
|
-
},
|
|
2248
|
-
};
|
|
2249
|
-
}
|
|
2250
|
-
// n_months_ago
|
|
2251
|
-
// starts at the first day of the month 00:00:00 n months ago from now
|
|
2252
|
-
// ends at 23:59:59 of n months ago
|
|
2253
|
-
case DateRange.n_months_ago: {
|
|
2254
|
-
return {
|
|
2255
|
-
binding: {
|
|
2256
|
-
// need to go 1 less month back then -1 seconds to get the end of it
|
|
2257
|
-
upper: `${-input + 1} months`,
|
|
2258
|
-
lower: `-${input} months`,
|
|
2259
|
-
},
|
|
2260
|
-
range: {
|
|
2261
|
-
upper: `${dateFunction}('now', 'start of month', ?, '-1 seconds')`,
|
|
2262
|
-
lower: `${dateFunction}('now', 'start of month', ?)`,
|
|
2263
|
-
},
|
|
2264
|
-
};
|
|
2265
|
-
}
|
|
2266
|
-
// next_n_years
|
|
2267
|
-
// starts 00:00:00 Jan 1st the year after the current year
|
|
2268
|
-
// ends Dec 31 23:59:59 of the nth year
|
|
2269
|
-
case DateRange.next_n_years: {
|
|
2270
|
-
return {
|
|
2271
|
-
binding: {
|
|
2272
|
-
upper: `+${input + 1} years`,
|
|
2273
|
-
lower: `+1 year`,
|
|
2274
|
-
},
|
|
2275
|
-
range: {
|
|
2276
|
-
upper: `${dateFunction}('now', 'start of year', ?, '-1 seconds')`,
|
|
2277
|
-
lower: `${dateFunction}('now', 'start of year', ?)`,
|
|
2278
|
-
},
|
|
2279
|
-
};
|
|
2280
|
-
}
|
|
2281
|
-
// last_n_years starts
|
|
2282
|
-
// starts 00:00:00 Jan 1 n+1 year ago and
|
|
2283
|
-
// ends dec 31 23:59:59 of the year before the current
|
|
2284
|
-
case DateRange.last_n_years: {
|
|
2285
|
-
return {
|
|
2286
|
-
binding: {
|
|
2287
|
-
upper: `-1 seconds`,
|
|
2288
|
-
lower: `-${input + 1} years`,
|
|
2289
|
-
},
|
|
2290
|
-
range: {
|
|
2291
|
-
upper: `${dateFunction}('now', 'start of year', ?)`,
|
|
2292
|
-
lower: `${dateFunction}('now', 'start of year', ?)`,
|
|
2293
|
-
},
|
|
2294
|
-
};
|
|
2295
|
-
}
|
|
2296
|
-
// n_years_ago
|
|
2297
|
-
// starts 00:00:00 Jan 1 of n years before the current year and
|
|
2298
|
-
// ends Dec 31 23:59:59 of that year
|
|
2299
|
-
case DateRange.n_years_ago: {
|
|
2300
|
-
return {
|
|
2301
|
-
binding: {
|
|
2302
|
-
upper: `-${input - 1} years`,
|
|
2303
|
-
lower: `-${input} years`,
|
|
2304
|
-
},
|
|
2305
|
-
range: {
|
|
2306
|
-
upper: `${dateFunction}('now', 'start of year', ?, '-1 seconds')`,
|
|
2307
|
-
lower: `${dateFunction}('now', 'start of year', ?)`,
|
|
2308
|
-
},
|
|
2309
|
-
};
|
|
2310
|
-
}
|
|
2311
|
-
// next_n_quarters
|
|
2312
|
-
// starts 00:00:00 first day of the quarter after the current quarter
|
|
2313
|
-
// ends 23:59:59 of the last day of n quarters in the future
|
|
2314
|
-
case DateRange.next_n_quarters: {
|
|
2315
|
-
const currentQuarterString = quarterStart(new Date());
|
|
2316
|
-
return {
|
|
2317
|
-
binding: {
|
|
2318
|
-
// add another quarter input to -1 seconds to get the end of the last day of the quarter
|
|
2319
|
-
upper: `${(input + 1) * 3} months`,
|
|
2320
|
-
lower: `3 months`,
|
|
2321
|
-
},
|
|
2322
|
-
range: {
|
|
2323
|
-
upper: `${dateFunction}('${currentQuarterString}', 'start of day', ?, '-1 seconds')`,
|
|
2324
|
-
lower: `${dateFunction}('${currentQuarterString}', 'start of day', ?)`,
|
|
2325
|
-
},
|
|
2326
|
-
};
|
|
2327
|
-
}
|
|
2328
|
-
// last_n_quarters
|
|
2329
|
-
// starts 00:00:00 the first day of n quarters ago
|
|
2330
|
-
// end 23:59:59 the last day of the quarter before the current quarter
|
|
2331
|
-
case DateRange.last_n_quarters: {
|
|
2332
|
-
const currentQuarterString = quarterStart(new Date());
|
|
2333
|
-
return {
|
|
2334
|
-
binding: {
|
|
2335
|
-
upper: `-1 seconds`,
|
|
2336
|
-
lower: `-${input * 3} months`,
|
|
2337
|
-
},
|
|
2338
|
-
range: {
|
|
2339
|
-
upper: `${dateFunction}('${currentQuarterString}', 'start of day', ?)`,
|
|
2340
|
-
lower: `${dateFunction}('${currentQuarterString}', 'start of day', ?)`,
|
|
2341
|
-
},
|
|
2342
|
-
};
|
|
2343
|
-
}
|
|
2344
|
-
// n_quarters_ago
|
|
2345
|
-
// starts 00:00:00 first day of the quarter n quarters before the current
|
|
2346
|
-
// ends 23:59:59 last day of the same quarter
|
|
2347
|
-
case DateRange.n_quarters_ago: {
|
|
2348
|
-
const currentQuarterString = quarterStart(new Date());
|
|
2349
|
-
return {
|
|
2350
|
-
binding: {
|
|
2351
|
-
// minus 1 quarter to be able to -1 seconds to get the end of the nth quarter
|
|
2352
|
-
upper: `-${(input - 1) * 3} months`,
|
|
2353
|
-
lower: `-${input * 3} months`,
|
|
2354
|
-
},
|
|
2355
|
-
range: {
|
|
2356
|
-
upper: `${dateFunction}('${currentQuarterString}', 'start of day', ?, '-1 seconds')`,
|
|
2357
|
-
lower: `${dateFunction}('${currentQuarterString}', 'start of day', ?)`,
|
|
2358
|
-
},
|
|
2359
|
-
};
|
|
2360
|
-
}
|
|
2361
|
-
default:
|
|
2362
|
-
// eslint-disable-next-line @salesforce/lds/no-error-in-production
|
|
2363
|
-
throw new Error(`DateRangeInput ${dateRange} is not supported in local evalutation`);
|
|
2364
|
-
}
|
|
2365
|
-
}
|
|
2366
|
-
function isTodayStartOfWeek() {
|
|
2367
|
-
return new Date().getDay() === FIRST_DAY_OF_WEEK;
|
|
2368
|
-
}
|
|
2369
|
-
|
|
2370
|
-
const JSON_EXTRACT_PATH_INGESTION_TIMESTAMP = '$.ingestionTimestamp';
|
|
2371
|
-
const JSON_EXTRACT_PATH_INGESTION_APINAME = '$.apiName';
|
|
2372
|
-
const JSON_EXTRACT_PATH_DRAFTS = '$.drafts';
|
|
2373
|
-
|
|
2374
|
-
const MultiPickListValueSeparator = ';';
|
|
2375
|
-
function filterToPredicates(where, recordType, alias, objectInfoMap, joins, draftFunctions) {
|
|
2376
|
-
if (!where)
|
|
2377
|
-
return [];
|
|
2378
|
-
let predicates = [];
|
|
2379
|
-
const fields = keys$1(where);
|
|
2380
|
-
for (const field of fields) {
|
|
2381
|
-
if (field === 'and' || field === 'or') {
|
|
2382
|
-
predicates.push(processCompoundPredicate(field, where[field], recordType, alias, objectInfoMap, joins));
|
|
2383
|
-
}
|
|
2384
|
-
if (field === 'not') {
|
|
2385
|
-
// get the predicates that are one level below the not field
|
|
2386
|
-
const children = filterToPredicates(where[field], recordType, alias, objectInfoMap, joins);
|
|
2387
|
-
// wrap a not predicate around all the child/children predicate
|
|
2388
|
-
const notPredicates = children.map((predicate) => {
|
|
2389
|
-
return {
|
|
2390
|
-
type: PredicateType.not,
|
|
2391
|
-
operator: 'not',
|
|
2392
|
-
children: [predicate],
|
|
2393
|
-
};
|
|
2394
|
-
});
|
|
2395
|
-
predicates = predicates.concat(notPredicates);
|
|
2396
|
-
}
|
|
2397
|
-
const fieldInfo = findFieldInfo(objectInfoMap[recordType], field);
|
|
2398
|
-
if (!fieldInfo) {
|
|
2399
|
-
continue;
|
|
2400
|
-
}
|
|
2401
|
-
// handles spanning field filter. For example, for 'OwnerId' field within 'Account' objectInfo, both 'OwnerId' and 'Owner' can be
|
|
2402
|
-
// treated as reference type, while 'Owner' field spanns since it equals to 'relationshipName'
|
|
2403
|
-
if (fieldInfo.dataType === 'Reference' && fieldInfo.relationshipName === field) {
|
|
2404
|
-
const path = {
|
|
2405
|
-
leftPath: `$.fields.${fieldInfo.apiName}.value`,
|
|
2406
|
-
rightPath: `$.id`,
|
|
2407
|
-
};
|
|
2408
|
-
const childAlias = `${alias}_${field}`;
|
|
2409
|
-
// Join can happen on polymorphic field too which has more than 1 `referenceToInfo`. If 'apiName' is undefined, it indicates a Join on polymorphic field.
|
|
2410
|
-
const join = {
|
|
2411
|
-
type: 'LEFT',
|
|
2412
|
-
alias: childAlias,
|
|
2413
|
-
to: alias,
|
|
2414
|
-
conditions: [path],
|
|
2415
|
-
apiName: fieldInfo.referenceToInfos.length === 1
|
|
2416
|
-
? fieldInfo.referenceToInfos[0].apiName
|
|
2417
|
-
: undefined,
|
|
2418
|
-
};
|
|
2419
|
-
joins.push(join);
|
|
2420
|
-
// 'RelationshipName' can be different from 'apiName'. For example, 'OwnerId' field
|
|
2421
|
-
// in 'Account' has the 'Owner' as the 'RelationshipName', the api name in 'referenceToInfos' is 'User'
|
|
2422
|
-
if (fieldInfo.referenceToInfos.length === 1) {
|
|
2423
|
-
const childRecordType = fieldInfo.referenceToInfos[0].apiName;
|
|
2424
|
-
predicates.push(...filterToPredicates(where[field], childRecordType, childAlias, objectInfoMap, joins));
|
|
2425
|
-
}
|
|
2426
|
-
else {
|
|
2427
|
-
// @W-12618378 polymorphic query sometimes does not work as expected on server. The GQL on certain entities could fail.
|
|
2428
|
-
const entityNames = keys$1(where[field]);
|
|
2429
|
-
const polyPredicatesGroups = entityNames
|
|
2430
|
-
.filter((entityName) => fieldInfo.referenceToInfos.some((referenceInfo) => referenceInfo.apiName === entityName))
|
|
2431
|
-
.map((entityName) => {
|
|
2432
|
-
return [
|
|
2433
|
-
{
|
|
2434
|
-
alias: childAlias,
|
|
2435
|
-
leftPath: JSON_EXTRACT_PATH_INGESTION_APINAME,
|
|
2436
|
-
operator: '=',
|
|
2437
|
-
value: entityName,
|
|
2438
|
-
dataType: 'String',
|
|
2439
|
-
type: PredicateType.single,
|
|
2440
|
-
isCaseSensitive: true,
|
|
2441
|
-
},
|
|
2442
|
-
...filterToPredicates(where[field][entityName], entityName, childAlias, objectInfoMap, joins),
|
|
2443
|
-
];
|
|
2444
|
-
});
|
|
2445
|
-
if (polyPredicatesGroups.length === 1) {
|
|
2446
|
-
predicates.push(...polyPredicatesGroups[0]);
|
|
2447
|
-
}
|
|
2448
|
-
else {
|
|
2449
|
-
// if polymorphic field has more than 1 entity like `{Owner: { User: {...} Group: {...}}}`, precidates for each entity(`User` and `Group`) should have
|
|
2450
|
-
// `and` operation. while at the base polymorphic field level (`Owner`), the group of compound predicates for each entity should have `or` operation.
|
|
2451
|
-
const polyCompoundPredicates = polyPredicatesGroups.map((polyEntityPredicates) => transformCompoundPredicate('and', polyEntityPredicates));
|
|
2452
|
-
predicates.push(transformCompoundPredicate('or', polyCompoundPredicates));
|
|
2453
|
-
}
|
|
2454
|
-
}
|
|
2455
|
-
}
|
|
2456
|
-
else {
|
|
2457
|
-
//`field` match the filedInfo's apiName
|
|
2458
|
-
for (const [op, value] of entries$1(where[field])) {
|
|
2459
|
-
const operator = operatorToSql(op);
|
|
2460
|
-
/**
|
|
2461
|
-
Two types ID processing might be needed. Draft ID swapping is optional, which depends on DraftFunctions existence.
|
|
2462
|
-
1. Coercing is needed when query is executed against local db if field is Id field of entity or a reference to an entity. It is OK to use 15 char ID to query agaist server,
|
|
2463
|
-
so there is no need to coerce the ID in injected AST which is used by luvio to do the network request when necessary.
|
|
2464
|
-
2. Draft ID swapping is needed when adapter rebuild is triggered. Draft ID swapping also happens in field injection phase when adapter call is invoked. The injected AST node has
|
|
2465
|
-
the canonical ID and is passed to Luvio
|
|
2466
|
-
*/
|
|
2467
|
-
const idProcessingNeeded = isIDValueField(fieldInfo);
|
|
2468
|
-
const makePredicate = fieldInfo.dataType === 'MultiPicklist'
|
|
2469
|
-
? createMultiPicklistPredicate
|
|
2470
|
-
: createSinglePredicate;
|
|
2471
|
-
const predicate = makePredicate(idProcessingNeeded ? sanitizePredicateIDValue(value, draftFunctions) : value, operator, fieldInfo, alias);
|
|
2472
|
-
// for the case where we have a not equals predicate that's value is not null
|
|
2473
|
-
// we need to compound into an or statement to also inlcude the null values for that field
|
|
2474
|
-
if (operator === '!=' && typeof value === 'string') {
|
|
2475
|
-
// create a is null predicate
|
|
2476
|
-
const isNullPredicate = createSinglePredicate(null, '=', fieldInfo, alias);
|
|
2477
|
-
// compound the predicates into an or predicate
|
|
2478
|
-
predicates.push(transformCompoundPredicate('or', [predicate, isNullPredicate]));
|
|
2479
|
-
}
|
|
2480
|
-
else {
|
|
2481
|
-
predicates.push(predicate);
|
|
2482
|
-
}
|
|
2483
|
-
}
|
|
2484
|
-
}
|
|
2485
|
-
}
|
|
2486
|
-
return predicates;
|
|
2487
|
-
}
|
|
2488
|
-
/**
|
|
2489
|
-
*
|
|
2490
|
-
* @param fieldInfo
|
|
2491
|
-
* @returns true if field is of IDValue type
|
|
2492
|
-
*/
|
|
2493
|
-
function isIDValueField(fieldInfo) {
|
|
2494
|
-
return fieldInfo.apiName === 'Id' || fieldInfo.referenceToInfos.length > 0;
|
|
2495
|
-
}
|
|
2496
|
-
/**
|
|
2497
|
-
* Coerces and swaps ID if it is a draft ID.
|
|
2498
|
-
* @param value Predicate value which might be a string value like 'John' or a List like ['Acme', 'John']
|
|
2499
|
-
* @param draftFunction optional. If undefined, draft ID swapping does not happen
|
|
2500
|
-
* @returns sanitized previdicate value
|
|
2501
|
-
*/
|
|
2502
|
-
function sanitizePredicateIDValue(value, draftFunction) {
|
|
2503
|
-
if (isArray(value)) {
|
|
2504
|
-
return value.map((singleValue) => sanitizePredicateIDValue(singleValue, draftFunction));
|
|
2505
|
-
}
|
|
2506
|
-
else if (typeof value === 'string' && value === '') {
|
|
2507
|
-
return value;
|
|
2508
|
-
}
|
|
2509
|
-
else {
|
|
2510
|
-
const coercedId = getRecordId18(value);
|
|
2511
|
-
if (coercedId !== undefined) {
|
|
2512
|
-
if (draftFunction !== undefined && draftFunction.isDraftId(coercedId)) {
|
|
2513
|
-
return draftFunction.getCanonicalId(coercedId);
|
|
2514
|
-
}
|
|
2515
|
-
return coercedId;
|
|
2516
|
-
}
|
|
2517
|
-
return value;
|
|
2518
|
-
}
|
|
2519
|
-
}
|
|
2520
|
-
function createMultiPicklistPredicate(value, operator, fieldInfo, alias) {
|
|
2521
|
-
if (!value || (isArray(value) && value.length === 0)) {
|
|
2522
|
-
// eslint-disable-next-line
|
|
2523
|
-
throw new Error(`No value specified for MultiPickList filter`);
|
|
2524
|
-
}
|
|
2525
|
-
// generate single prodicate for = and !=
|
|
2526
|
-
if (operator === '=' || operator === '!=') {
|
|
2527
|
-
// The raw value could be ';;a; b;;', clean it up to 'a;b'
|
|
2528
|
-
const welformatedValue = value
|
|
2529
|
-
.toString()
|
|
2530
|
-
.split(MultiPickListValueSeparator)
|
|
2531
|
-
.map((v) => v.trim())
|
|
2532
|
-
.filter((v) => v.length > 0)
|
|
2533
|
-
.join(MultiPickListValueSeparator);
|
|
2534
|
-
return createSinglePredicate(welformatedValue, operator, fieldInfo, alias);
|
|
2535
|
-
}
|
|
2536
|
-
// if we have more than 1 value in the array from an includes/excludes we must split the values into
|
|
2537
|
-
// a compound OR predicate for includes, AND predicate for excludes. each predicate could a composite
|
|
2538
|
-
// if the value in the array is ; separated. sample: ['a:b','c']
|
|
2539
|
-
const valueArray = isArray(value) ? value : [value];
|
|
2540
|
-
return {
|
|
2541
|
-
type: PredicateType.compound,
|
|
2542
|
-
operator: operator === 'LIKE' ? 'or' : 'and',
|
|
2543
|
-
children: valueArray.map((v) => {
|
|
2544
|
-
const splittedValue = v
|
|
2545
|
-
.split(MultiPickListValueSeparator)
|
|
2546
|
-
.map((v) => v.trim())
|
|
2547
|
-
.filter((v) => v.length > 0);
|
|
2548
|
-
if (splittedValue.length === 1) {
|
|
2549
|
-
return createSinglePredicate(v, operator, fieldInfo, alias);
|
|
2550
|
-
}
|
|
2551
|
-
else {
|
|
2552
|
-
return {
|
|
2553
|
-
type: PredicateType.compound,
|
|
2554
|
-
operator: operator === 'LIKE' ? 'and' : 'or',
|
|
2555
|
-
children: splittedValue.map((vInner) => createSinglePredicate(vInner, operator, fieldInfo, alias)),
|
|
2556
|
-
};
|
|
2557
|
-
}
|
|
2558
|
-
}),
|
|
2559
|
-
};
|
|
2560
|
-
}
|
|
2561
|
-
function createSinglePredicate(val, operator, field, alias) {
|
|
2562
|
-
let value = val;
|
|
2563
|
-
if (field.dataType === 'MultiPicklist') {
|
|
2564
|
-
value = multiPicklistToSql(operator, val);
|
|
2565
|
-
}
|
|
2566
|
-
else if (typeof value !== 'object' &&
|
|
2567
|
-
!isArray(value) &&
|
|
2568
|
-
typeof value !== 'number' &&
|
|
2569
|
-
(typeof value !== 'string' || typeof value !== 'boolean')) {
|
|
2570
|
-
value = value.toString();
|
|
2571
|
-
}
|
|
2572
|
-
else if (isDateTimeInput(value)) {
|
|
2573
|
-
return dateTimePredicate(value, operator, field, alias);
|
|
2574
|
-
}
|
|
2575
|
-
let leftPath = `$.fields.${field.apiName}.value`;
|
|
2576
|
-
if (field.apiName === 'Id') {
|
|
2577
|
-
leftPath = '$.id';
|
|
2578
|
-
}
|
|
2579
|
-
else if (field.apiName === 'weakEtag') {
|
|
2580
|
-
leftPath = '$.weakEtag';
|
|
2581
|
-
}
|
|
2582
|
-
else if (field.apiName === 'RecordTypeId') {
|
|
2583
|
-
leftPath = '$.recordTypeId';
|
|
2584
|
-
}
|
|
2585
|
-
return {
|
|
2586
|
-
alias,
|
|
2587
|
-
leftPath,
|
|
2588
|
-
operator,
|
|
2589
|
-
value,
|
|
2590
|
-
dataType: field.dataType,
|
|
2591
|
-
type: PredicateType.single,
|
|
2592
|
-
isCaseSensitive: isIDField(field),
|
|
2593
|
-
};
|
|
2594
|
-
}
|
|
2595
|
-
function isIDField(field) {
|
|
2596
|
-
return field.apiName === 'Id' || field.referenceToInfos.length > 0;
|
|
2597
|
-
}
|
|
2598
|
-
function multiPicklistToSql(operator, value) {
|
|
2599
|
-
// Individual multipicklist terms that delimited by semicolon are stored server-side
|
|
2600
|
-
// as lexically sorted strings and treated like logical ANDs. We can approximate this
|
|
2601
|
-
// behavior in SQL with wildcarded `LIKE` SQL operators. Terms with no delimiter can
|
|
2602
|
-
// be treated as string literals. Multiple terms are logically OR'd together to
|
|
2603
|
-
// match the behavior described in SOQL documentation (https://sfdc.co/c9j0r)
|
|
2604
|
-
// To make sure the match is safe for includes/excludes. the value is prefix and
|
|
2605
|
-
// suffix with ';', like 'abc' to '%;abc;%'. raw value for eq and ne.
|
|
2606
|
-
if (operator === 'LIKE' || operator === 'NOT LIKE') {
|
|
2607
|
-
return `%${MultiPickListValueSeparator}${value}${MultiPickListValueSeparator}%`;
|
|
2608
|
-
}
|
|
2609
|
-
else {
|
|
2610
|
-
return value;
|
|
2611
|
-
}
|
|
2612
|
-
}
|
|
2613
|
-
// handles 'SinglePredicate' | 'CompoundPredicate'| 'NotPredicate' conversion to SQLite
|
|
2614
|
-
function predicateToSQL(predicate, defaultAlias, isChildNotPredicate = false) {
|
|
2615
|
-
if (isSinglePredicate(predicate)) {
|
|
2616
|
-
return singlePredicateToSql(predicate, defaultAlias, isChildNotPredicate);
|
|
2617
|
-
}
|
|
2618
|
-
else if (isNotPredicate(predicate)) {
|
|
2619
|
-
return notPredicateToSql(predicate, defaultAlias);
|
|
2620
|
-
}
|
|
2621
|
-
else {
|
|
2622
|
-
return flattenSqlAndBindings(predicatesToSqls(predicate.children, defaultAlias, isChildNotPredicate), predicate.operator);
|
|
2623
|
-
}
|
|
2624
|
-
}
|
|
2625
|
-
/**
|
|
2626
|
-
* Takes an array of predicates turned to sql and flattens them into one statement string
|
|
2627
|
-
* with combined bindings
|
|
2628
|
-
* @param sqlAndBindings
|
|
2629
|
-
* @param operator
|
|
2630
|
-
* @returns
|
|
2631
|
-
*/
|
|
2632
|
-
function flattenSqlAndBindings(sqlAndBindings, operator) {
|
|
2633
|
-
const joiner = operator === undefined ? ' ' : ` ${operator} `.toUpperCase();
|
|
2634
|
-
const sql = sqlAndBindings.map((sqlAndBinding) => sqlAndBinding.sql).join(joiner);
|
|
2635
|
-
const binding = sqlAndBindings
|
|
2636
|
-
.map((sqlAndBinding) => sqlAndBinding.binding)
|
|
2637
|
-
.reduce(flatten, []);
|
|
2638
|
-
return { sql, binding };
|
|
2639
|
-
}
|
|
2640
|
-
function inOrNotInValuesIncludeNull(predicate) {
|
|
2641
|
-
const isInOrNin = predicate.operator === 'IN' || predicate.operator === 'NOT IN';
|
|
2642
|
-
const nullValueExists = isArray(predicate.value)
|
|
2643
|
-
? predicate.value.some((v) => v === null)
|
|
2644
|
-
: predicate.value === null;
|
|
2645
|
-
return isInOrNin && nullValueExists;
|
|
2646
|
-
}
|
|
2647
|
-
/**
|
|
2648
|
-
* Takes a single predicate and appends AND IS NOT NULL to the end of it.
|
|
2649
|
-
* So when we wrap a NOT around the predicate it will include non-null values.
|
|
2650
|
-
* @param predicate
|
|
2651
|
-
* @param defaultAlias
|
|
2652
|
-
* @returns
|
|
2653
|
-
*/
|
|
2654
|
-
function singleNotPredicateToIncludeNulls(predicate, defaultAlias) {
|
|
2655
|
-
// if we are checking a value of null then there is no need to append on and is not null
|
|
2656
|
-
// also do not add to in/nin predicates
|
|
2657
|
-
if (predicate.value === null || inOrNotInValuesIncludeNull(predicate)) {
|
|
2658
|
-
return predicateToSQL(predicate, defaultAlias);
|
|
2659
|
-
}
|
|
2660
|
-
const { sql, binding } = predicateToSQL(predicate, defaultAlias);
|
|
2661
|
-
const notNull = { ...predicate, operator: 'IS NOT', value: null };
|
|
2662
|
-
const notNullSql = predicateToSQL(notNull, defaultAlias);
|
|
2663
|
-
return {
|
|
2664
|
-
sql: `${sql} AND ${notNullSql.sql}`,
|
|
2665
|
-
binding: binding.concat(notNullSql.binding),
|
|
2666
|
-
};
|
|
2667
|
-
}
|
|
2668
|
-
function notPredicateToSql(predicate, defaultAlias) {
|
|
2669
|
-
const children = predicate.children.map((child) => {
|
|
2670
|
-
if (isNotPredicate(child)) {
|
|
2671
|
-
// eslint-disable-next-line @salesforce/lds/no-error-in-production
|
|
2672
|
-
throw new Error('A not predicate cannot be a child of a not query');
|
|
2673
|
-
}
|
|
2674
|
-
if (isSinglePredicate(child)) {
|
|
2675
|
-
const { sql, binding } = singleNotPredicateToIncludeNulls(child, defaultAlias);
|
|
2676
|
-
return { sql: `NOT ( ${sql} )`, binding };
|
|
2677
|
-
}
|
|
2678
|
-
else {
|
|
2679
|
-
const compound = child.children.map((c) => {
|
|
2680
|
-
if (isSinglePredicate(c)) {
|
|
2681
|
-
const { sql, binding } = singleNotPredicateToIncludeNulls(c, defaultAlias);
|
|
2682
|
-
return { sql: `( ${sql} )`, binding };
|
|
2683
|
-
}
|
|
2684
|
-
else {
|
|
2685
|
-
return predicateToSQL(c, defaultAlias, true);
|
|
2686
|
-
}
|
|
2687
|
-
});
|
|
2688
|
-
const { sql, binding } = flattenSqlAndBindings(compound, child.operator);
|
|
2689
|
-
return { sql: `( NOT ( ${sql} ) )`, binding };
|
|
2690
|
-
}
|
|
2691
|
-
});
|
|
2692
|
-
return flattenSqlAndBindings(children);
|
|
2693
|
-
}
|
|
2694
|
-
/**
|
|
2695
|
-
* Used to build a SinglePredicate into a sql statement with bindings
|
|
2696
|
-
* @param predicate
|
|
2697
|
-
* @param defaultAlias
|
|
2698
|
-
* @returns
|
|
2699
|
-
*/
|
|
2700
|
-
function singlePredicateToSql(predicate, defaultAlias, isChildNotPredicate = false) {
|
|
2701
|
-
//if a child of a not predicate than we need to include nulls
|
|
2702
|
-
if (isChildNotPredicate) {
|
|
2703
|
-
return singleNotPredicateToIncludeNulls(predicate, defaultAlias);
|
|
2704
|
-
}
|
|
2705
|
-
// handles different type of filter predicate like 'date', 'picklist', 'time', 'percent'
|
|
2706
|
-
let { alias, leftPath, operator, value, dataType, isCaseSensitive } = predicate;
|
|
2707
|
-
let binding = [];
|
|
2708
|
-
let boundValue = extractPredicateValue(value, dataType);
|
|
2709
|
-
// Handles boolean type field
|
|
2710
|
-
if (dataType === 'Boolean') {
|
|
2711
|
-
if (boundValue === null) {
|
|
2712
|
-
if (operator === '=') {
|
|
2713
|
-
operator = 'IS';
|
|
2714
|
-
}
|
|
2715
|
-
boundValue = 'NULL';
|
|
2716
|
-
}
|
|
2717
|
-
else if (boundValue === true) {
|
|
2718
|
-
boundValue = 1;
|
|
2719
|
-
}
|
|
2720
|
-
else {
|
|
2721
|
-
boundValue = 0;
|
|
2722
|
-
}
|
|
2723
|
-
}
|
|
2724
|
-
if (boundValue === null) {
|
|
2725
|
-
if (operator === '=') {
|
|
2726
|
-
operator = 'IS';
|
|
2727
|
-
}
|
|
2728
|
-
else if (operator === '!=') {
|
|
2729
|
-
operator = 'IS NOT';
|
|
2730
|
-
}
|
|
2731
|
-
}
|
|
2732
|
-
alias = !alias ? defaultAlias : alias;
|
|
2733
|
-
let sql;
|
|
2734
|
-
if (predicate.bindings !== undefined) {
|
|
2735
|
-
if (predicate.dataType !== undefined &&
|
|
2736
|
-
(predicate.dataType === 'DateTime' || predicate.dataType === 'Date')) {
|
|
2737
|
-
const dateFunction = predicate.dataType === 'DateTime' ? 'datetime' : 'date';
|
|
2738
|
-
sql = `${dateFunction}(json_extract("${alias}".data, '${leftPath}')) ${operator} ${predicate.value}`;
|
|
2739
|
-
}
|
|
2740
|
-
else {
|
|
2741
|
-
sql = `json_extract("${alias}".data, '${leftPath}') ${operator} ${predicate.value}`;
|
|
2742
|
-
}
|
|
2743
|
-
binding = binding.concat(predicate.bindings);
|
|
2744
|
-
}
|
|
2745
|
-
else {
|
|
2746
|
-
if (operator === 'IN' || operator === 'NOT IN') {
|
|
2747
|
-
const { sql: questionSql, binding: valueBinding, includesNull, } = handleExtractedPredicateValue(boundValue, true);
|
|
2748
|
-
// If an explicit collating sequence is required on an IN operator it should be applied to the left operand,
|
|
2749
|
-
// like this: "x COLLATE NOCASE IN (y,z, ...)".
|
|
2750
|
-
const nullCheck = `json_extract("${alias}".data, '${leftPath}') ${operator === 'IN' ? 'IS' : 'IS NOT'} ?`;
|
|
2751
|
-
sql = `json_extract("${alias}".data, '${leftPath}')${isCaseSensitive === true ? '' : ` COLLATE NOCASE`} ${operator} ${questionSql}`;
|
|
2752
|
-
binding.push(...valueBinding);
|
|
2753
|
-
if (includesNull) {
|
|
2754
|
-
sql = `(${sql} OR ${nullCheck})`;
|
|
2755
|
-
binding.push(null);
|
|
2756
|
-
}
|
|
2757
|
-
}
|
|
2758
|
-
else {
|
|
2759
|
-
const { sql: questionSql, binding: valueBinding } = handleExtractedPredicateValue(boundValue, false);
|
|
2760
|
-
// SQLite is case sensitive by default, SOQL is case in-sensitive by default
|
|
2761
|
-
// For pick list includes or excludeds, prefix and suffix the field value with ';' to guarantee the query accuracy.
|
|
2762
|
-
if (dataType === 'MultiPicklist' && (operator === 'LIKE' || operator === 'NOT LIKE')) {
|
|
2763
|
-
sql = `'${MultiPickListValueSeparator}' || json_extract("${alias}".data, '${leftPath}') || '${MultiPickListValueSeparator}' ${operator} ${questionSql} COLLATE NOCASE`;
|
|
2764
|
-
}
|
|
2765
|
-
else {
|
|
2766
|
-
sql = `json_extract("${alias}".data, '${leftPath}') ${operator} ${questionSql}${isCaseSensitive === true ? '' : ` COLLATE NOCASE`}`;
|
|
2767
|
-
}
|
|
2768
|
-
binding.push(...valueBinding);
|
|
2769
|
-
}
|
|
2770
|
-
}
|
|
2771
|
-
return { sql, binding };
|
|
2772
|
-
}
|
|
2773
|
-
// for one value, return { sql: '?', bindings:'xxx'}. for multiple value, return { sql: '(?, ?, ?)', binding: ['xxx','yyy','zzz'] }
|
|
2774
|
-
function handleExtractedPredicateValue(boundValue, checkForNull) {
|
|
2775
|
-
let questionSql = '?';
|
|
2776
|
-
let binding = [];
|
|
2777
|
-
let includesNull = false;
|
|
2778
|
-
if (isArray(boundValue)) {
|
|
2779
|
-
const removeNull = boundValue.filter((v) => v !== null);
|
|
2780
|
-
includesNull = removeNull.length !== boundValue.length;
|
|
2781
|
-
// construct (?, ?)
|
|
2782
|
-
questionSql = `(${new Array(checkForNull ? removeNull.length : boundValue.length)
|
|
2783
|
-
.fill('?')
|
|
2784
|
-
.join(', ')})`;
|
|
2785
|
-
binding = binding.concat(...(checkForNull ? removeNull : boundValue));
|
|
2786
|
-
}
|
|
2787
|
-
else {
|
|
2788
|
-
binding = binding.concat(boundValue);
|
|
2789
|
-
}
|
|
2790
|
-
return { sql: questionSql, binding, includesNull };
|
|
2791
|
-
}
|
|
2792
|
-
function processCompoundPredicate(operator, filters, recordType, alias, objectInfoMap, joins) {
|
|
2793
|
-
const predicates = filters
|
|
2794
|
-
.map((filter) => filterToPredicates(filter, recordType, alias, objectInfoMap, joins))
|
|
2795
|
-
.reduce(flatten);
|
|
2796
|
-
return transformCompoundPredicate(operator, predicates);
|
|
2797
|
-
}
|
|
2798
|
-
function transformCompoundPredicate(operator, childPredicates) {
|
|
2799
|
-
const diffCompoundPredicates = childPredicates
|
|
2800
|
-
.filter(isCompoundPredicate)
|
|
2801
|
-
.filter((compoundPredicate) => compoundPredicate.operator !== operator);
|
|
2802
|
-
// flattens the child compound predicate if its operator is the same of its parent to improve the SQL efficiency.
|
|
2803
|
-
const sameCompoundPredicates = childPredicates
|
|
2804
|
-
.filter(isCompoundPredicate)
|
|
2805
|
-
.filter((compoundPredicate) => compoundPredicate.operator === operator)
|
|
2806
|
-
.map((pred) => pred.children)
|
|
2807
|
-
.reduce(flatten, []);
|
|
2808
|
-
const singlePredicates = childPredicates.filter((p) => isSinglePredicate(p) || isNotPredicate(p));
|
|
2809
|
-
const children = [...diffCompoundPredicates, ...sameCompoundPredicates, ...singlePredicates];
|
|
2810
|
-
if (children.length === 1) {
|
|
2811
|
-
return children[0];
|
|
2812
|
-
}
|
|
2813
|
-
return {
|
|
2814
|
-
type: PredicateType.compound,
|
|
2815
|
-
operator,
|
|
2816
|
-
children,
|
|
2817
|
-
};
|
|
2818
|
-
}
|
|
2819
|
-
function predicatesToSqls(predicates, defaultAlias, isChildNotPredicate = false) {
|
|
2820
|
-
// (a and (b or c)) is differnt from (a and b or c)
|
|
2821
|
-
const wrapit = predicates.length >= 1 && predicates.some(isCompoundPredicate);
|
|
2822
|
-
const sqlAndBindingsResult = [];
|
|
2823
|
-
if (wrapit) {
|
|
2824
|
-
const unwrappedSqlAndBindings = predicates
|
|
2825
|
-
.filter(isSinglePredicate)
|
|
2826
|
-
.map((predicate) => predicateToSQL(predicate, defaultAlias, isChildNotPredicate));
|
|
2827
|
-
if (unwrappedSqlAndBindings && unwrappedSqlAndBindings.length) {
|
|
2828
|
-
sqlAndBindingsResult.push(...unwrappedSqlAndBindings);
|
|
2829
|
-
}
|
|
2830
|
-
const wrappedSqlAndBindings = predicates
|
|
2831
|
-
.filter(isCompoundPredicate)
|
|
2832
|
-
.map((predicate) => predicateToSQL(predicate, defaultAlias, isChildNotPredicate))
|
|
2833
|
-
.map(wrapSqlAndBindings);
|
|
2834
|
-
sqlAndBindingsResult.push(...wrappedSqlAndBindings);
|
|
2835
|
-
}
|
|
2836
|
-
else {
|
|
2837
|
-
const sqlAndBindings = predicates.map((predicate) => predicateToSQL(predicate, defaultAlias, isChildNotPredicate));
|
|
2838
|
-
sqlAndBindingsResult.push(...sqlAndBindings);
|
|
2839
|
-
}
|
|
2840
|
-
return sqlAndBindingsResult;
|
|
2841
|
-
}
|
|
2842
|
-
function wrapSqlAndBindings(sqlAndBindings) {
|
|
2843
|
-
return { sql: `(` + sqlAndBindings.sql + `)`, binding: sqlAndBindings.binding };
|
|
2844
|
-
}
|
|
2845
|
-
function operatorToSql(graphqlOperator) {
|
|
2846
|
-
switch (graphqlOperator) {
|
|
2847
|
-
case 'eq':
|
|
2848
|
-
return '=';
|
|
2849
|
-
case 'ne':
|
|
2850
|
-
return '!=';
|
|
2851
|
-
case 'gt':
|
|
2852
|
-
return '>';
|
|
2853
|
-
case 'gte':
|
|
2854
|
-
return '>=';
|
|
2855
|
-
case 'lt':
|
|
2856
|
-
return '<';
|
|
2857
|
-
case 'lte':
|
|
2858
|
-
return '<=';
|
|
2859
|
-
case 'in':
|
|
2860
|
-
return 'IN';
|
|
2861
|
-
case 'nin':
|
|
2862
|
-
return 'NOT IN';
|
|
2863
|
-
case 'like':
|
|
2864
|
-
case 'includes':
|
|
2865
|
-
return 'LIKE';
|
|
2866
|
-
case 'excludes':
|
|
2867
|
-
return 'NOT LIKE';
|
|
2868
|
-
default:
|
|
2869
|
-
// eslint-disable-next-line
|
|
2870
|
-
throw new Error(`Unknown GraphQL operator: ${graphqlOperator}`);
|
|
2871
|
-
}
|
|
2872
|
-
}
|
|
2873
|
-
function extractPredicateValue(value, dataType) {
|
|
2874
|
-
if (dataType !== undefined && dataType === 'Boolean') {
|
|
2875
|
-
if (typeof value === 'string') {
|
|
2876
|
-
return value === 'true';
|
|
2877
|
-
}
|
|
2878
|
-
if (typeof value === 'boolean')
|
|
2879
|
-
return value;
|
|
2880
|
-
}
|
|
2881
|
-
if (typeof value !== 'object') {
|
|
2882
|
-
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
2883
|
-
return value;
|
|
2884
|
-
}
|
|
2885
|
-
else {
|
|
2886
|
-
return value.toString();
|
|
2887
|
-
}
|
|
2888
|
-
}
|
|
2889
|
-
return value;
|
|
2890
|
-
}
|
|
2891
|
-
|
|
2892
|
-
var PredicateType;
|
|
2893
|
-
(function (PredicateType) {
|
|
2894
|
-
PredicateType["compound"] = "compound";
|
|
2895
|
-
PredicateType["single"] = "single";
|
|
2896
|
-
PredicateType["not"] = "not";
|
|
2897
|
-
})(PredicateType || (PredicateType = {}));
|
|
2898
|
-
function buildQuery(config) {
|
|
2899
|
-
const joins = buildJoins(config);
|
|
2900
|
-
const predicates = buildPredicates(config);
|
|
2901
|
-
const orderBy = buildOrderBy(config);
|
|
2902
|
-
const staleRecordsSql = excludeStaleRecordsGate.isOpen({ fallback: false })
|
|
2903
|
-
? `AND (
|
|
2904
|
-
json_extract("${config.alias}".metadata, '${JSON_EXTRACT_PATH_INGESTION_TIMESTAMP}') >= ?
|
|
2905
|
-
OR json_extract("${config.alias}".data, '${JSON_EXTRACT_PATH_DRAFTS}') IS NOT NULL
|
|
2906
|
-
)`
|
|
2907
|
-
: '';
|
|
2908
|
-
const sql = `
|
|
2909
|
-
SELECT "${config.alias}".data
|
|
2910
|
-
FROM lds_data "${config.alias}" ${joins.sql}
|
|
2911
|
-
WHERE "${config.alias}".key like 'UiApi::RecordRepresentation:%'
|
|
2912
|
-
AND json_extract("${config.alias}".data, '${JSON_EXTRACT_PATH_INGESTION_APINAME}') = '${config.alias}'
|
|
2913
|
-
${staleRecordsSql}
|
|
2914
|
-
${predicates.sql}
|
|
2915
|
-
${orderBy.sql}
|
|
2916
|
-
LIMIT ?
|
|
2917
|
-
OFFSET ?
|
|
2918
|
-
`
|
|
2919
|
-
.split('\n')
|
|
2920
|
-
.map((line) => line.trim())
|
|
2921
|
-
.join(' ');
|
|
2922
|
-
const bindings = [
|
|
2923
|
-
// bindings from predicates on joins
|
|
2924
|
-
...joins.bindings,
|
|
2925
|
-
...(excludeStaleRecordsGate.isOpen({ fallback: false }) ? [config.ingestionTimestamp] : []),
|
|
2926
|
-
// where clause and parent scope bindings
|
|
2927
|
-
...predicates.bindings,
|
|
2928
|
-
// limit binding
|
|
2929
|
-
config.limit || 10,
|
|
2930
|
-
config.offset || 0,
|
|
2931
|
-
];
|
|
2932
|
-
return { sql: sql.trim(), bindings };
|
|
2933
|
-
}
|
|
2934
|
-
function dedupeJoins(joins) {
|
|
2935
|
-
const deduped = {};
|
|
2936
|
-
for (const join of joins) {
|
|
2937
|
-
deduped[join.alias + join.to] = join;
|
|
2938
|
-
}
|
|
2939
|
-
return values$1(deduped);
|
|
2940
|
-
}
|
|
2941
|
-
function buildJoins(config) {
|
|
2942
|
-
let sql = '';
|
|
2943
|
-
const bindings = [];
|
|
2944
|
-
const orderByJoins = config.orderBy !== undefined ? removeUndefined(config.orderBy.map((x) => x.joins)) : [];
|
|
2945
|
-
const allJoins = dedupeJoins([
|
|
2946
|
-
...(config.joins || []),
|
|
2947
|
-
...(orderByJoins.reduce(flatten, []) || []),
|
|
2948
|
-
]);
|
|
2949
|
-
if (allJoins.length === 0)
|
|
2950
|
-
return { sql, bindings };
|
|
2951
|
-
sql = allJoins.reduce((joinAccumulator, join) => {
|
|
2952
|
-
let timestampAdded = false;
|
|
2953
|
-
const joinConditions = join.conditions.reduce((conditionAccumulator, condition) => {
|
|
2954
|
-
let joined_sql;
|
|
2955
|
-
const joinMetadataTimestamp = excludeStaleRecordsGate.isOpen({ fallback: false })
|
|
2956
|
-
? ` AND (json_extract("${join.alias}".metadata, '${JSON_EXTRACT_PATH_INGESTION_TIMESTAMP}') >= ? OR json_extract("${join.alias}".data, '${JSON_EXTRACT_PATH_DRAFTS}') IS NOT NULL)`
|
|
2957
|
-
: '';
|
|
2958
|
-
// predicate on a value, use the newly joined table
|
|
2959
|
-
if ('type' in condition) {
|
|
2960
|
-
const { sql, binding } = predicateToSQL(condition, join.alias);
|
|
2961
|
-
joined_sql = ` AND ${sql}${timestampAdded ? '' : joinMetadataTimestamp}`;
|
|
2962
|
-
bindings.push(...binding);
|
|
2963
|
-
if (excludeStaleRecordsGate.isOpen({ fallback: false }) &&
|
|
2964
|
-
timestampAdded === false) {
|
|
2965
|
-
bindings.push(config.ingestionTimestamp);
|
|
2966
|
-
timestampAdded = true;
|
|
2967
|
-
}
|
|
2968
|
-
}
|
|
2969
|
-
else {
|
|
2970
|
-
// predicate on a path
|
|
2971
|
-
const left = ` AND json_extract("${join.to}".data, '${condition.leftPath}')`;
|
|
2972
|
-
const right = `json_extract("${join.alias}".data, '${condition.rightPath}')`;
|
|
2973
|
-
joined_sql = `${left} = ${right}${timestampAdded ? '' : joinMetadataTimestamp}`;
|
|
2974
|
-
if (excludeStaleRecordsGate.isOpen({ fallback: false }) &&
|
|
2975
|
-
timestampAdded === false) {
|
|
2976
|
-
bindings.push(config.ingestionTimestamp);
|
|
2977
|
-
timestampAdded = true;
|
|
2978
|
-
}
|
|
2979
|
-
}
|
|
2980
|
-
conditionAccumulator += joined_sql;
|
|
2981
|
-
return conditionAccumulator;
|
|
2982
|
-
}, '');
|
|
2983
|
-
// 'joinConditions' has the prefix 'AND_' which needs to be removed.
|
|
2984
|
-
const formattedJoinCondition = joinConditions.slice(4);
|
|
2985
|
-
joinAccumulator += `
|
|
2986
|
-
${join.type} JOIN lds_data "${join.alias}"
|
|
2987
|
-
ON "${join.alias}".key like 'UiApi::RecordRepresentation:%'
|
|
2988
|
-
AND${formattedJoinCondition}
|
|
2989
|
-
`;
|
|
2990
|
-
return joinAccumulator;
|
|
2991
|
-
}, '');
|
|
2992
|
-
return { sql, bindings };
|
|
2993
|
-
}
|
|
2994
|
-
function buildPredicates(config) {
|
|
2995
|
-
if (!config.predicates)
|
|
2996
|
-
return { sql: '', bindings: [] };
|
|
2997
|
-
const sqlAndBindingsResult = predicatesToSqls(config.predicates, config.alias);
|
|
2998
|
-
let sql = sqlAndBindingsResult.map((sqlAndBinding) => sqlAndBinding.sql).join(` AND `);
|
|
2999
|
-
if (sql && sql.length > 0) {
|
|
3000
|
-
sql = `AND ` + sql;
|
|
3001
|
-
}
|
|
3002
|
-
const bindings = sqlAndBindingsResult
|
|
3003
|
-
.map((sqlAndBinding) => sqlAndBinding.binding)
|
|
3004
|
-
.reduce(flatten, []);
|
|
3005
|
-
return { sql, bindings };
|
|
3006
|
-
}
|
|
3007
|
-
function buildOrderBy(config) {
|
|
3008
|
-
let sql = '';
|
|
3009
|
-
if (!config.orderBy)
|
|
3010
|
-
return { sql };
|
|
3011
|
-
const orderByStatements = config.orderBy.reduce((accumulator, orderBy) => {
|
|
3012
|
-
const sortOrder = orderBy.order !== undefined ? `${orderBy.order}` : ``;
|
|
3013
|
-
const sortNulls = orderBy.nulls !== undefined ? ` NULLS ${orderBy.nulls}` : ``;
|
|
3014
|
-
const statement = `json_extract("${orderBy.alias}".data, '${orderBy.path}') ${sortOrder} ${sortNulls}`;
|
|
3015
|
-
accumulator.push(statement);
|
|
3016
|
-
return accumulator;
|
|
3017
|
-
}, []);
|
|
3018
|
-
sql = `
|
|
3019
|
-
${orderByStatements.length > 0 ? 'ORDER BY' : ''}
|
|
3020
|
-
${orderByStatements.join(', ')}
|
|
3021
|
-
`;
|
|
3022
|
-
return { sql };
|
|
3023
|
-
}
|
|
3024
|
-
function isCompoundPredicate(predicate) {
|
|
3025
|
-
return predicate.type === PredicateType.compound;
|
|
3026
|
-
}
|
|
3027
|
-
function isSinglePredicate(predicate) {
|
|
3028
|
-
return predicate.type === PredicateType.single;
|
|
3029
|
-
}
|
|
3030
|
-
function isNotPredicate(predicate) {
|
|
3031
|
-
return predicate.type === PredicateType.not;
|
|
3032
|
-
}
|
|
3033
|
-
function isDateTimeInput(object) {
|
|
3034
|
-
return (object !== undefined &&
|
|
3035
|
-
object !== null &&
|
|
3036
|
-
typeof object === 'object' &&
|
|
3037
|
-
(object.value !== undefined || object.literal !== undefined || object.range !== undefined));
|
|
3038
|
-
}
|
|
3039
|
-
const PARENT_RELATIONSHIP = 'parentRelationship';
|
|
3040
|
-
function depth(json, currentLevel = 0) {
|
|
3041
|
-
if (typeof json !== 'object') {
|
|
3042
|
-
return currentLevel;
|
|
3043
|
-
}
|
|
3044
|
-
const keys$1$1 = keys$1(json);
|
|
3045
|
-
if (keys$1$1.length === 0)
|
|
3046
|
-
return 0;
|
|
3047
|
-
const depths = keys$1$1.map((key) => {
|
|
3048
|
-
const value = json[key];
|
|
3049
|
-
return depth(value, currentLevel + 1);
|
|
3050
|
-
});
|
|
3051
|
-
return Math.max(...depths);
|
|
3052
|
-
}
|
|
3053
|
-
function flatten(previous, current) {
|
|
3054
|
-
return previous.concat(current);
|
|
3055
|
-
}
|
|
3056
|
-
function findFieldInfo(objectInfo, fieldName) {
|
|
3057
|
-
return values$1(objectInfo.fields).find((field) => field.apiName === fieldName ||
|
|
3058
|
-
(field.dataType === 'Reference' && field.relationshipName === fieldName));
|
|
3059
|
-
}
|
|
3060
|
-
async function readIngestionTimestampForKey(key, query) {
|
|
3061
|
-
let ingestionTimestamp = 0;
|
|
3062
|
-
const sql = `SELECT json_extract(metadata, '${JSON_EXTRACT_PATH_INGESTION_TIMESTAMP}') FROM lds_data WHERE key IS ?`;
|
|
3063
|
-
const results = await query(sql, [key]);
|
|
3064
|
-
const [timestamp] = results.rows.map((row) => row[0]);
|
|
3065
|
-
if (timestamp !== null) {
|
|
3066
|
-
const numericalTimestamp = Number(timestamp);
|
|
3067
|
-
if (isNaN(numericalTimestamp)) {
|
|
3068
|
-
return ingestionTimestamp;
|
|
3069
|
-
}
|
|
3070
|
-
ingestionTimestamp = numericalTimestamp;
|
|
3071
|
-
}
|
|
3072
|
-
return ingestionTimestamp;
|
|
3073
|
-
}
|
|
3074
|
-
|
|
3075
|
-
function findSpanningField(name) {
|
|
3076
|
-
return (field) => {
|
|
3077
|
-
return (field.apiName === name ||
|
|
3078
|
-
(field.dataType === 'Reference' && field.relationshipName === name));
|
|
3079
|
-
};
|
|
3080
|
-
}
|
|
3081
|
-
function orderByToPredicate(orderBy, recordType, alias, objectInfoMap, joins) {
|
|
3082
|
-
let predicates = [];
|
|
3083
|
-
if (!orderBy)
|
|
3084
|
-
return predicates;
|
|
3085
|
-
const isSpanning = depth(orderBy) > 2;
|
|
3086
|
-
if (isSpanning) {
|
|
3087
|
-
const keys$1$1 = keys$1(orderBy);
|
|
3088
|
-
for (let i = 0, len = keys$1$1.length; i < len; i++) {
|
|
3089
|
-
const key = keys$1$1[i];
|
|
3090
|
-
const parentFields = objectInfoMap[recordType].fields;
|
|
3091
|
-
const fieldInfo = values$1(parentFields).find(findSpanningField(key));
|
|
3092
|
-
if (fieldInfo && fieldInfo.referenceToInfos.length > 0) {
|
|
3093
|
-
const { apiName } = fieldInfo.referenceToInfos[0];
|
|
3094
|
-
const parentFieldInfo = values$1(objectInfoMap[recordType].fields).find(findSpanningField(fieldInfo.apiName));
|
|
3095
|
-
if (parentFieldInfo !== undefined) {
|
|
3096
|
-
const path = {
|
|
3097
|
-
leftPath: `$.fields.${parentFieldInfo.apiName}.value`,
|
|
3098
|
-
rightPath: '$.id',
|
|
3099
|
-
};
|
|
3100
|
-
const childAlias = `${alias}_${fieldInfo.relationshipName}`;
|
|
3101
|
-
const join = [
|
|
3102
|
-
{
|
|
3103
|
-
type: 'LEFT',
|
|
3104
|
-
alias: childAlias,
|
|
3105
|
-
to: alias,
|
|
3106
|
-
conditions: [path],
|
|
3107
|
-
apiName: apiName,
|
|
3108
|
-
},
|
|
3109
|
-
];
|
|
3110
|
-
let spanningPredicates = orderByToPredicate(orderBy[key], apiName, childAlias, objectInfoMap, joins ? joins.concat(join) : join);
|
|
3111
|
-
predicates = predicates.concat(spanningPredicates);
|
|
3112
|
-
}
|
|
3113
|
-
}
|
|
3114
|
-
}
|
|
3115
|
-
}
|
|
3116
|
-
else {
|
|
3117
|
-
const keys$1$1 = keys$1(orderBy);
|
|
3118
|
-
for (let i = 0, len = keys$1$1.length; i < len; i++) {
|
|
3119
|
-
const key = keys$1$1[i];
|
|
3120
|
-
if (!objectInfoMap[recordType])
|
|
3121
|
-
continue;
|
|
3122
|
-
const fieldInfo = objectInfoMap[recordType].fields[key];
|
|
3123
|
-
if (fieldInfo === undefined)
|
|
3124
|
-
continue;
|
|
3125
|
-
const { order, nulls } = orderBy[key];
|
|
3126
|
-
const predicate = {
|
|
3127
|
-
path: pathForKey(key),
|
|
3128
|
-
alias,
|
|
3129
|
-
order,
|
|
3130
|
-
nulls,
|
|
3131
|
-
joins,
|
|
3132
|
-
};
|
|
3133
|
-
predicates.push(predicate);
|
|
3134
|
-
}
|
|
3135
|
-
}
|
|
3136
|
-
return predicates;
|
|
3137
|
-
}
|
|
3138
|
-
function pathForKey(key) {
|
|
3139
|
-
switch (key) {
|
|
3140
|
-
case 'ApiName':
|
|
3141
|
-
return JSON_EXTRACT_PATH_INGESTION_APINAME;
|
|
3142
|
-
case 'WeakEtag':
|
|
3143
|
-
return '$.weakEtag';
|
|
3144
|
-
case 'Id':
|
|
3145
|
-
return '$.id';
|
|
3146
|
-
default:
|
|
3147
|
-
return `$.fields.${key}.value`;
|
|
3148
|
-
}
|
|
3149
|
-
}
|
|
3150
|
-
|
|
3151
|
-
function scopeToJoins(scope = '', settings) {
|
|
3152
|
-
if (scope !== 'ASSIGNEDTOME')
|
|
3153
|
-
return [];
|
|
3154
|
-
return [
|
|
3155
|
-
{
|
|
3156
|
-
alias: 'ServiceAppointment_AssignedResource',
|
|
3157
|
-
type: 'INNER',
|
|
3158
|
-
to: 'ServiceAppointment',
|
|
3159
|
-
conditions: [
|
|
3160
|
-
{
|
|
3161
|
-
type: PredicateType.single,
|
|
3162
|
-
alias: 'ServiceAppointment_AssignedResource',
|
|
3163
|
-
leftPath: JSON_EXTRACT_PATH_INGESTION_APINAME,
|
|
3164
|
-
operator: '=',
|
|
3165
|
-
value: 'AssignedResource',
|
|
3166
|
-
dataType: 'String',
|
|
3167
|
-
isCaseSensitive: true,
|
|
3168
|
-
},
|
|
3169
|
-
{
|
|
3170
|
-
leftPath: '$.id',
|
|
3171
|
-
rightPath: '$.fields.ServiceAppointmentId.value',
|
|
3172
|
-
},
|
|
3173
|
-
],
|
|
3174
|
-
apiName: 'AssignedResource',
|
|
3175
|
-
},
|
|
3176
|
-
{
|
|
3177
|
-
alias: 'ServiceAppointment_AssignedResource_ServiceResource',
|
|
3178
|
-
type: 'INNER',
|
|
3179
|
-
to: 'ServiceAppointment_AssignedResource',
|
|
3180
|
-
conditions: [
|
|
3181
|
-
{
|
|
3182
|
-
type: PredicateType.single,
|
|
3183
|
-
alias: 'ServiceAppointment_AssignedResource_ServiceResource',
|
|
3184
|
-
leftPath: JSON_EXTRACT_PATH_INGESTION_APINAME,
|
|
3185
|
-
operator: '=',
|
|
3186
|
-
value: 'ServiceResource',
|
|
3187
|
-
dataType: 'String',
|
|
3188
|
-
isCaseSensitive: true,
|
|
3189
|
-
},
|
|
3190
|
-
{
|
|
3191
|
-
leftPath: '$.fields.ServiceResourceId.value',
|
|
3192
|
-
rightPath: '$.id',
|
|
3193
|
-
},
|
|
3194
|
-
{
|
|
3195
|
-
type: PredicateType.single,
|
|
3196
|
-
alias: 'ServiceAppointment_AssignedResource_ServiceResource',
|
|
3197
|
-
leftPath: '$.fields.RelatedRecordId.value',
|
|
3198
|
-
operator: '=',
|
|
3199
|
-
value: settings.userId,
|
|
3200
|
-
dataType: 'String',
|
|
3201
|
-
isCaseSensitive: true,
|
|
3202
|
-
},
|
|
3203
|
-
],
|
|
3204
|
-
apiName: 'ServiceResource',
|
|
3205
|
-
},
|
|
3206
|
-
];
|
|
3207
|
-
}
|
|
3208
|
-
function scopeToPredicates(scope = '', settings) {
|
|
3209
|
-
if (scope !== 'MINE')
|
|
3210
|
-
return [];
|
|
3211
|
-
return [
|
|
3212
|
-
{
|
|
3213
|
-
type: PredicateType.single,
|
|
3214
|
-
leftPath: '$.fields.OwnerId.value',
|
|
3215
|
-
operator: '=',
|
|
3216
|
-
value: settings.userId,
|
|
3217
|
-
},
|
|
3218
|
-
];
|
|
3219
|
-
}
|
|
3220
|
-
|
|
3221
|
-
// Code lifted from https://github.com/MaxArt2501/base64-js/blob/master/base64.js
|
|
3222
|
-
// base64 character set, plus padding character (=)
|
|
3223
|
-
const b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
|
3224
|
-
const b64re = /^(?:[A-Za-z\d+/]{4})*?(?:[A-Za-z\d+/]{2}(?:==)?|[A-Za-z\d+/]{3}=?)?$/;
|
|
3225
|
-
function btoaPolyfill(input) {
|
|
3226
|
-
let bitmap, a, b, c;
|
|
3227
|
-
let result = '', i = 0;
|
|
3228
|
-
const rest = input.length % 3; // To determine the final padding
|
|
3229
|
-
for (; i < input.length;) {
|
|
3230
|
-
if ((a = input.charCodeAt(i++)) > 255 ||
|
|
3231
|
-
(b = input.charCodeAt(i++)) > 255 ||
|
|
3232
|
-
(c = input.charCodeAt(i++)) > 255) {
|
|
3233
|
-
throw new TypeError('Failed base64ToAscii encoding: The string to be encoded contains characters outside of the Latin1 range. ' +
|
|
3234
|
-
input);
|
|
3235
|
-
}
|
|
3236
|
-
bitmap = (a << 16) | (b << 8) | c;
|
|
3237
|
-
result +=
|
|
3238
|
-
b64.charAt((bitmap >> 18) & 63) +
|
|
3239
|
-
b64.charAt((bitmap >> 12) & 63) +
|
|
3240
|
-
b64.charAt((bitmap >> 6) & 63) +
|
|
3241
|
-
b64.charAt(bitmap & 63);
|
|
3242
|
-
}
|
|
3243
|
-
// If there's need of padding, replace the last 'A's with equal signs
|
|
3244
|
-
return rest ? result.slice(0, rest - 3) + '==='.substring(rest) : result;
|
|
3245
|
-
}
|
|
3246
|
-
function atobPolyfill(data) {
|
|
3247
|
-
// atob can work with strings with whitespaces, even inside the encoded part,
|
|
3248
|
-
// but only \t, \n, \f, \r and ' ', which can be stripped.
|
|
3249
|
-
let string = String(data).replace(/[\t\n\f\r ]+/g, '');
|
|
3250
|
-
if (!b64re.test(string))
|
|
3251
|
-
throw new TypeError("Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.");
|
|
3252
|
-
// Adding the padding if missing, for semplicity
|
|
3253
|
-
string += '=='.slice(2 - (string.length & 3));
|
|
3254
|
-
var bitmap, result = '', r1, r2, i = 0;
|
|
3255
|
-
for (; i < string.length;) {
|
|
3256
|
-
bitmap =
|
|
3257
|
-
(b64.indexOf(string.charAt(i++)) << 18) |
|
|
3258
|
-
(b64.indexOf(string.charAt(i++)) << 12) |
|
|
3259
|
-
((r1 = b64.indexOf(string.charAt(i++))) << 6) |
|
|
3260
|
-
(r2 = b64.indexOf(string.charAt(i++)));
|
|
3261
|
-
result +=
|
|
3262
|
-
r1 === 64
|
|
3263
|
-
? String.fromCharCode((bitmap >> 16) & 255)
|
|
3264
|
-
: r2 === 64
|
|
3265
|
-
? String.fromCharCode((bitmap >> 16) & 255, (bitmap >> 8) & 255)
|
|
3266
|
-
: String.fromCharCode((bitmap >> 16) & 255, (bitmap >> 8) & 255, bitmap & 255);
|
|
3267
|
-
}
|
|
3268
|
-
return result;
|
|
3269
|
-
}
|
|
3270
|
-
const base64encode = typeof btoa === 'function' ? btoa : btoaPolyfill;
|
|
3271
|
-
const base64decode = typeof atob === 'function' ? atob : atobPolyfill;
|
|
3272
|
-
|
|
3273
|
-
function cursorResolver(source) {
|
|
3274
|
-
return encodeV1Cursor(source.index);
|
|
3275
|
-
}
|
|
3276
|
-
function pageInfoResolver(source) {
|
|
3277
|
-
if (source.length === 0) {
|
|
3278
|
-
return {
|
|
3279
|
-
startCursor: null,
|
|
3280
|
-
endCursor: null,
|
|
3281
|
-
};
|
|
3282
|
-
}
|
|
3283
|
-
let startIndex = source[0].index;
|
|
3284
|
-
let endIndex = source[source.length - 1].index;
|
|
3285
|
-
return {
|
|
3286
|
-
startCursor: encodeV1Cursor(startIndex),
|
|
3287
|
-
endCursor: encodeV1Cursor(endIndex),
|
|
3288
|
-
};
|
|
3289
|
-
}
|
|
3290
|
-
function pageResultCountResolver(source) {
|
|
3291
|
-
return source.length;
|
|
3292
|
-
}
|
|
3293
|
-
function encodeV1Cursor(index) {
|
|
3294
|
-
return base64encode(`v1:${index}`);
|
|
3295
|
-
}
|
|
3296
|
-
const cursorRegex = /^v1:(?<index>\d+)$/;
|
|
3297
|
-
function decodeV1Cursor(base64cursor) {
|
|
3298
|
-
const cursor = base64decode(base64cursor);
|
|
3299
|
-
if (!cursor) {
|
|
3300
|
-
// eslint-disable-next-line @salesforce/lds/no-error-in-production
|
|
3301
|
-
throw new Error('Unable to parse cursor');
|
|
3302
|
-
}
|
|
3303
|
-
const found = cursor.match(cursorRegex);
|
|
3304
|
-
if (!found || !found.groups) {
|
|
3305
|
-
// eslint-disable-next-line @salesforce/lds/no-error-in-production
|
|
3306
|
-
throw new Error('Unable to parse cursor');
|
|
3307
|
-
}
|
|
3308
|
-
return Number(found.groups.index);
|
|
3309
|
-
}
|
|
3310
|
-
|
|
3311
|
-
/*
|
|
3312
|
-
resolves connections...
|
|
3313
|
-
*/
|
|
3314
|
-
async function connectionResolver(obj, args, context, info) {
|
|
3315
|
-
let { recordRepresentation: parentRecord, ingestionTimestamp = 0 } = obj;
|
|
3316
|
-
if (!parentRecord && excludeStaleRecordsGate.isOpen({ fallback: false })) {
|
|
3317
|
-
// at our record query we fetch each ingestion time stamp and pass it down to each lower resolver to query against
|
|
3318
|
-
ingestionTimestamp = await fetchIngestionTimeStampFromDatabase(info.fieldName, info, args, context.query);
|
|
3319
|
-
}
|
|
3320
|
-
const { query, objectInfos, draftFunctions } = context;
|
|
3321
|
-
let joins = [];
|
|
3322
|
-
let alias = info.fieldName;
|
|
3323
|
-
let childRelationshipFieldName = undefined;
|
|
3324
|
-
if (parentRecord) {
|
|
3325
|
-
context.seenRecordIds.add(parentRecord.id);
|
|
3326
|
-
const parentApiName = parentRecord.apiName;
|
|
3327
|
-
const parentObjectInfo = objectInfos[parentApiName];
|
|
3328
|
-
const childRelationship = parentObjectInfo &&
|
|
3329
|
-
parentObjectInfo.childRelationships.find((rel) => rel.relationshipName === info.fieldName);
|
|
3330
|
-
// or emit/throw if we want to report it
|
|
3331
|
-
if (!childRelationship)
|
|
3332
|
-
return [];
|
|
3333
|
-
alias = childRelationship.childObjectApiName;
|
|
3334
|
-
childRelationshipFieldName = childRelationship.fieldName;
|
|
3335
|
-
}
|
|
3336
|
-
// Alias starts as entity's ApiName
|
|
3337
|
-
const predicates = [
|
|
3338
|
-
...filterToPredicates(args.where, alias, alias, context.objectInfos, joins, draftFunctions),
|
|
3339
|
-
...scopeToPredicates(args.scope, context.settings),
|
|
3340
|
-
...childRelationshipToPredicates(childRelationshipFieldName, parentRecord ? parentRecord.id : undefined),
|
|
3341
|
-
];
|
|
3342
|
-
const scopeJoins = scopeToJoins(args.scope, context.settings);
|
|
3343
|
-
joins.push(...scopeJoins);
|
|
3344
|
-
let offset = 0;
|
|
3345
|
-
if (args.after) {
|
|
3346
|
-
offset = decodeV1Cursor(args.after) + 1;
|
|
3347
|
-
}
|
|
3348
|
-
// Alias starts as entity's ApiName
|
|
3349
|
-
const queryConfig = {
|
|
3350
|
-
alias,
|
|
3351
|
-
joins,
|
|
3352
|
-
predicates,
|
|
3353
|
-
orderBy: orderByToPredicate(args.orderBy, alias, alias, context.objectInfos),
|
|
3354
|
-
limit: args.first,
|
|
3355
|
-
offset: offset,
|
|
3356
|
-
ingestionTimestamp,
|
|
3357
|
-
};
|
|
3358
|
-
const { sql, bindings } = buildQuery(queryConfig);
|
|
3359
|
-
const results = await query(sql, bindings);
|
|
3360
|
-
//map each sql result with the ingestion timestamp to pass it down a level
|
|
3361
|
-
return results.rows
|
|
3362
|
-
.map((row) => parse(row[0]))
|
|
3363
|
-
.map((recordRepresentation, index) => {
|
|
3364
|
-
context.seenRecordIds.add(recordRepresentation.id);
|
|
3365
|
-
return {
|
|
3366
|
-
recordRepresentation,
|
|
3367
|
-
ingestionTimestamp,
|
|
3368
|
-
index: index + offset,
|
|
3369
|
-
};
|
|
3370
|
-
});
|
|
3371
|
-
}
|
|
3372
|
-
/**
|
|
3373
|
-
* Converts a childRelationship into a predicate
|
|
3374
|
-
* @param childRelationshipFieldName Reference ID field name to its parent record. A defined `childRelationshipFieldName` string indicates that a child relationship exists
|
|
3375
|
-
* and a relationship predicate needs to be put into place. For example, `ServiceAppointment` has a child relationship `ServiceResources`, whose entity name is `AssignedResource`.
|
|
3376
|
-
* Once the parent `ServiceAppointment` record comes back, its child connection starts to resolve. Child `AssignedResource` record needs to reference to parent Id using field `ServiceAppointmentId`.
|
|
3377
|
-
* @param parentId prarent record Id
|
|
3378
|
-
* @returns predicate array consists at most 1 predicate
|
|
3379
|
-
*/
|
|
3380
|
-
function childRelationshipToPredicates(childRelationshipFieldName, parentId) {
|
|
3381
|
-
const predicates = [];
|
|
3382
|
-
if (childRelationshipFieldName !== undefined && parentId !== undefined) {
|
|
3383
|
-
predicates.push({
|
|
3384
|
-
type: PredicateType.single,
|
|
3385
|
-
leftPath: `$.fields.${childRelationshipFieldName}.value`,
|
|
3386
|
-
operator: '=',
|
|
3387
|
-
value: parentId,
|
|
3388
|
-
});
|
|
3389
|
-
}
|
|
3390
|
-
return predicates;
|
|
3391
|
-
}
|
|
3392
|
-
/**
|
|
3393
|
-
* fetches a query level ingestion time stamp from the L2 cache
|
|
3394
|
-
* if no query has been seen then the timestamp is 0
|
|
3395
|
-
* @param apiName
|
|
3396
|
-
* @param info
|
|
3397
|
-
* @param args
|
|
3398
|
-
* @param query
|
|
3399
|
-
* @returns
|
|
3400
|
-
*/
|
|
3401
|
-
async function fetchIngestionTimeStampFromDatabase(apiName, info, args, query) {
|
|
3402
|
-
const { operation, variableValues } = info;
|
|
3403
|
-
// if we cannot find the query key in the database then default to 0 as we assume we have not seen the query
|
|
3404
|
-
// and all the data is not stale
|
|
3405
|
-
let ingestionTimestamp = 0;
|
|
3406
|
-
if (info.fieldNodes.length > 0 && info.fieldNodes[0].arguments !== undefined) {
|
|
3407
|
-
const key = buildKeyStringForRecordQuery(operation,
|
|
3408
|
-
// join varables passed from query to the argument variables given from the AST
|
|
3409
|
-
{ ...variableValues, ...args }, info.fieldNodes[0].arguments, apiName);
|
|
3410
|
-
return readIngestionTimestampForKey(key, query);
|
|
3411
|
-
}
|
|
3412
|
-
return ingestionTimestamp;
|
|
3413
|
-
}
|
|
3414
|
-
/**
|
|
3415
|
-
* Builds the top level record query key based on AST data
|
|
3416
|
-
* @param operation
|
|
3417
|
-
* @param variables
|
|
3418
|
-
* @param argumentNodes
|
|
3419
|
-
* @param currentFieldName
|
|
3420
|
-
* @returns
|
|
3421
|
-
*/
|
|
3422
|
-
function buildKeyStringForRecordQuery(operation, variables, argumentNodes, currentFieldName) {
|
|
3423
|
-
const queryKey = buildQueryTypeStringKey({
|
|
3424
|
-
luvio: {},
|
|
3425
|
-
keyPrefix: 'UiApi',
|
|
3426
|
-
schemaName: 'uiapi',
|
|
3427
|
-
queryTypeName: 'Query',
|
|
3428
|
-
operationNode: operation,
|
|
3429
|
-
variables,
|
|
3430
|
-
fragmentMap: {},
|
|
3431
|
-
});
|
|
3432
|
-
const filteredArgumentNodes = assign$1([], argumentNodes).filter((node) => node.name.value !== 'first' && node.name.value !== 'after');
|
|
3433
|
-
const argumentString = filteredArgumentNodes.length > 0
|
|
3434
|
-
? '__' + serializeFieldArguments(filteredArgumentNodes, variables)
|
|
3435
|
-
: '';
|
|
3436
|
-
return `${queryKey}__uiapi__query__${currentFieldName}${argumentString}`;
|
|
3437
|
-
}
|
|
3438
|
-
|
|
3439
|
-
function passThroughResolver(source) {
|
|
3440
|
-
return source;
|
|
3441
|
-
}
|
|
3442
|
-
|
|
3443
|
-
/**
|
|
3444
|
-
*
|
|
3445
|
-
* @param schema GraphQL Schema generated from ObjectInfos
|
|
3446
|
-
* @param polyFields The record types which support polymorphic fields. For example, 'Owner' has the 'Record' interface which is a polymorphic fields. The 'User' and 'Group' are the concrete types which support 'Owner' interface.
|
|
3447
|
-
* @returns Updated schema
|
|
3448
|
-
*/
|
|
3449
|
-
function addResolversToSchema(schema, polyFields) {
|
|
3450
|
-
// Polymorphic field type is 'recordInterface'
|
|
3451
|
-
let recordInterface = undefined;
|
|
3452
|
-
let baseRecord = undefined;
|
|
3453
|
-
// Concrete types for Polymorphic field
|
|
3454
|
-
const polyTypes = [];
|
|
3455
|
-
for (const type of values$1(schema.getTypeMap())) {
|
|
3456
|
-
if (type.name === 'Record') {
|
|
3457
|
-
recordInterface = type;
|
|
3458
|
-
}
|
|
3459
|
-
else if (type.name === 'RecordRepresentation') {
|
|
3460
|
-
baseRecord = type;
|
|
3461
|
-
}
|
|
3462
|
-
if (!isObjectType(type))
|
|
3463
|
-
continue;
|
|
3464
|
-
// Finds entity record type which supports polymorphism.
|
|
3465
|
-
if (polyFields.find((fieldTypeName) => fieldTypeName === type.name) !== undefined) {
|
|
3466
|
-
polyTypes.push(type);
|
|
3467
|
-
}
|
|
3468
|
-
const fields = values$1(type.getFields());
|
|
3469
|
-
// initialize the fields of current type with default behavior
|
|
3470
|
-
for (const field of fields) {
|
|
3471
|
-
field.resolve = defaultFieldResolver;
|
|
3472
|
-
}
|
|
3473
|
-
if (type.name === 'Query') {
|
|
3474
|
-
for (const field of fields) {
|
|
3475
|
-
field.resolve = passThroughResolver;
|
|
3476
|
-
}
|
|
3477
|
-
}
|
|
3478
|
-
if (type.name === 'UIAPI') {
|
|
3479
|
-
for (const field of fields) {
|
|
3480
|
-
if (field.name === 'query') {
|
|
3481
|
-
field.resolve = passThroughResolver;
|
|
3482
|
-
}
|
|
3483
|
-
else {
|
|
3484
|
-
const fieldName = field.name;
|
|
3485
|
-
field.resolve = (_obj, _args, { snapshot }) => {
|
|
3486
|
-
if (!snapshot.data)
|
|
3487
|
-
return null;
|
|
3488
|
-
if (!snapshot.data.data)
|
|
3489
|
-
return null;
|
|
3490
|
-
return snapshot.data.data.uiapi[fieldName];
|
|
3491
|
-
};
|
|
3492
|
-
}
|
|
3493
|
-
}
|
|
3494
|
-
}
|
|
3495
|
-
if (type.name === 'RecordQuery') {
|
|
3496
|
-
// {
|
|
3497
|
-
// uiapi {
|
|
3498
|
-
// query { <-----------|
|
|
3499
|
-
// Account |
|
|
3500
|
-
// User |
|
|
3501
|
-
// Opportunity |
|
|
3502
|
-
// } <-----------------|
|
|
3503
|
-
// }
|
|
3504
|
-
// }
|
|
3505
|
-
// Fields of the `RecordQuery` type are the record queries for the entity types
|
|
3506
|
-
// supported for the org
|
|
3507
|
-
for (const recordQuery of fields) {
|
|
3508
|
-
recordQuery.resolve = connectionResolver;
|
|
3509
|
-
}
|
|
3510
|
-
}
|
|
3511
|
-
if (type.name.endsWith('Connection')) {
|
|
3512
|
-
// {
|
|
3513
|
-
// uiapi {
|
|
3514
|
-
// query {
|
|
3515
|
-
// ServiceAppointment @category(name: "recordQuery") { <---|
|
|
3516
|
-
// totalCount |
|
|
3517
|
-
// edges { |
|
|
3518
|
-
// node { |
|
|
3519
|
-
// Id |
|
|
3520
|
-
// } |
|
|
3521
|
-
// } |
|
|
3522
|
-
// } <----------------------------------------------------|
|
|
3523
|
-
// }
|
|
3524
|
-
// }
|
|
3525
|
-
// }
|
|
3526
|
-
for (const field of fields) {
|
|
3527
|
-
switch (field.name) {
|
|
3528
|
-
case 'edges':
|
|
3529
|
-
field.resolve = passThroughResolver;
|
|
3530
|
-
break;
|
|
3531
|
-
case 'pageInfo':
|
|
3532
|
-
field.resolve = pageInfoResolver;
|
|
3533
|
-
break;
|
|
3534
|
-
case 'pageResultCount':
|
|
3535
|
-
field.resolve = pageResultCountResolver;
|
|
3536
|
-
break;
|
|
3537
|
-
default:
|
|
3538
|
-
field.resolve = defaultFieldResolver;
|
|
3539
|
-
}
|
|
3540
|
-
}
|
|
3541
|
-
}
|
|
3542
|
-
if (type.name.endsWith('Edge')) {
|
|
3543
|
-
// {
|
|
3544
|
-
// uiapi {
|
|
3545
|
-
// query {
|
|
3546
|
-
// ServiceAppointment @category(name: "recordQuery") {
|
|
3547
|
-
// edges { <--|
|
|
3548
|
-
// node { |
|
|
3549
|
-
// Id |
|
|
3550
|
-
// } |
|
|
3551
|
-
// } <--|
|
|
3552
|
-
// }
|
|
3553
|
-
// }
|
|
3554
|
-
// }
|
|
3555
|
-
// }
|
|
3556
|
-
for (const field of fields) {
|
|
3557
|
-
if (field.name === 'node') {
|
|
3558
|
-
field.resolve = passThroughResolver;
|
|
3559
|
-
}
|
|
3560
|
-
else if (field.name === 'cursor') {
|
|
3561
|
-
field.resolve = cursorResolver;
|
|
3562
|
-
}
|
|
3563
|
-
}
|
|
3564
|
-
}
|
|
3565
|
-
if (isRecordType(type)) {
|
|
3566
|
-
// All the generic named GraphQL Objects that map to Record Queries ("Account", "ServiceAppointment")
|
|
3567
|
-
// {
|
|
3568
|
-
// uiapi {
|
|
3569
|
-
// query {
|
|
3570
|
-
// ServiceAppointment @category(name: "recordQuery") {
|
|
3571
|
-
// edges {
|
|
3572
|
-
// node { <--|
|
|
3573
|
-
// Id |
|
|
3574
|
-
// } <--|
|
|
3575
|
-
// }
|
|
3576
|
-
// }
|
|
3577
|
-
// }
|
|
3578
|
-
// }
|
|
3579
|
-
// }
|
|
3580
|
-
for (const field of fields) {
|
|
3581
|
-
switch (field.name) {
|
|
3582
|
-
case 'Id':
|
|
3583
|
-
field.resolve = ({ recordRepresentation: record }) => record.id;
|
|
3584
|
-
break;
|
|
3585
|
-
case 'ApiName':
|
|
3586
|
-
field.resolve = ({ recordRepresentation: record }) => record.apiName;
|
|
3587
|
-
break;
|
|
3588
|
-
case 'WeakEtag':
|
|
3589
|
-
field.resolve = ({ recordRepresentation: record }) => record.weakEtag;
|
|
3590
|
-
break;
|
|
3591
|
-
case '_drafts':
|
|
3592
|
-
field.resolve = ({ recordRepresentation: record, }) => {
|
|
3593
|
-
return record.drafts ? record.drafts : null;
|
|
3594
|
-
};
|
|
3595
|
-
break;
|
|
3596
|
-
case 'LastModifiedById':
|
|
3597
|
-
field.resolve = ({ recordRepresentation: record }) => {
|
|
3598
|
-
return record.lastModifiedById
|
|
3599
|
-
? { value: record.lastModifiedById }
|
|
3600
|
-
: null;
|
|
3601
|
-
};
|
|
3602
|
-
break;
|
|
3603
|
-
case 'LastModifiedDate':
|
|
3604
|
-
field.resolve = ({ recordRepresentation: record }) => {
|
|
3605
|
-
// In UIAPI record reps, LastModifiedDate might be present as a field,
|
|
3606
|
-
// which will include both the value and displayValue
|
|
3607
|
-
if (record.fields['LastModifiedDate']) {
|
|
3608
|
-
return record.fields['LastModifiedDate'];
|
|
3609
|
-
}
|
|
3610
|
-
// If the field is not present, just return the value of the root property
|
|
3611
|
-
return record.lastModifiedDate
|
|
3612
|
-
? { value: record.lastModifiedDate }
|
|
3613
|
-
: null;
|
|
3614
|
-
};
|
|
3615
|
-
break;
|
|
3616
|
-
case 'SystemModstamp':
|
|
3617
|
-
field.resolve = ({ recordRepresentation: record }) => {
|
|
3618
|
-
return record.systemModstamp ? { value: record.systemModstamp } : null;
|
|
3619
|
-
};
|
|
3620
|
-
break;
|
|
3621
|
-
case 'RecordTypeId':
|
|
3622
|
-
field.resolve = ({ recordRepresentation: record }) => {
|
|
3623
|
-
return record.recordTypeId ? { value: record.recordTypeId } : null;
|
|
3624
|
-
};
|
|
3625
|
-
break;
|
|
3626
|
-
case 'DisplayValue':
|
|
3627
|
-
field.resolve = recordDisplayValueResolver;
|
|
3628
|
-
break;
|
|
3629
|
-
default: {
|
|
3630
|
-
const recordFieldType = schema.getType(field.type.name);
|
|
3631
|
-
// Both concrete record type and 'record' interface type(polymorphic field) uses 'RecordLoader' to resolve.
|
|
3632
|
-
if ((isObjectType(recordFieldType) &&
|
|
3633
|
-
recordFieldType
|
|
3634
|
-
.getInterfaces()
|
|
3635
|
-
.find((iface) => iface.name === 'Record')) ||
|
|
3636
|
-
(recordFieldType && recordFieldType.name === 'Record')) {
|
|
3637
|
-
field.resolve = async function relationResolver(obj, _args, { Record, seenRecordIds }) {
|
|
3638
|
-
const fetchRecordOrNull = async (key) => {
|
|
3639
|
-
const recordRepresentation = await Record.load(key);
|
|
3640
|
-
return recordRepresentation !== null
|
|
3641
|
-
? {
|
|
3642
|
-
recordRepresentation,
|
|
3643
|
-
ingestionTimestamp,
|
|
3644
|
-
}
|
|
3645
|
-
: null;
|
|
3646
|
-
};
|
|
3647
|
-
const { recordRepresentation: record, ingestionTimestamp } = obj;
|
|
3648
|
-
let id = undefined;
|
|
3649
|
-
if (field.name === 'RecordType') {
|
|
3650
|
-
// RecordTypeId has special handling during ingest and is
|
|
3651
|
-
// not in record.fields, so check for it at the UIAPI root property location
|
|
3652
|
-
id = record.recordTypeId;
|
|
3653
|
-
}
|
|
3654
|
-
else if (field.name.endsWith('__r')) {
|
|
3655
|
-
// Custom relationships end in `__r` and the corresponding ID field should be `__c`
|
|
3656
|
-
let fieldName = field.name.replace('__r', '__c');
|
|
3657
|
-
id = record.fields[fieldName] && record.fields[fieldName].value;
|
|
3658
|
-
}
|
|
3659
|
-
else {
|
|
3660
|
-
// Standard relationships are just FieldNameId
|
|
3661
|
-
let fieldName = field.name + 'Id';
|
|
3662
|
-
id = record.fields[fieldName] && record.fields[fieldName].value;
|
|
3663
|
-
}
|
|
3664
|
-
if (!id || typeof id !== 'string') {
|
|
3665
|
-
// possibly field injection did not inject the necessary Id field
|
|
3666
|
-
// for the relationship, or we found a non-string value.
|
|
3667
|
-
return null;
|
|
3668
|
-
}
|
|
3669
|
-
seenRecordIds.add(id);
|
|
3670
|
-
return fetchRecordOrNull(id);
|
|
3671
|
-
};
|
|
3672
|
-
}
|
|
3673
|
-
else if (isObjectType(recordFieldType) &&
|
|
3674
|
-
field.type.name.endsWith('Connection')) {
|
|
3675
|
-
// spanning field to a connection
|
|
3676
|
-
field.resolve = connectionResolver;
|
|
3677
|
-
}
|
|
3678
|
-
else {
|
|
3679
|
-
field.resolve = function recordFieldResolver({ recordRepresentation: record, }) {
|
|
3680
|
-
return record.fields[field.name] || null;
|
|
3681
|
-
};
|
|
3682
|
-
}
|
|
3683
|
-
}
|
|
3684
|
-
}
|
|
3685
|
-
}
|
|
3686
|
-
}
|
|
3687
|
-
}
|
|
3688
|
-
if (recordInterface !== undefined && baseRecord !== undefined) {
|
|
3689
|
-
// Applys 'resolveType' of GraphQLInterfaceType to 'Record' interface. Since all the heterogenous types are named as 'apiName', the type with same name as the loaded record 'apiName' property is the type wanted.
|
|
3690
|
-
// GraphQL executor would match InLineFragment' condition with type and keeps the deeper level field resolving going.
|
|
3691
|
-
recordInterface.resolveType = function ({ recordRepresentation: value, }) {
|
|
3692
|
-
const targetType = polyTypes.find((type) => type.name === value.apiName);
|
|
3693
|
-
return targetType === undefined ? baseRecord : targetType;
|
|
3694
|
-
};
|
|
3695
|
-
}
|
|
3696
|
-
return schema;
|
|
3697
|
-
}
|
|
3698
|
-
/**
|
|
3699
|
-
* Resolver to fetch the DisplayValue of a graphql record.
|
|
3700
|
-
* This is taking the main Name field for the record
|
|
3701
|
-
* type and using it for the DisplayValue.
|
|
3702
|
-
*
|
|
3703
|
-
* This allows us to be more performant without needing to call
|
|
3704
|
-
* another SQL lookup for the cached graphql object of the RAML record.
|
|
3705
|
-
*/
|
|
3706
|
-
function recordDisplayValueResolver({ recordRepresentation },
|
|
3707
|
-
// not used, but gets sent in the resolver
|
|
3708
|
-
_, { objectInfos }) {
|
|
3709
|
-
const { apiName } = recordRepresentation;
|
|
3710
|
-
const objectInfo = objectInfos[apiName];
|
|
3711
|
-
if (objectInfo !== undefined) {
|
|
3712
|
-
const { nameFields, fields } = objectInfo;
|
|
3713
|
-
let nameField;
|
|
3714
|
-
// use the field Name if it exists or the first field name
|
|
3715
|
-
if (nameFields.length !== 0) {
|
|
3716
|
-
nameField = nameFields.find((x) => x === 'Name');
|
|
3717
|
-
if (nameField === undefined) {
|
|
3718
|
-
nameField = nameFields[0];
|
|
3719
|
-
}
|
|
3720
|
-
}
|
|
3721
|
-
if (nameField !== undefined) {
|
|
3722
|
-
nameField = fields[nameField].apiName;
|
|
3723
|
-
const nameFieldRep = recordRepresentation.fields[nameField];
|
|
3724
|
-
if (nameFieldRep !== undefined) {
|
|
3725
|
-
return nameFieldRep.value;
|
|
3726
|
-
}
|
|
3727
|
-
}
|
|
3728
|
-
}
|
|
3729
|
-
return null;
|
|
3730
|
-
}
|
|
3731
|
-
function isRecordType(type) {
|
|
3732
|
-
const interfaces = type.getInterfaces();
|
|
3733
|
-
return Boolean(interfaces.find((iface) => iface.name === 'Record'));
|
|
3734
|
-
}
|
|
3735
|
-
|
|
3736
|
-
var uiapiSchemaString = "scalar String\nscalar DateTime\nscalar Currency\nscalar ID\nscalar Boolean\nscalar Longitude\nscalar Float\nscalar MultiPicklist\nscalar Base64\nscalar Url\nscalar PhoneNumber\nscalar Email\nscalar TextArea\nscalar Latitude\nscalar Picklist\nscalar RichTextArea\nscalar EncryptedString\nscalar Double\nscalar Long\nscalar JSON\nscalar Time\nscalar Int\nscalar Percent\nscalar LongTextArea\nscalar IdOrRef\nscalar Date\ntype PercentAggregate implements FieldValue {\n value: Percent\n displayValue: String\n avg: DoubleValue\n count: LongValue\n countDistinct: LongValue\n format: String\n max: PercentValue\n min: PercentValue\n sum: PercentValue\n}\n\ntype StringAggregate implements FieldValue {\n value: String\n displayValue: String\n count: LongValue\n countDistinct: LongValue\n grouping: IntValue\n label: String\n max: StringValue\n min: StringValue\n}\n\ntype Query {\n uiapi: UIAPI!\n setup: Setup__Setup!\n}\n\ninput EmailOperators {\n eq: Email\n ne: Email\n like: Email\n lt: Email\n gt: Email\n lte: Email\n gte: Email\n in: [Email]\n nin: [Email]\n}\n\ninput PolymorphicParentRelationshipRecordOrderBy @generic {\n RecordOrderBy: RecordOrderBy @fieldCategory\n}\n\ninput DoubleOperators {\n eq: Double\n ne: Double\n lt: Double\n gt: Double\n lte: Double\n gte: Double\n in: [Double]\n nin: [Double]\n}\n\ntype DateOnlyAggregation {\n value: Date\n format: String\n}\n\ntype RecordCreatePayload @generic {\n Record: RecordRepresentation\n}\n\ntype DateAggregate implements FieldValue {\n value: Date\n displayValue: String\n calendarMonth: DateFunctionAggregation\n calendarQuarter: DateFunctionAggregation\n calendarYear: DateFunctionAggregation\n count: LongValue\n countDistinct: LongValue\n dayInMonth: DateFunctionAggregation\n dayInWeek: DateFunctionAggregation\n dayInYear: DateFunctionAggregation\n fiscalMonth: DateFunctionAggregation\n fiscalQuarter: DateFunctionAggregation\n fiscalYear: DateFunctionAggregation\n format: String\n grouping: IntValue\n max: DateValue\n min: DateValue\n weekInMonth: DateFunctionAggregation\n weekInYear: DateFunctionAggregation\n}\n\ninput PolymorphicParentRelationshipGroupBy @generic {\n RecordGroupBy: RecordGroupBy @fieldCategory\n}\n\nenum GroupByFunction {\n DAY_IN_WEEK\n DAY_IN_MONTH\n DAY_IN_YEAR\n WEEK_IN_MONTH\n WEEK_IN_YEAR\n CALENDAR_MONTH\n CALENDAR_QUARTER\n CALENDAR_YEAR\n FISCAL_MONTH\n FISCAL_QUARTER\n FISCAL_YEAR\n DAY_ONLY\n HOUR_IN_DAY\n}\n\ntype RecordTypeInfo {\n available: Boolean!\n defaultRecordTypeMapping: Boolean!\n master: Boolean!\n name: String\n recordTypeId: ID\n}\n\ninput UIAPIMutationsInput {\n allOrNone: Boolean = true\n}\n\ntype BooleanValue implements FieldValue {\n value: Boolean\n displayValue: String\n}\n\ntype ReferenceToInfo {\n ApiName: String!\n nameFields: [String]!\n objectInfo: ObjectInfo\n}\n\ninterface FieldValue {\n displayValue: String\n}\n\ntype LongitudeValue implements FieldValue {\n value: Longitude\n displayValue: String\n}\n\ntype StringValue implements FieldValue {\n value: String\n displayValue: String\n label: String\n}\n\ntype IntValue implements FieldValue {\n value: Int\n displayValue: String\n format: String\n}\n\ntype UrlValue implements FieldValue {\n value: Url\n displayValue: String\n}\n\ninput IdOperators {\n eq: ID\n ne: ID\n lt: ID\n gt: ID\n lte: ID\n gte: ID\n in: [ID]\n nin: [ID]\n inq: JoinInput\n ninq: JoinInput\n}\n\ninput Setup__SetupOrderBy @generic {\n orderableField: OrderByClause @fieldCategory\n orderableGeolocationField: OrderByGeolocationClause @fieldCategory\n orderableParentRelationship: Setup__SetupOrderBy @fieldCategory\n orderablePolymorphicParentRelationship: Setup__SetupPolymorphicParentRelationshipRecordOrderBy @fieldCategory\n}\n\ntype LongAggregate implements FieldValue {\n value: Long\n displayValue: String\n avg: DoubleValue\n count: LongValue\n countDistinct: LongValue\n format: String\n grouping: IntValue\n max: LongValue\n min: LongValue\n sum: LongValue\n}\n\ntype PhoneNumberAggregate implements FieldValue {\n value: PhoneNumber\n displayValue: String\n count: LongValue\n countDistinct: LongValue\n grouping: IntValue\n max: PhoneNumberValue\n min: PhoneNumberValue\n}\n\ninput TimeOperators {\n eq: Time\n ne: Time\n lt: Time\n gt: Time\n lte: Time\n gte: Time\n in: [Time]\n nin: [Time]\n}\n\ntype PicklistValue implements FieldValue {\n value: Picklist\n displayValue: String\n label: String\n}\n\ntype CurrencyAggregate implements FieldValue {\n value: Currency\n displayValue: String\n avg: DoubleValue\n count: LongValue\n countDistinct: LongValue\n format: String\n max: CurrencyValue\n min: CurrencyValue\n sum: CurrencyValue\n}\n\ntype RelatedListInfo {\n childApiName: String!\n relatedListName: String!\n label: String!\n displayColumns: [ListColumn!]!\n orderedByInfo: [ListOrder!]!\n parentApiName: String!\n fieldApiName: String!\n}\n\ninput StringOperators {\n eq: String\n ne: String\n like: String\n lt: String\n gt: String\n lte: String\n gte: String\n in: [String]\n nin: [String]\n}\n\ntype UIAPI {\n query: RecordQuery!\n aggregate: RecordQueryAggregate!\n objectInfos(apiNames: [String], locale: String): [ObjectInfo]\n relatedListByName(parentApiName: String!, relatedListName: String!): RelatedListInfo\n}\n\ninput MultiPicklistOperators {\n eq: MultiPicklist\n ne: MultiPicklist\n includes: [MultiPicklist]\n excludes: [MultiPicklist]\n}\n\ntype DateTimeAggregate implements FieldValue {\n value: DateTime\n displayValue: String\n calendarMonth: DateFunctionAggregation\n calendarQuarter: DateFunctionAggregation\n calendarYear: DateFunctionAggregation\n count: LongValue\n countDistinct: LongValue\n dayInMonth: DateFunctionAggregation\n dayInWeek: DateFunctionAggregation\n dayInYear: DateFunctionAggregation\n dayOnly: DateOnlyAggregation\n fiscalMonth: DateFunctionAggregation\n fiscalQuarter: DateFunctionAggregation\n fiscalYear: DateFunctionAggregation\n format: String\n hourInDay: DateFunctionAggregation\n max: DateTimeValue\n min: DateTimeValue\n weekInMonth: DateFunctionAggregation\n weekInYear: DateFunctionAggregation\n}\n\ninput BooleanOperators {\n eq: Boolean\n ne: Boolean\n}\n\ntype EmailAggregate implements FieldValue {\n value: Email\n displayValue: String\n count: LongValue\n countDistinct: LongValue\n grouping: IntValue\n max: EmailValue\n min: EmailValue\n}\n\n#enum OrderByType {\n#}\n\ninput GroupByDateFunction {\n function: GroupByFunction\n}\n\ntype RichTextAreaValue implements FieldValue {\n value: RichTextArea\n displayValue: String\n}\n\ntype MultiPicklistValue implements FieldValue {\n value: MultiPicklist\n displayValue: String\n label: String\n}\n\ntype Setup__SetupEdge @generic {\n node: Setup__EntityRepresentation\n cursor: String!\n}\n\ninput DatePrimitiveOperators {\n eq: Date\n ne: Date\n lt: Date\n gt: Date\n lte: Date\n gte: Date\n in: [Date]\n nin: [Date]\n}\n\ntype TimeAggregate implements FieldValue {\n value: Time\n displayValue: String\n format: String\n hourInDay: DateFunctionAggregation\n}\n\ntype __Type {\n kind: __TypeKind!\n name: String\n description: String\n fields(includeDeprecated: Boolean = false): [__Field!]\n interfaces: [__Type!]\n possibleTypes: [__Type!]\n enumValues(includeDeprecated: Boolean = false): [__EnumValue!]\n inputFields: [__InputValue!]\n ofType: __Type\n}\n\ntype ListColumn {\n fieldApiName: String!\n label: String!\n lookupId: String\n sortable: Boolean\n}\n\ntype Setup__SetupQuery {\n recordQuery(first: Int, after: String, where: Setup__SetupFilter, orderBy: Setup__SetupOrderBy, scope: String, upperBound: Int): Setup__SetupConnection @fieldCategory\n}\n\ntype Setup__EntityRepresentation @generic {\n Id: ID!\n ApiName: String!\n IntValue: IntValue @fieldCategory\n StringValue: StringValue @fieldCategory\n BooleanValue: BooleanValue @fieldCategory\n IDValue: IDValue @fieldCategory\n DateTimeValue: DateTimeValue @fieldCategory\n TimeValue: TimeValue @fieldCategory\n DateValue: DateValue @fieldCategory\n TextAreaValue: TextAreaValue @fieldCategory\n LongTextAreaValue: LongTextAreaValue @fieldCategory\n RichTextAreaValue: RichTextAreaValue @fieldCategory\n PhoneNumberValue: PhoneNumberValue @fieldCategory\n EmailValue: EmailValue @fieldCategory\n UrlValue: UrlValue @fieldCategory\n EncryptedStringValue: EncryptedStringValue @fieldCategory\n CurrencyValue: CurrencyValue @fieldCategory\n LongitudeValue: LongitudeValue @fieldCategory\n LatitudeValue: LatitudeValue @fieldCategory\n PicklistValue: PicklistValue @fieldCategory\n MultiPicklistValue: MultiPicklistValue @fieldCategory\n LongValue: LongValue @fieldCategory\n DoubleValue: DoubleValue @fieldCategory\n PercentValue: PercentValue @fieldCategory\n Base64Value: Base64Value @fieldCategory\n JSONValue: JSONValue @fieldCategory\n parentRelationship: Setup__EntityRepresentation @fieldCategory\n polymorphicParentRelationship: Setup__SetupPolymorphicParentRelationship @fieldCategory\n childRelationship(first: Int, after: String, where: Setup__SetupFilter, orderBy: Setup__SetupOrderBy, upperBound: Int): Setup__SetupConnection @fieldCategory\n CompoundField: CompoundField @fieldCategory\n}\n\ntype LatitudeAggregate implements FieldValue {\n value: Latitude\n displayValue: String\n avg: DoubleValue\n count: LongValue\n countDistinct: LongValue\n max: LatitudeValue\n min: LatitudeValue\n sum: DoubleValue\n}\n\ninput CurrencyOperators {\n eq: Currency\n ne: Currency\n lt: Currency\n gt: Currency\n lte: Currency\n gte: Currency\n in: [Currency]\n nin: [Currency]\n}\n\ninput DistanceInput {\n latitude: Latitude!\n longitude: Longitude!\n}\n\nunion PolymorphicParentRelationship @generic = RecordRepresentation\n\nenum AggregateOrderByNumberFunction {\n AVG\n COUNT\n COUNT_DISTINCT\n MAX\n MIN\n SUM\n}\n\ntype LongTextAreaValue implements FieldValue {\n value: LongTextArea\n displayValue: String\n}\n\ntype LatitudeValue implements FieldValue {\n value: Latitude\n displayValue: String\n}\n\ninput OrderByClause {\n order: ResultOrder\n nulls: NullOrder\n}\n\ninput AggregateOrderBy @generic {\n orderableNumberField: AggregateOrderByNumberClause @fieldCategory\n orderableStringField: AggregateOrderByStringClause @fieldCategory\n orderableField: NoFunctionAggregateOrderByClause @fieldCategory\n orderableGeolocationField: OrderByGeolocationClause @fieldCategory\n orderableParentRelationship: AggregateOrderBy @fieldCategory\n orderablePolymorphicParentRelationship: PolymorphicParentRelationshipOrderBy @fieldCategory\n type: String = ORDER_BY\n}\n\ninput GroupByClause {\n group: Boolean\n}\n\ntype RecordAggregateConnection @generic {\n edges: [RecordAggregateEdge]\n pageInfo: PageInfo!\n totalCount: Int!\n}\n\ntype LongitudeAggregate implements FieldValue {\n value: Longitude\n displayValue: String\n avg: DoubleValue\n count: LongValue\n countDistinct: LongValue\n max: LongitudeValue\n min: LongitudeValue\n sum: DoubleValue\n}\n\ntype RecordEdge @generic {\n node: RecordRepresentation\n cursor: String!\n}\n\nunion Setup__SetupPolymorphicParentRelationship @generic = Setup__EntityRepresentation\n\ntype DateValue implements FieldValue {\n value: Date\n displayValue: String\n format: String\n}\n\ninput URLOperators {\n eq: Url\n ne: Url\n like: Url\n lt: Url\n gt: Url\n lte: Url\n gte: Url\n in: [Url]\n nin: [Url]\n}\n\ninput LongOperators {\n eq: Long\n ne: Long\n lt: Long\n gt: Long\n lte: Long\n gte: Long\n in: [Long]\n nin: [Long]\n}\n\nenum DataType {\n STRING\n TEXTAREA\n PHONE\n EMAIL\n URL\n ENCRYPTEDSTRING\n BOOLEAN\n CURRENCY\n INT\n LONG\n DOUBLE\n PERCENT\n DATETIME\n TIME\n DATE\n REFERENCE\n PICKLIST\n MULTIPICKLIST\n ADDRESS\n LOCATION\n BASE64\n COMPLEXVALUE\n COMBOBOX\n JSON\n JUNCTIONIDLIST\n ANYTYPE\n}\n\nenum NullOrder {\n FIRST\n LAST\n}\n\ntype PhoneNumberValue implements FieldValue {\n value: PhoneNumber\n displayValue: String\n}\n\n# Cannot have empty enum\n# enum RecordScope @generic {\n# }\n\ninput Setup__SetupFilter @generic {\n and: [Setup__SetupFilter]\n or: [Setup__SetupFilter]\n not: Setup__SetupFilter\n parentRelationshipRecordFilter: Setup__SetupFilter @fieldCategory\n polymorphicParentRelationshipRecordFilter: Setup__SetupPolymorphicParentRelationshipRecordFilter @fieldCategory\n IntegerOperator: IntegerOperators @fieldCategory\n LongOperator: LongOperators @fieldCategory\n StringOperator: StringOperators @fieldCategory\n DoubleOperator: DoubleOperators @fieldCategory\n PercentOperator: PercentOperators @fieldCategory\n LongitudeOperator: LongitudeOperators @fieldCategory\n LatitudeOperator: LatitudeOperators @fieldCategory\n EmailOperator: EmailOperators @fieldCategory\n TextAreaOperator: TextAreaOperators @fieldCategory\n LongTextAreaOperator: LongTextAreaOperators @fieldCategory\n URLOperator: URLOperators @fieldCategory\n PhoneNumberOperator: PhoneNumberOperators @fieldCategory\n BooleanOperator: BooleanOperators @fieldCategory\n Setup__IdOperator: Setup__IdOperators @fieldCategory\n CurrencyOperator: CurrencyOperators @fieldCategory\n TimeOperator: TimeOperators @fieldCategory\n DateOperator: DateOperators @fieldCategory\n DateTimeOperator: DateTimeOperators @fieldCategory\n PicklistOperator: PicklistOperators @fieldCategory\n MultiPicklistOperator: MultiPicklistOperators @fieldCategory\n GeolocationOperator: GeolocationOperators @fieldCategory\n}\n\ntype DoubleAggregate implements FieldValue {\n value: Double\n displayValue: String\n avg: DoubleValue\n count: LongValue\n countDistinct: LongValue\n format: String\n max: DoubleValue\n min: DoubleValue\n sum: DoubleValue\n}\n\ntype __Field {\n name: String!\n description: String\n args: [__InputValue!]!\n type: __Type!\n isDeprecated: Boolean!\n deprecationReason: String\n}\n\ninput DateOperators {\n eq: DateInput\n ne: DateInput\n lt: DateInput\n gt: DateInput\n lte: DateInput\n gte: DateInput\n in: [DateInput]\n nin: [DateInput]\n DAY_IN_WEEK: DateFunctionInput\n DAY_IN_MONTH: DateFunctionInput\n DAY_IN_YEAR: DateFunctionInput\n WEEK_IN_MONTH: DateFunctionInput\n WEEK_IN_YEAR: DateFunctionInput\n CALENDAR_MONTH: DateFunctionInput\n CALENDAR_QUARTER: DateFunctionInput\n CALENDAR_YEAR: DateFunctionInput\n FISCAL_MONTH: DateFunctionInput\n FISCAL_QUARTER: DateFunctionInput\n FISCAL_YEAR: DateFunctionInput\n}\n\ninput GeolocationInput {\n latitude: Latitude!\n longitude: Longitude!\n radius: Float!\n unit: Unit!\n}\n\ninput JoinInput {\n Record: RecordFilter @fieldCategory\n ApiName: String\n}\n\ninput TextAreaOperators {\n eq: TextArea\n ne: TextArea\n like: TextArea\n lt: TextArea\n gt: TextArea\n lte: TextArea\n gte: TextArea\n in: [TextArea]\n nin: [TextArea]\n}\n\ntype TextAreaValue implements FieldValue {\n value: TextArea\n displayValue: String\n}\n\ntype RecordUpdatePayload @generic {\n success: Boolean\n}\n\ninput PercentOperators {\n eq: Percent\n ne: Percent\n lt: Percent\n gt: Percent\n lte: Percent\n gte: Percent\n in: [Percent]\n nin: [Percent]\n}\n\ninput Setup__SetupPolymorphicParentRelationshipRecordOrderBy @generic {\n Setup__SetupOrderBy: Setup__SetupOrderBy @fieldCategory\n}\n\ntype DoubleValue implements FieldValue {\n value: Double\n displayValue: String\n format: String\n}\n\ntype IDAggregate implements FieldValue {\n value: ID\n displayValue: String\n count: LongValue\n countDistinct: LongValue\n grouping: IntValue\n max: IDValue\n min: IDValue\n}\n\ntype __InputValue {\n name: String!\n description: String\n type: __Type!\n defaultValue: String\n}\n\ntype RecordAggregateEdge @generic {\n node: RecordResult\n cursor: String!\n}\n\ntype __Directive {\n name: String\n description: String\n locations: [__DirectiveLocation!]\n args: [__InputValue!]!\n}\n\ninput RecordCreateInput @generic {\n record: RecordCreateRepresentation! @fieldCategory\n}\n\ntype ThemeInfo {\n color: String\n iconUrl: String\n}\n\ninput AggregateOrderByStringClause {\n function: AggregateOrderByStringFunction\n order: ResultsOrder\n nulls: NullsOrder\n}\n\ntype RecordDeletePayload {\n Id: ID\n}\n\ntype UrlAggregate implements FieldValue {\n value: Url\n displayValue: String\n count: LongValue\n countDistinct: LongValue\n grouping: IntValue\n max: UrlValue\n min: UrlValue\n}\n\nenum DateLiteral {\n LAST_YEAR\n LAST_WEEK\n THIS_QUARTER\n NEXT_FISCAL_YEAR\n LAST_QUARTER\n TOMORROW\n NEXT_FISCAL_QUARTER\n YESTERDAY\n NEXT_QUARTER\n THIS_FISCAL_QUARTER\n THIS_WEEK\n LAST_MONTH\n LAST_90_DAYS\n NEXT_90_DAYS\n THIS_FISCAL_YEAR\n NEXT_WEEK\n TODAY\n NEXT_YEAR\n NEXT_MONTH\n LAST_FISCAL_QUARTER\n THIS_MONTH\n LAST_FISCAL_YEAR\n THIS_YEAR\n}\n\ntype __EnumValue {\n name: String!\n description: String\n isDeprecated: Boolean!\n deprecationReason: String\n}\n\ntype RecordRepresentation implements Record @generic {\n Id: ID!\n ApiName: String!\n WeakEtag: Long!\n DisplayValue: String\n LastModifiedById: IDValue\n LastModifiedDate: DateTimeValue\n SystemModstamp: DateTimeValue\n RecordTypeId(fallback: Boolean): IDValue\n IntValue: IntValue @fieldCategory\n StringValue: StringValue @fieldCategory\n BooleanValue: BooleanValue @fieldCategory\n IDValue: IDValue @fieldCategory\n DateTimeValue: DateTimeValue @fieldCategory\n TimeValue: TimeValue @fieldCategory\n DateValue: DateValue @fieldCategory\n TextAreaValue: TextAreaValue @fieldCategory\n LongTextAreaValue: LongTextAreaValue @fieldCategory\n RichTextAreaValue: RichTextAreaValue @fieldCategory\n PhoneNumberValue: PhoneNumberValue @fieldCategory\n EmailValue: EmailValue @fieldCategory\n UrlValue: UrlValue @fieldCategory\n EncryptedStringValue: EncryptedStringValue @fieldCategory\n CurrencyValue: CurrencyValue @fieldCategory\n LongitudeValue: LongitudeValue @fieldCategory\n LatitudeValue: LatitudeValue @fieldCategory\n PicklistValue: PicklistValue @fieldCategory\n MultiPicklistValue: MultiPicklistValue @fieldCategory\n LongValue: LongValue @fieldCategory\n DoubleValue: DoubleValue @fieldCategory\n PercentValue: PercentValue @fieldCategory\n Base64Value: Base64Value @fieldCategory\n JSONValue: JSONValue @fieldCategory\n parentRelationship: RecordRepresentation @fieldCategory\n polymorphicParentRelationship: PolymorphicParentRelationship @fieldCategory\n childRelationship(first: Int, after: String, where: RecordFilter, orderBy: RecordOrderBy, upperBound: Int): RecordConnection @fieldCategory\n CompoundField: CompoundField @fieldCategory\n}\n\ntype IDValue implements FieldValue {\n value: ID\n displayValue: String\n}\n\nenum Unit {\n MI\n KM\n}\n\ninput PolymorphicParentRelationshipOrderBy @generic {\n AggregateOrderBy: AggregateOrderBy @fieldCategory\n}\n\ninput OrderByGeolocationClause {\n distance: DistanceInput\n order: ResultOrder\n nulls: NullOrder\n}\n\ninput Setup__IdOperators {\n eq: ID\n ne: ID\n lt: ID\n gt: ID\n lte: ID\n gte: ID\n in: [ID]\n nin: [ID]\n inq: Setup__JoinInput\n ninq: Setup__JoinInput\n}\n\nenum NullsOrder {\n FIRST\n LAST\n}\n\ntype TextAreaAggregate implements FieldValue {\n value: TextArea\n displayValue: String\n count: LongValue\n countDistinct: LongValue\n grouping: IntValue\n max: TextAreaValue\n min: TextAreaValue\n}\n\nenum GroupByType {\n GROUP_BY\n ROLLUP\n CUBE\n}\n\nenum ResultOrder {\n ASC\n DESC\n}\n\ninput RecordOrderBy @generic {\n orderableField: OrderByClause @fieldCategory\n orderableGeolocationField: OrderByGeolocationClause @fieldCategory\n orderableParentRelationship: RecordOrderBy @fieldCategory\n orderablePolymorphicParentRelationship: PolymorphicParentRelationshipRecordOrderBy @fieldCategory\n}\n\ninput Setup__JoinInput {\n Record: Setup__SetupFilter @fieldCategory\n ApiName: String\n}\n\ninput PicklistOperators {\n eq: Picklist\n ne: Picklist\n in: [Picklist]\n nin: [Picklist]\n like: Picklist\n lt: Picklist\n gt: Picklist\n lte: Picklist\n gte: Picklist\n}\n\nenum ResultsOrder {\n ASC\n DESC\n}\n\ninput RecordFilter @generic {\n and: [RecordFilter]\n or: [RecordFilter]\n not: RecordFilter\n parentRelationshipRecordFilter: RecordFilter @fieldCategory\n polymorphicParentRelationshipRecordFilter: PolymorphicParentRelationshipRecordFilter @fieldCategory\n IntegerOperator: IntegerOperators @fieldCategory\n LongOperator: LongOperators @fieldCategory\n StringOperator: StringOperators @fieldCategory\n DoubleOperator: DoubleOperators @fieldCategory\n PercentOperator: PercentOperators @fieldCategory\n LongitudeOperator: LongitudeOperators @fieldCategory\n LatitudeOperator: LatitudeOperators @fieldCategory\n EmailOperator: EmailOperators @fieldCategory\n TextAreaOperator: TextAreaOperators @fieldCategory\n LongTextAreaOperator: LongTextAreaOperators @fieldCategory\n URLOperator: URLOperators @fieldCategory\n PhoneNumberOperator: PhoneNumberOperators @fieldCategory\n BooleanOperator: BooleanOperators @fieldCategory\n IdOperator: IdOperators @fieldCategory\n CurrencyOperator: CurrencyOperators @fieldCategory\n TimeOperator: TimeOperators @fieldCategory\n DateOperator: DateOperators @fieldCategory\n DateTimeOperator: DateTimeOperators @fieldCategory\n PicklistOperator: PicklistOperators @fieldCategory\n MultiPicklistOperator: MultiPicklistOperators @fieldCategory\n GeolocationOperator: GeolocationOperators @fieldCategory\n}\n\ntype TimeValue implements FieldValue {\n value: Time\n displayValue: String\n format: String\n}\n\ninput GeolocationOperators {\n lt: GeolocationInput\n gt: GeolocationInput\n}\n\ntype PicklistAggregate implements FieldValue {\n value: Picklist\n displayValue: String\n count: LongValue\n countDistinct: LongValue\n grouping: IntValue\n label: String\n max: PicklistValue\n min: PicklistValue\n}\n\ninput LatitudeOperators {\n eq: Latitude\n ne: Latitude\n lt: Latitude\n gt: Latitude\n lte: Latitude\n gte: Latitude\n in: [Latitude]\n nin: [Latitude]\n}\n\ninput RecordUpdateRepresentation @generic {\n Int: Int @fieldCategory\n String: String @fieldCategory\n Boolean: Boolean @fieldCategory\n ID: IdOrRef @fieldCategory\n DateTime: DateTime @fieldCategory\n Time: Time @fieldCategory\n Date: Date @fieldCategory\n TextArea: TextArea @fieldCategory\n LongTextArea: LongTextArea @fieldCategory\n RichTextArea: RichTextArea @fieldCategory\n PhoneNumber: PhoneNumber @fieldCategory\n Email: Email @fieldCategory\n Url: Url @fieldCategory\n EncryptedString: EncryptedString @fieldCategory\n Currency: Currency @fieldCategory\n Longitude: Longitude @fieldCategory\n Latitude: Latitude @fieldCategory\n Picklist: Picklist @fieldCategory\n MultiPicklist: MultiPicklist @fieldCategory\n Long: Long @fieldCategory\n Double: Double @fieldCategory\n Percent: Percent @fieldCategory\n Base64: Base64 @fieldCategory\n JSON: JSON @fieldCategory\n}\n\ntype DateTimeValue implements FieldValue {\n value: DateTime\n displayValue: String\n format: String\n}\n\ninput RecordDeleteInput {\n Id: IdOrRef!\n}\n\nenum __DirectiveLocation {\n QUERY\n MUTATION\n FIELD\n FRAGMENT_DEFINITION\n FRAGMENT_SPREAD\n INLINE_FRAGMENT\n SCHEMA\n SCALAR\n OBJECT\n FIELD_DEFINITION\n ARGUMENT_DEFINITION\n INTERFACE\n UNION\n ENUM\n ENUM_VALUE\n INPUT_OBJECT\n INPUT_FIELD_DEFINITION\n}\n\ntype IntAggregate implements FieldValue {\n value: Int\n displayValue: String\n avg: DoubleValue\n count: LongValue\n countDistinct: LongValue\n format: String\n grouping: IntValue\n max: IntValue\n min: IntValue\n sum: LongValue\n}\n\ntype ListOrder {\n fieldApiName: String!\n sortDirection: ResultOrder\n}\n\ntype RecordAggregate @generic {\n ApiName: String!\n BooleanAggregate: BooleanAggregate @fieldCategory\n CurrencyAggregate: CurrencyAggregate @fieldCategory\n DateAggregate: DateAggregate @fieldCategory\n DoubleAggregate: DoubleAggregate @fieldCategory\n EmailAggregate: EmailAggregate @fieldCategory\n IDAggregate: IDAggregate @fieldCategory\n IntAggregate: IntAggregate @fieldCategory\n LatitudeAggregate: LatitudeAggregate @fieldCategory\n LongitudeAggregate: LongitudeAggregate @fieldCategory\n LongAggregate: LongAggregate @fieldCategory\n PercentAggregate: PercentAggregate @fieldCategory\n PhoneNumberAggregate: PhoneNumberAggregate @fieldCategory\n PicklistAggregate: PicklistAggregate @fieldCategory\n StringAggregate: StringAggregate @fieldCategory\n TextAreaAggregate: TextAreaAggregate @fieldCategory\n TimeAggregate: TimeAggregate @fieldCategory\n UrlAggregate: UrlAggregate @fieldCategory\n}\n\ntype JSONValue implements FieldValue {\n value: JSON\n displayValue: String\n}\n\ntype EmailValue implements FieldValue {\n value: Email\n displayValue: String\n}\n\ntype Setup__Setup {\n query: Setup__SetupQuery!\n}\n\nenum AggregateOrderByStringFunction {\n COUNT\n COUNT_DISTINCT\n MAX\n MIN\n}\n\ntype LongValue implements FieldValue {\n value: Long\n displayValue: String\n format: String\n}\n\ninput DateFunctionInput {\n value: LongOperators\n convertTimezoneValue: LongOperators\n}\n\n# Mutations aren't supported yet.\n#type Mutation {\n# uiapi(input: UIAPIMutationsInput): UIAPIMutations!\n#}\n\ntype DependentField {\n controllingField: String!\n dependentFields: [String]!\n}\n\ninput LongTextAreaOperators {\n eq: LongTextArea\n ne: LongTextArea\n like: LongTextArea\n lt: LongTextArea\n gt: LongTextArea\n lte: LongTextArea\n gte: LongTextArea\n in: [LongTextArea]\n nin: [LongTextArea]\n}\n\nenum __TypeKind {\n SCALAR\n OBJECT\n INTERFACE\n UNION\n ENUM\n INPUT_OBJECT\n LIST\n NON_NULL\n}\n\ntype Setup__SetupConnection @generic {\n edges: [Setup__SetupEdge]\n pageInfo: PageInfo!\n totalCount: Int!\n pageResultCount: Int!\n}\n\ntype PercentValue implements FieldValue {\n value: Percent\n displayValue: String\n format: String\n}\n\ninput DateTimeOperators {\n eq: DateTimeInput\n ne: DateTimeInput\n lt: DateTimeInput\n gt: DateTimeInput\n lte: DateTimeInput\n gte: DateTimeInput\n in: [DateTimeInput]\n nin: [DateTimeInput]\n DAY_IN_WEEK: DateFunctionInput\n DAY_IN_MONTH: DateFunctionInput\n DAY_IN_YEAR: DateFunctionInput\n WEEK_IN_MONTH: DateFunctionInput\n WEEK_IN_YEAR: DateFunctionInput\n CALENDAR_MONTH: DateFunctionInput\n CALENDAR_QUARTER: DateFunctionInput\n CALENDAR_YEAR: DateFunctionInput\n FISCAL_MONTH: DateFunctionInput\n FISCAL_QUARTER: DateFunctionInput\n FISCAL_YEAR: DateFunctionInput\n DAY_ONLY: DateTimeFunctionInput\n HOUR_IN_DAY: DateFunctionInput\n}\n\ninput NoFunctionAggregateOrderByClause {\n order: ResultsOrder\n nulls: NullsOrder\n}\n\ntype BooleanAggregate implements FieldValue {\n value: Boolean\n displayValue: String\n grouping: IntValue\n}\n\ntype RecordQueryAggregate {\n # RecordScope is replaced with String\n recordQueryAggregate(first: Int, after: String, where: RecordFilter, orderBy: AggregateOrderBy, scope: String, groupBy: RecordGroupBy, upperBound: Int): RecordAggregateConnection @fieldCategory\n}\n\ntype RecordConnection @generic {\n edges: [RecordEdge]\n pageInfo: PageInfo!\n totalCount: Int!\n pageResultCount: Int!\n}\n\ntype FilteredLookupInfo {\n controllingFields: [String]!\n dependent: Boolean!\n optionalFilter: Boolean!\n}\n\ninput PhoneNumberOperators {\n eq: PhoneNumber\n ne: PhoneNumber\n like: PhoneNumber\n lt: PhoneNumber\n gt: PhoneNumber\n lte: PhoneNumber\n gte: PhoneNumber\n in: [PhoneNumber]\n nin: [PhoneNumber]\n}\n\ntype ObjectInfo {\n ApiName: String!\n childRelationships: [ChildRelationship]!\n createable: Boolean!\n custom: Boolean!\n defaultRecordTypeId: ID\n deletable: Boolean!\n dependentFields: [DependentField]!\n feedEnabled: Boolean!\n fields: [Field]!\n keyPrefix: String\n label: String\n labelPlural: String\n layoutable: Boolean!\n mruEnabled: Boolean!\n nameFields: [String]!\n queryable: Boolean!\n recordTypeInfos: [RecordTypeInfo]!\n searchable: Boolean!\n themeInfo: ThemeInfo\n updateable: Boolean!\n locale: String\n}\n\ninput LongitudeOperators {\n eq: Longitude\n ne: Longitude\n lt: Longitude\n gt: Longitude\n lte: Longitude\n gte: Longitude\n in: [Longitude]\n nin: [Longitude]\n}\n\ninput RecordCreateRepresentation @generic {\n Int: Int @fieldCategory\n String: String @fieldCategory\n Boolean: Boolean @fieldCategory\n ID: IdOrRef @fieldCategory\n DateTime: DateTime @fieldCategory\n Time: Time @fieldCategory\n Date: Date @fieldCategory\n TextArea: TextArea @fieldCategory\n LongTextArea: LongTextArea @fieldCategory\n RichTextArea: RichTextArea @fieldCategory\n PhoneNumber: PhoneNumber @fieldCategory\n Email: Email @fieldCategory\n Url: Url @fieldCategory\n EncryptedString: EncryptedString @fieldCategory\n Currency: Currency @fieldCategory\n Longitude: Longitude @fieldCategory\n Latitude: Latitude @fieldCategory\n Picklist: Picklist @fieldCategory\n MultiPicklist: MultiPicklist @fieldCategory\n Long: Long @fieldCategory\n Double: Double @fieldCategory\n Percent: Percent @fieldCategory\n Base64: Base64 @fieldCategory\n JSON: JSON @fieldCategory\n}\n\ntype Field {\n ApiName: String!\n calculated: Boolean!\n compound: Boolean!\n compoundComponentName: String\n compoundFieldName: String\n controllerName: String\n controllingFields: [String]!\n createable: Boolean!\n custom: Boolean!\n dataType: DataType\n extraTypeInfo: FieldExtraTypeInfo\n filterable: Boolean!\n filteredLookupInfo: FilteredLookupInfo\n highScaleNumber: Boolean!\n htmlFormatted: Boolean!\n inlineHelpText: String\n label: String\n nameField: Boolean!\n polymorphicForeignKey: Boolean!\n precision: Int\n reference: Boolean!\n referenceTargetField: String\n referenceToInfos: [ReferenceToInfo]!\n relationshipName: String\n required: Boolean!\n scale: Int\n searchPrefilterable: Boolean\n sortable: Boolean!\n updateable: Boolean!\n}\n\nenum FieldExtraTypeInfo {\n IMAGE_URL\n EXTERNAL_LOOKUP\n INDIRECT_LOOKUP\n PERSONNAME\n SWITCHABLE_PERSONNAME\n PLAINTEXTAREA\n RICHTEXTAREA\n}\n\ntype RateLimit {\n cost: Long\n limit: Long\n remaining: Long\n resetAt: DateTime\n}\n\ninput DateRange {\n last_n_days: Int\n next_n_days: Int\n last_n_weeks: Int\n next_n_weeks: Int\n last_n_months: Int\n next_n_months: Int\n last_n_quarters: Int\n next_n_quarters: Int\n last_n_fiscal_quarters: Int\n next_n_fiscal_quarters: Int\n last_n_years: Int\n next_n_years: Int\n last_n_fiscal_years: Int\n next_n_fiscal_years: Int\n n_days_ago: Int\n n_weeks_ago: Int\n n_months_ago: Int\n n_quarters_ago: Int\n n_years_ago: Int\n n_fiscal_quarters_ago: Int\n n_fiscal_years_ago: Int\n}\n\ntype UIAPIMutations {\n recordCreate(input: RecordCreateInput!): RecordCreatePayload @fieldCategory\n recordDelete(input: RecordDeleteInput!): RecordDeletePayload @fieldCategory\n recordUpdate(input: RecordUpdateInput!): RecordUpdatePayload @fieldCategory\n}\n\ninput DateTimeFunctionInput {\n value: DatePrimitiveOperators\n convertTimezoneValue: DatePrimitiveOperators\n}\n\ntype Base64Value implements FieldValue {\n value: Base64\n displayValue: String\n}\n\ninput IntegerOperators {\n eq: Int\n ne: Int\n lt: Int\n gt: Int\n lte: Int\n gte: Int\n in: [Int]\n nin: [Int]\n}\n\ntype EncryptedStringValue implements FieldValue {\n value: EncryptedString\n displayValue: String\n}\n\ninterface Record {\n Id: ID!\n ApiName: String!\n WeakEtag: Long!\n DisplayValue: String\n LastModifiedById: IDValue\n LastModifiedDate: DateTimeValue\n SystemModstamp: DateTimeValue\n RecordTypeId(fallback: Boolean): IDValue\n}\n\ninput PolymorphicParentRelationshipRecordFilter @generic {\n RecordFilter: RecordFilter @fieldCategory\n}\n\ninput AggregateOrderByNumberClause {\n function: AggregateOrderByNumberFunction\n order: ResultsOrder\n nulls: NullsOrder\n}\n\ntype __Schema {\n types: [__Type!]!\n queryType: __Type!\n mutationType: __Type\n directives: [__Directive!]!\n subscriptionType: __Type\n}\n\ninput Setup__SetupPolymorphicParentRelationshipRecordFilter @generic {\n Setup__SetupFilter: Setup__SetupFilter @fieldCategory\n}\n\ntype CompoundField @generic {\n IntValue: IntValue @fieldCategory\n StringValue: StringValue @fieldCategory\n BooleanValue: BooleanValue @fieldCategory\n IDValue: IDValue @fieldCategory\n DateTimeValue: DateTimeValue @fieldCategory\n TimeValue: TimeValue @fieldCategory\n DateValue: DateValue @fieldCategory\n TextAreaValue: TextAreaValue @fieldCategory\n LongTextAreaValue: LongTextAreaValue @fieldCategory\n RichTextAreaValue: RichTextAreaValue @fieldCategory\n PhoneNumberValue: PhoneNumberValue @fieldCategory\n EmailValue: EmailValue @fieldCategory\n UrlValue: UrlValue @fieldCategory\n EncryptedStringValue: EncryptedStringValue @fieldCategory\n CurrencyValue: CurrencyValue @fieldCategory\n LongitudeValue: LongitudeValue @fieldCategory\n LatitudeValue: LatitudeValue @fieldCategory\n PicklistValue: PicklistValue @fieldCategory\n MultiPicklistValue: MultiPicklistValue @fieldCategory\n LongValue: LongValue @fieldCategory\n DoubleValue: DoubleValue @fieldCategory\n PercentValue: PercentValue @fieldCategory\n Base64Value: Base64Value @fieldCategory\n JSONValue: JSONValue @fieldCategory\n}\n\ninput RecordUpdateInput @generic {\n Id: IdOrRef!\n record: RecordUpdateRepresentation! @fieldCategory\n}\n\ninput DateTimeInput {\n value: DateTime\n literal: DateLiteral\n range: DateRange\n}\n\ntype ChildRelationship {\n childObjectApiName: String!\n fieldName: String\n junctionIdListNames: [String]!\n junctionReferenceTo: [String]!\n relationshipName: String\n objectInfo: ObjectInfo\n}\n\ntype RecordResult @generic {\n aggregate: RecordAggregate\n}\n\ntype PageInfo {\n hasNextPage: Boolean!\n hasPreviousPage: Boolean!\n startCursor: String\n endCursor: String\n}\n\ntype CurrencyValue implements FieldValue {\n value: Currency\n displayValue: String\n format: String\n}\n\ninput DateInput {\n value: Date\n literal: DateLiteral\n range: DateRange\n}\n\ninput RecordGroupBy @generic {\n groupableField: GroupByClause @fieldCategory\n groupableDateField: GroupByDateFunction @fieldCategory\n groupableParentRelationship: RecordGroupBy @fieldCategory\n groupablePolymorphicParentRelationship: PolymorphicParentRelationshipGroupBy @fieldCategory\n type: GroupByType = GROUP_BY\n}\n\ntype DateFunctionAggregation {\n value: Long\n format: String\n}\n\ntype RecordQuery {\n # scope should be type RecordScope but that's empty enum.\n recordQuery(first: Int, after: String, where: RecordFilter, orderBy: RecordOrderBy, scope: String, upperBound: Int): RecordConnection @fieldCategory\n}\n\ndirective @generic on OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT\ndirective @fieldCategory on FIELD_DEFINITION | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION | ENUM_VALUE\ndirective @category(name: String!) on FIELD";
|
|
3737
|
-
|
|
3738
|
-
// Define additional schema that is missing from uiapi that we use in local evaluation
|
|
3739
|
-
const additionalSchemaDefinitions = /* GraphQL */ `
|
|
3740
|
-
enum SupportedScopes {
|
|
3741
|
-
ASSIGNEDTOME
|
|
3742
|
-
MINE
|
|
3743
|
-
}
|
|
3744
|
-
`;
|
|
3745
|
-
const baseTypeDefinitions = uiapiSchemaString + additionalSchemaDefinitions;
|
|
3746
|
-
class CachedGraphQLSchema {
|
|
3747
|
-
constructor() {
|
|
3748
|
-
this._schema = buildBaseSchema();
|
|
3749
|
-
this._polymorphicFieldTypeNames = [];
|
|
3750
|
-
}
|
|
3751
|
-
getSchema() {
|
|
3752
|
-
return this._schema;
|
|
3753
|
-
}
|
|
3754
|
-
setSchema(value) {
|
|
3755
|
-
this._schema = value;
|
|
3756
|
-
}
|
|
3757
|
-
getPolymorphicFieldTypeNames() {
|
|
3758
|
-
return this._polymorphicFieldTypeNames;
|
|
3759
|
-
}
|
|
3760
|
-
setPolymorphicFieldTypeNames(value) {
|
|
3761
|
-
this._polymorphicFieldTypeNames = value;
|
|
3762
|
-
}
|
|
3763
|
-
set(schema, polymorphicFieldTypeNames) {
|
|
3764
|
-
this._schema = schema;
|
|
3765
|
-
this._polymorphicFieldTypeNames = polymorphicFieldTypeNames;
|
|
3766
|
-
}
|
|
3767
|
-
}
|
|
3768
|
-
/**
|
|
3769
|
-
* Builds the base schema from uiapi graphql adapter with resolvers attached
|
|
3770
|
-
* @returns GraphQLSchema
|
|
3771
|
-
*/
|
|
3772
|
-
function buildBaseSchema() {
|
|
3773
|
-
return addResolversToSchema(buildSchema(baseTypeDefinitions), []);
|
|
3774
|
-
}
|
|
3775
|
-
|
|
3776
|
-
({
|
|
3777
|
-
kind: Kind.DIRECTIVE,
|
|
3778
|
-
name: {
|
|
3779
|
-
kind: Kind.NAME,
|
|
3780
|
-
value: 'category',
|
|
3781
|
-
},
|
|
3782
|
-
arguments: [
|
|
3783
|
-
{
|
|
3784
|
-
kind: Kind.ARGUMENT,
|
|
3785
|
-
name: {
|
|
3786
|
-
kind: Kind.NAME,
|
|
3787
|
-
value: 'name',
|
|
3788
|
-
},
|
|
3789
|
-
value: {
|
|
3790
|
-
kind: Kind.STRING,
|
|
3791
|
-
value: PARENT_RELATIONSHIP,
|
|
3792
|
-
block: false,
|
|
3793
|
-
},
|
|
3794
|
-
},
|
|
3795
|
-
],
|
|
3796
|
-
});
|
|
3797
|
-
({
|
|
3798
|
-
kind: Kind.SELECTION_SET,
|
|
3799
|
-
selections: [
|
|
3800
|
-
{
|
|
3801
|
-
kind: Kind.FIELD,
|
|
3802
|
-
name: {
|
|
3803
|
-
kind: Kind.NAME,
|
|
3804
|
-
value: 'value',
|
|
3805
|
-
},
|
|
3806
|
-
},
|
|
3807
|
-
],
|
|
3808
|
-
});
|
|
3809
|
-
|
|
3810
1662
|
/**
|
|
3811
1663
|
* Copyright (c) 2022, Salesforce, Inc.,
|
|
3812
1664
|
* All rights reserved.
|
|
@@ -4175,8 +2027,6 @@ function makeRecordDenormalizingDurableStore(luvio, durableStore, getStoreRecord
|
|
|
4175
2027
|
getDenormalizedRecord: { value: getDenormalizedRecord, writable: true },
|
|
4176
2028
|
});
|
|
4177
2029
|
}
|
|
4178
|
-
// create the runtime cache for the graphql schema when the factory creates the adapter
|
|
4179
|
-
new CachedGraphQLSchema();
|
|
4180
2030
|
|
|
4181
2031
|
let luvio;
|
|
4182
2032
|
let getIngestRecords;
|
|
@@ -4235,4 +2085,4 @@ function ldsRuntimeBridge() {
|
|
|
4235
2085
|
}
|
|
4236
2086
|
|
|
4237
2087
|
export { ldsRuntimeBridge as default };
|
|
4238
|
-
// version: 1.
|
|
2088
|
+
// version: 1.295.0-1bf771a0b
|