@salesforce/lds-runtime-mobile 1.229.0-dev1 → 1.229.0-dev3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.js +247 -163
- package/package.json +18 -18
- package/sfdc/main.js +247 -163
package/dist/main.js
CHANGED
|
@@ -552,6 +552,9 @@ function handleDurableStoreRejection(instrument) {
|
|
|
552
552
|
}
|
|
553
553
|
|
|
554
554
|
function isStoreEntryError(storeRecord) {
|
|
555
|
+
if (!storeRecord || typeof storeRecord !== 'object') {
|
|
556
|
+
return false;
|
|
557
|
+
}
|
|
555
558
|
return storeRecord.__type === 'error';
|
|
556
559
|
}
|
|
557
560
|
|
|
@@ -4260,8 +4263,8 @@ function rootRecordQuery(selection, input) {
|
|
|
4260
4263
|
// If there is no metadata for this query or it somehow lacks a timestamp
|
|
4261
4264
|
// skip setting the root timestamp
|
|
4262
4265
|
if (queryMetadata !== undefined && queryMetadata.ingestionTimestamp !== undefined) {
|
|
4263
|
-
// subtract
|
|
4264
|
-
input.rootTimestamp = queryMetadata.ingestionTimestamp -
|
|
4266
|
+
// subtract 1000ms from timestamp to account for ingestion processing time
|
|
4267
|
+
input.rootTimestamp = queryMetadata.ingestionTimestamp - 1000;
|
|
4265
4268
|
}
|
|
4266
4269
|
}
|
|
4267
4270
|
return recordQuery(selection, alias, apiName, [], input);
|
|
@@ -8057,6 +8060,10 @@ function isOperationDefinitionNode(node) {
|
|
|
8057
8060
|
return node.kind === 'OperationDefinition';
|
|
8058
8061
|
}
|
|
8059
8062
|
|
|
8063
|
+
const POLYMORPHIC_PARENT_RELATIONSHIP = 'polymorphicParentRelationship';
|
|
8064
|
+
const PARENT_RELATIONSHIP = 'parentRelationship';
|
|
8065
|
+
const CHILD_RELATIONSHIP = 'childRelationship';
|
|
8066
|
+
const RECORD_QUERY = 'recordQuery';
|
|
8060
8067
|
function requestsDraftsField(recordFieldNode) {
|
|
8061
8068
|
if (!recordFieldNode.selectionSet)
|
|
8062
8069
|
return false;
|
|
@@ -8072,18 +8079,41 @@ function isRecordQuery(recordQueryField) {
|
|
|
8072
8079
|
directive.arguments
|
|
8073
8080
|
.map((argument) => argument.value)
|
|
8074
8081
|
.filter(isStringValueNode)
|
|
8075
|
-
.some((categoryName) => categoryName.value ===
|
|
8082
|
+
.some((categoryName) => categoryName.value === RECORD_QUERY));
|
|
8076
8083
|
});
|
|
8077
8084
|
}
|
|
8078
8085
|
return false;
|
|
8079
8086
|
}
|
|
8080
|
-
// finds field with 'recordQuery' and 'childRelationship' directive
|
|
8081
|
-
function
|
|
8082
|
-
const
|
|
8083
|
-
return
|
|
8087
|
+
// finds connection field with 'recordQuery' and 'childRelationship' directive.
|
|
8088
|
+
function findNearestConnection(ancestors) {
|
|
8089
|
+
const connectionAncestor = findNearestAncesterPath(ancestors, true).node;
|
|
8090
|
+
return connectionAncestor === undefined ? undefined : connectionAncestor;
|
|
8091
|
+
}
|
|
8092
|
+
// convinient method to find nearest connection with its path
|
|
8093
|
+
function findNearestConnectionWithPath(ancestors) {
|
|
8094
|
+
const closestAncestorPath = findNearestAncesterPath(ancestors, true);
|
|
8095
|
+
let connection = undefined;
|
|
8096
|
+
let connectionPath = undefined;
|
|
8097
|
+
if (closestAncestorPath.parentIndex > 0) {
|
|
8098
|
+
const connectionAncestor = closestAncestorPath.node;
|
|
8099
|
+
const connectionAncestors = ancestors.slice(0, closestAncestorPath.parentIndex);
|
|
8100
|
+
connection =
|
|
8101
|
+
connectionAncestor === undefined ? undefined : connectionAncestor;
|
|
8102
|
+
if (connection !== undefined) {
|
|
8103
|
+
const ancesterPath = findAncesterPath(connectionAncestors);
|
|
8104
|
+
connectionPath =
|
|
8105
|
+
ancesterPath === ''
|
|
8106
|
+
? connection.name.value
|
|
8107
|
+
: `${ancesterPath}#${connection.name.value}`;
|
|
8108
|
+
}
|
|
8109
|
+
}
|
|
8110
|
+
return {
|
|
8111
|
+
connection,
|
|
8112
|
+
path: connectionPath,
|
|
8113
|
+
};
|
|
8084
8114
|
}
|
|
8085
|
-
// finds
|
|
8086
|
-
function findNearestAncesterPath(ancestors,
|
|
8115
|
+
// finds closest ancestor. If node with 'parentRelationship' is the ancester, the end result could be 'InlineFragmentNode' since it inherits the 'parent' relationship. 'InlineFragmentNode' makes sure that only one 'apiName' returns when tree is traversed.
|
|
8116
|
+
function findNearestAncesterPath(ancestors, connectionOnly) {
|
|
8087
8117
|
let recordQueryPath = { node: undefined, parentIndex: -1 };
|
|
8088
8118
|
let relationship = '';
|
|
8089
8119
|
for (let i = ancestors.length - 1; i >= 0; i--) {
|
|
@@ -8097,9 +8127,11 @@ function findNearestAncesterPath(ancestors, recordQueryOnly) {
|
|
|
8097
8127
|
continue;
|
|
8098
8128
|
for (let arg of directive.arguments) {
|
|
8099
8129
|
if (arg.value &&
|
|
8100
|
-
(arg.value.value ===
|
|
8101
|
-
arg.value.value ===
|
|
8102
|
-
(!
|
|
8130
|
+
(arg.value.value === RECORD_QUERY ||
|
|
8131
|
+
arg.value.value === CHILD_RELATIONSHIP ||
|
|
8132
|
+
(!connectionOnly &&
|
|
8133
|
+
(arg.value.value === PARENT_RELATIONSHIP ||
|
|
8134
|
+
arg.value.value === POLYMORPHIC_PARENT_RELATIONSHIP)))) {
|
|
8103
8135
|
recordQueryPath = { node: node, parentIndex: i };
|
|
8104
8136
|
relationship = arg.value.value;
|
|
8105
8137
|
break;
|
|
@@ -8114,17 +8146,19 @@ function findNearestAncesterPath(ancestors, recordQueryOnly) {
|
|
|
8114
8146
|
//checks if nearest ancester could be an inline fragment
|
|
8115
8147
|
if (recordQueryPath.node !== undefined &&
|
|
8116
8148
|
recordQueryPath.node.selectionSet &&
|
|
8117
|
-
relationship ===
|
|
8118
|
-
//
|
|
8119
|
-
|
|
8120
|
-
|
|
8121
|
-
|
|
8122
|
-
if (
|
|
8149
|
+
(relationship === PARENT_RELATIONSHIP || relationship === POLYMORPHIC_PARENT_RELATIONSHIP)) {
|
|
8150
|
+
// InlineFragment is usually 3 steps aways from its FieldNode parent within ancester hierarchy if it exists. The below search
|
|
8151
|
+
// is applied to adapt to future AST structure change
|
|
8152
|
+
let parentIndex = recordQueryPath.parentIndex + 1;
|
|
8153
|
+
while (parentIndex < ancestors.length) {
|
|
8154
|
+
if (isInlineFragmentNode(ancestors[parentIndex])) {
|
|
8123
8155
|
recordQueryPath = {
|
|
8124
|
-
node: ancestors[
|
|
8125
|
-
parentIndex
|
|
8156
|
+
node: ancestors[parentIndex],
|
|
8157
|
+
parentIndex,
|
|
8126
8158
|
};
|
|
8159
|
+
break;
|
|
8127
8160
|
}
|
|
8161
|
+
parentIndex++;
|
|
8128
8162
|
}
|
|
8129
8163
|
}
|
|
8130
8164
|
return recordQueryPath;
|
|
@@ -8148,7 +8182,7 @@ function findAncesterPath(ancesters) {
|
|
|
8148
8182
|
? sectionPath
|
|
8149
8183
|
: sectionPath === ''
|
|
8150
8184
|
? path
|
|
8151
|
-
: `${sectionPath}
|
|
8185
|
+
: `${sectionPath}#${path}`;
|
|
8152
8186
|
}
|
|
8153
8187
|
}
|
|
8154
8188
|
boundaryIndex = parentIndex;
|
|
@@ -8206,9 +8240,9 @@ function getRelation(node) {
|
|
|
8206
8240
|
const relationships = args
|
|
8207
8241
|
.map((arg) => arg.value)
|
|
8208
8242
|
.filter(isStringValueNode)
|
|
8209
|
-
.filter((valueNode) => valueNode.value ===
|
|
8210
|
-
valueNode.value ===
|
|
8211
|
-
valueNode.value ===
|
|
8243
|
+
.filter((valueNode) => valueNode.value === CHILD_RELATIONSHIP ||
|
|
8244
|
+
valueNode.value === PARENT_RELATIONSHIP ||
|
|
8245
|
+
valueNode.value === POLYMORPHIC_PARENT_RELATIONSHIP)
|
|
8212
8246
|
.map((relationshipNode) => relationshipNode.value);
|
|
8213
8247
|
if (relationships.length > 0) {
|
|
8214
8248
|
return relationships[0];
|
|
@@ -8265,8 +8299,8 @@ function isFieldSpanning(node, parentNode) {
|
|
|
8265
8299
|
*/
|
|
8266
8300
|
function isParentRelationship(node) {
|
|
8267
8301
|
return (node &&
|
|
8268
|
-
(isRelationship(node,
|
|
8269
|
-
isRelationship(node,
|
|
8302
|
+
(isRelationship(node, PARENT_RELATIONSHIP) ||
|
|
8303
|
+
isRelationship(node, POLYMORPHIC_PARENT_RELATIONSHIP)));
|
|
8270
8304
|
}
|
|
8271
8305
|
/*
|
|
8272
8306
|
checks if the InlineFragment spans
|
|
@@ -9099,8 +9133,8 @@ async function fetchIngestionTimeStampFromDatabase(apiName, info, args, query) {
|
|
|
9099
9133
|
const results = await query(sql, [key]);
|
|
9100
9134
|
const [timestamp] = results.rows.map((row) => row[0]);
|
|
9101
9135
|
if (timestamp !== null && typeof timestamp === 'number') {
|
|
9102
|
-
//go back
|
|
9103
|
-
ingestionTimestamp = timestamp -
|
|
9136
|
+
//go back 1000 ms to adjust for margin of error when top level query is stored and when raml objects are stored
|
|
9137
|
+
ingestionTimestamp = timestamp - 1000;
|
|
9104
9138
|
}
|
|
9105
9139
|
}
|
|
9106
9140
|
return ingestionTimestamp;
|
|
@@ -9149,26 +9183,20 @@ function generateRecordQueries(objectInfos) {
|
|
|
9149
9183
|
let recordConnections = ``;
|
|
9150
9184
|
const polymorphicFieldTypeNames = new Set();
|
|
9151
9185
|
let typedScalars = new Set();
|
|
9186
|
+
let parentRelationshipFields = new Set();
|
|
9152
9187
|
for (const objectInfo of values$1(objectInfos)) {
|
|
9153
9188
|
const { apiName, childRelationships } = objectInfo;
|
|
9154
9189
|
let fields = ``;
|
|
9155
9190
|
typedScalars.add(`${apiName}_Filter`);
|
|
9156
9191
|
typedScalars.add(`${apiName}_OrderBy`);
|
|
9157
|
-
for (const childRelationship of childRelationships) {
|
|
9158
|
-
const { childObjectApiName } = childRelationship;
|
|
9159
|
-
// Only add the relationship if there is relevant objectinfos for it,
|
|
9160
|
-
// otherwise we'd be defining types we cannot satisfy and aren't referenced in
|
|
9161
|
-
// the query.
|
|
9162
|
-
if (objectInfos[childObjectApiName] !== undefined) {
|
|
9163
|
-
fields += `${childRelationship.relationshipName}(first: Int, where: ${childObjectApiName}_Filter, orderBy: ${childObjectApiName}_OrderBy, scope: SupportedScopes): ${childObjectApiName}Connection \n`;
|
|
9164
|
-
typedScalars.add(`${childObjectApiName}_Filter`);
|
|
9165
|
-
typedScalars.add(`${childObjectApiName}_OrderBy`);
|
|
9166
|
-
}
|
|
9167
|
-
}
|
|
9168
9192
|
for (const field of values$1(objectInfo.fields)) {
|
|
9169
9193
|
if (!fieldsStaticallyAdded.includes(field.apiName)) {
|
|
9170
9194
|
fields += `${field.apiName}: ${dataTypeToType(field.dataType, field.apiName)}\n`;
|
|
9171
9195
|
}
|
|
9196
|
+
//handles parent relationship
|
|
9197
|
+
if (field.relationshipName === null) {
|
|
9198
|
+
continue;
|
|
9199
|
+
}
|
|
9172
9200
|
// For spanning parent relationships with no union types
|
|
9173
9201
|
if (field.referenceToInfos.length === 1) {
|
|
9174
9202
|
const [relation] = field.referenceToInfos;
|
|
@@ -9176,11 +9204,13 @@ function generateRecordQueries(objectInfos) {
|
|
|
9176
9204
|
// otherwise we'd be defining types we cannot satisfy and aren't referenced in
|
|
9177
9205
|
// the query.
|
|
9178
9206
|
if (objectInfos[relation.apiName] !== undefined) {
|
|
9207
|
+
parentRelationshipFields.add(field.relationshipName);
|
|
9179
9208
|
fields += `${field.relationshipName}: ${relation.apiName}\n`;
|
|
9180
9209
|
}
|
|
9181
9210
|
// For polymorphic field, its type is 'Record' inteface. The concrete entity type name is saved for field resolving of next phase
|
|
9182
9211
|
}
|
|
9183
9212
|
else if (field.referenceToInfos.length > 1) {
|
|
9213
|
+
parentRelationshipFields.add(field.relationshipName);
|
|
9184
9214
|
fields += `${field.relationshipName}: Record\n`;
|
|
9185
9215
|
for (const relation of field.referenceToInfos) {
|
|
9186
9216
|
if (objectInfos[relation.apiName] !== undefined) {
|
|
@@ -9189,6 +9219,20 @@ function generateRecordQueries(objectInfos) {
|
|
|
9189
9219
|
}
|
|
9190
9220
|
}
|
|
9191
9221
|
}
|
|
9222
|
+
// handles child relationship
|
|
9223
|
+
for (const childRelationship of childRelationships) {
|
|
9224
|
+
const { childObjectApiName } = childRelationship;
|
|
9225
|
+
// Only add the relationship if there is relevant objectinfos for it,
|
|
9226
|
+
// otherwise we'd be defining types we cannot satisfy and aren't referenced in
|
|
9227
|
+
// the query.
|
|
9228
|
+
// If one field has both parent relationship and child relationship with the same name, the child relationship is ignored. This is how the server GQL has implemented as date of 08/07/2023
|
|
9229
|
+
if (objectInfos[childObjectApiName] !== undefined &&
|
|
9230
|
+
!parentRelationshipFields.has(childRelationship.relationshipName)) {
|
|
9231
|
+
fields += `${childRelationship.relationshipName}(first: Int, where: ${childObjectApiName}_Filter, orderBy: ${childObjectApiName}_OrderBy, scope: SupportedScopes): ${childObjectApiName}Connection \n`;
|
|
9232
|
+
typedScalars.add(`${childObjectApiName}_Filter`);
|
|
9233
|
+
typedScalars.add(`${childObjectApiName}_OrderBy`);
|
|
9234
|
+
}
|
|
9235
|
+
}
|
|
9192
9236
|
recordQueries += `${apiName}(first: Int, where: ${apiName}_Filter, orderBy: ${apiName}_OrderBy, scope: SupportedScopes): ${apiName}Connection\n`;
|
|
9193
9237
|
const isServiceAppointment = apiName === 'ServiceAppointment';
|
|
9194
9238
|
recordConnections += /* GraphQL */ `
|
|
@@ -9348,7 +9392,7 @@ const parentRelationshipDirective = {
|
|
|
9348
9392
|
},
|
|
9349
9393
|
value: {
|
|
9350
9394
|
kind: Kind.STRING,
|
|
9351
|
-
value:
|
|
9395
|
+
value: PARENT_RELATIONSHIP,
|
|
9352
9396
|
block: false,
|
|
9353
9397
|
},
|
|
9354
9398
|
},
|
|
@@ -9362,8 +9406,8 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
|
|
|
9362
9406
|
// example 2 'ServiceAppointment' -> ['Owner']; 'Owner' -> ['User', 'Group']
|
|
9363
9407
|
const objectNodeInfoTree = {};
|
|
9364
9408
|
// save the field path to apiName map
|
|
9365
|
-
// example 1: 'ServiceAppointment' -> ['ServiceAppointment']; '
|
|
9366
|
-
const
|
|
9409
|
+
// example 1: 'ServiceAppointment' -> ['ServiceAppointment']; 'ServiceAppointment#ccount' -> ['Account']; 'ServiceAppointment#Account#Owner' -> ['User']
|
|
9410
|
+
const pathToObjectApiNamesMap = {};
|
|
9367
9411
|
let startNodes = new Set();
|
|
9368
9412
|
let totalNodes = new Set();
|
|
9369
9413
|
let objectInfos = {};
|
|
@@ -9377,11 +9421,11 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
|
|
|
9377
9421
|
visit(originalAST, {
|
|
9378
9422
|
Argument: {
|
|
9379
9423
|
enter(node, key, parent, path, ancestors) {
|
|
9380
|
-
const
|
|
9381
|
-
if (!
|
|
9424
|
+
const { connection: recordConnectionNode, path: ancesterPath } = findNearestConnectionWithPath(ancestors);
|
|
9425
|
+
if (!recordConnectionNode || !ancesterPath)
|
|
9382
9426
|
return;
|
|
9383
|
-
if (!objectNodeInfoTree[
|
|
9384
|
-
objectNodeInfoTree[
|
|
9427
|
+
if (!objectNodeInfoTree[ancesterPath]) {
|
|
9428
|
+
objectNodeInfoTree[ancesterPath] = [];
|
|
9385
9429
|
}
|
|
9386
9430
|
switch (node.name.value) {
|
|
9387
9431
|
case 'orderBy':
|
|
@@ -9389,12 +9433,12 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
|
|
|
9389
9433
|
if (node.value.kind !== 'ObjectValue') {
|
|
9390
9434
|
return;
|
|
9391
9435
|
}
|
|
9392
|
-
totalNodes.add(
|
|
9436
|
+
totalNodes.add(ancesterPath);
|
|
9393
9437
|
// 'childRelationship' node is not taken as the startNode of the 'NodeInfoTree' graph. The field scanning will construct the graph which lead here.
|
|
9394
|
-
if (isRecordQuery(
|
|
9395
|
-
startNodes.add(
|
|
9438
|
+
if (isRecordQuery(recordConnectionNode)) {
|
|
9439
|
+
startNodes.add(recordConnectionNode.name.value);
|
|
9396
9440
|
}
|
|
9397
|
-
growObjectFieldTree(objectNodeInfoTree,
|
|
9441
|
+
growObjectFieldTree(objectNodeInfoTree, ancesterPath, node.value, totalNodes, startNodes);
|
|
9398
9442
|
break;
|
|
9399
9443
|
case 'scope':
|
|
9400
9444
|
if (!isScopeArgumentNodeWithType(node, 'ASSIGNEDTOME', variables)) {
|
|
@@ -9409,17 +9453,16 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
|
|
|
9409
9453
|
name: 'ServiceResources',
|
|
9410
9454
|
});
|
|
9411
9455
|
}
|
|
9412
|
-
if (objectNodeInfoTree['ServiceResources'] === undefined) {
|
|
9413
|
-
objectNodeInfoTree['ServiceResources'] = [
|
|
9414
|
-
|
|
9415
|
-
|
|
9416
|
-
|
|
9417
|
-
|
|
9418
|
-
|
|
9419
|
-
});
|
|
9456
|
+
if (objectNodeInfoTree['ServiceAppointment#ServiceResources'] === undefined) {
|
|
9457
|
+
objectNodeInfoTree['ServiceAppointment#ServiceResources'] = [
|
|
9458
|
+
{
|
|
9459
|
+
relation: 'parent',
|
|
9460
|
+
name: 'ServiceResource',
|
|
9461
|
+
},
|
|
9462
|
+
];
|
|
9420
9463
|
}
|
|
9421
|
-
if (objectNodeInfoTree['ServiceResource'] === undefined) {
|
|
9422
|
-
objectNodeInfoTree['ServiceResource'] = [];
|
|
9464
|
+
if (objectNodeInfoTree['ServiceAppointment#ServiceResources#ServiceResource'] === undefined) {
|
|
9465
|
+
objectNodeInfoTree['ServiceAppointment#ServiceResources#ServiceResource'] = [];
|
|
9423
9466
|
}
|
|
9424
9467
|
break;
|
|
9425
9468
|
default:
|
|
@@ -9433,7 +9476,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
|
|
|
9433
9476
|
return;
|
|
9434
9477
|
if (!node.selectionSet)
|
|
9435
9478
|
return;
|
|
9436
|
-
const recordQueryField =
|
|
9479
|
+
const recordQueryField = findNearestConnection(ancestors);
|
|
9437
9480
|
//only injects fields for 'recordQuery' field. ignores the 'childRelationship' field since it will be traversed as the child of the 'recordQuery'
|
|
9438
9481
|
if (isRecordQuery(recordQueryField) && recordQueryField) {
|
|
9439
9482
|
totalNodes.add(recordQueryField.name.value);
|
|
@@ -9444,21 +9487,21 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
|
|
|
9444
9487
|
},
|
|
9445
9488
|
});
|
|
9446
9489
|
if (objectInfoService && startNodes.size > 0) {
|
|
9447
|
-
objectInfos = await resolveObjectInfos(objectNodeInfoTree,
|
|
9490
|
+
objectInfos = await resolveObjectInfos(objectNodeInfoTree, pathToObjectApiNamesMap, startNodes, objectInfoService);
|
|
9448
9491
|
}
|
|
9449
9492
|
// read pass; gather whats needed
|
|
9450
9493
|
visit(originalAST, {
|
|
9451
9494
|
Argument: {
|
|
9452
9495
|
leave(node, key, parent, path, ancestors) {
|
|
9453
|
-
const recordQueryField =
|
|
9496
|
+
const recordQueryField = findNearestConnection(ancestors);
|
|
9454
9497
|
if (!recordQueryField)
|
|
9455
9498
|
return;
|
|
9456
9499
|
const ancestorPath = findAncesterPath(ancestors);
|
|
9457
9500
|
if (!inlineFragmentSelections[ancestorPath]) {
|
|
9458
9501
|
inlineFragmentSelections[ancestorPath] = [];
|
|
9459
9502
|
}
|
|
9460
|
-
const recordQueryApiName =
|
|
9461
|
-
?
|
|
9503
|
+
const recordQueryApiName = pathToObjectApiNamesMap[ancestorPath]
|
|
9504
|
+
? pathToObjectApiNamesMap[ancestorPath][0]
|
|
9462
9505
|
: recordQueryField.name.value;
|
|
9463
9506
|
// The record node acts as the reference. The duplicated field in the record node is not injected
|
|
9464
9507
|
const recordReferenceNode = [recordQueryField]
|
|
@@ -9472,7 +9515,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
|
|
|
9472
9515
|
case 'scope':
|
|
9473
9516
|
// Hanle 'MINE' field
|
|
9474
9517
|
if (isScopeArgumentNodeWithType(node, 'MINE', variables)) {
|
|
9475
|
-
if (isMineScopeAvailable(ancestorPath,
|
|
9518
|
+
if (isMineScopeAvailable(ancestorPath, pathToObjectApiNamesMap, objectInfos)) {
|
|
9476
9519
|
// 'typeConditon' is added when the 'InlineFragmentNode' is appended at the write pass
|
|
9477
9520
|
inlineFragmentSelections[ancestorPath].push(...mineFragmentSelections);
|
|
9478
9521
|
}
|
|
@@ -9498,7 +9541,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
|
|
|
9498
9541
|
case 'where': {
|
|
9499
9542
|
inlineFragmentSelections[ancestorPath] = [
|
|
9500
9543
|
...inlineFragmentSelections[ancestorPath],
|
|
9501
|
-
...injectFilter(node, idState, ancestorPath, objectInfos,
|
|
9544
|
+
...injectFilter(node, idState, ancestorPath, false, objectInfos, pathToObjectApiNamesMap, draftFunctions, recordReferenceNode),
|
|
9502
9545
|
];
|
|
9503
9546
|
break;
|
|
9504
9547
|
}
|
|
@@ -9514,7 +9557,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
|
|
|
9514
9557
|
if (!node.selectionSet)
|
|
9515
9558
|
return;
|
|
9516
9559
|
// it could be 'recordQuery' or 'childRelationship'
|
|
9517
|
-
const recordQueryField =
|
|
9560
|
+
const recordQueryField = findNearestConnection(ancestors);
|
|
9518
9561
|
if (!recordQueryField)
|
|
9519
9562
|
return;
|
|
9520
9563
|
const ancestorPath = findAncesterPath(ancestors);
|
|
@@ -9526,7 +9569,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
|
|
|
9526
9569
|
spanningSelections.push(selection);
|
|
9527
9570
|
}
|
|
9528
9571
|
}
|
|
9529
|
-
const injectedFields = injectFields(spanningSelections, node, ancestors, objectInfos,
|
|
9572
|
+
const injectedFields = injectFields(spanningSelections, node, ancestorPath, ancestors, objectInfos, pathToObjectApiNamesMap);
|
|
9530
9573
|
const mergedInjectedFields = mergeSelectionNodes(inlineFragmentSelections[ancestorPath], injectedFields);
|
|
9531
9574
|
inlineFragmentSelections[ancestorPath] = mergedInjectedFields;
|
|
9532
9575
|
},
|
|
@@ -9539,7 +9582,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
|
|
|
9539
9582
|
// removes 'ServicesResources' query field node if 'assignedtome' scope shows up
|
|
9540
9583
|
if (assignedtomeQueryFieldNode !== undefined &&
|
|
9541
9584
|
node.name.value === 'ServiceResources') {
|
|
9542
|
-
const serviceResourcesAncestor =
|
|
9585
|
+
const serviceResourcesAncestor = findNearestConnection(ancestors);
|
|
9543
9586
|
if (serviceResourcesAncestor === assignedtomeQueryFieldNode) {
|
|
9544
9587
|
return null;
|
|
9545
9588
|
}
|
|
@@ -9548,7 +9591,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
|
|
|
9548
9591
|
return;
|
|
9549
9592
|
if (!node.selectionSet)
|
|
9550
9593
|
return;
|
|
9551
|
-
const recordQueryField =
|
|
9594
|
+
const recordQueryField = findNearestConnection(ancestors);
|
|
9552
9595
|
if (!recordQueryField)
|
|
9553
9596
|
return;
|
|
9554
9597
|
const ancestorPath = findAncesterPath(ancestors);
|
|
@@ -9557,8 +9600,8 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
|
|
|
9557
9600
|
return;
|
|
9558
9601
|
//const recordQueryPath = findAncesterPath(ancestors);
|
|
9559
9602
|
// 'apiName' has to be at index 0 since 'node' record type could only be of 'recordQuery' or 'childRelationship'. They can not have the 'InlineFragmentNode' as its children.
|
|
9560
|
-
const recordQueryApiName =
|
|
9561
|
-
?
|
|
9603
|
+
const recordQueryApiName = pathToObjectApiNamesMap[ancestorPath]
|
|
9604
|
+
? pathToObjectApiNamesMap[ancestorPath][0]
|
|
9562
9605
|
: recordQueryField.name.value;
|
|
9563
9606
|
const nodeWithFragments = {
|
|
9564
9607
|
...node,
|
|
@@ -9595,7 +9638,7 @@ async function injectSyntheticFields(originalAST, objectInfoService, draftFuncti
|
|
|
9595
9638
|
if (node.name.value === 'where') {
|
|
9596
9639
|
const ancestorPath = findAncesterPath(ancestors);
|
|
9597
9640
|
if (idState.paths.includes(ancestorPath)) {
|
|
9598
|
-
const apiName =
|
|
9641
|
+
const apiName = pathToObjectApiNamesMap[ancestorPath][0];
|
|
9599
9642
|
const objectInfo = objectInfos[apiName];
|
|
9600
9643
|
const swappedIdFilter = swapIdField(node.value, objectInfo, false, idState, draftFunctions);
|
|
9601
9644
|
return {
|
|
@@ -9661,8 +9704,8 @@ function swapIdField(filterFields, objectInfo, swapped, idState, draftFunctions)
|
|
|
9661
9704
|
};
|
|
9662
9705
|
}
|
|
9663
9706
|
}
|
|
9664
|
-
function isMineScopeAvailable(apiNamePath,
|
|
9665
|
-
const apiName =
|
|
9707
|
+
function isMineScopeAvailable(apiNamePath, pathToObjectApiNamesMap, objectInfos) {
|
|
9708
|
+
const apiName = pathToObjectApiNamesMap[apiNamePath];
|
|
9666
9709
|
if (!apiName)
|
|
9667
9710
|
return false;
|
|
9668
9711
|
const objectInfo = objectInfos[apiName[0]];
|
|
@@ -9751,15 +9794,16 @@ function growObjectFieldTree(tree, parentNode, entryNode, totalNodes, startNodes
|
|
|
9751
9794
|
}
|
|
9752
9795
|
// example: 'Account'
|
|
9753
9796
|
const childNode = objectFieldNode.name.value;
|
|
9797
|
+
const childNodepath = `${parentNode}#${childNode}`;
|
|
9754
9798
|
if (!tree[parentNode].some((child) => child.name === childNode)) {
|
|
9755
9799
|
tree[parentNode].push({
|
|
9756
9800
|
relation: 'parent',
|
|
9757
9801
|
name: childNode,
|
|
9758
9802
|
});
|
|
9759
|
-
totalNodes.add(
|
|
9803
|
+
totalNodes.add(childNodepath);
|
|
9760
9804
|
}
|
|
9761
9805
|
// recursively go to deeper level of filter.
|
|
9762
|
-
growObjectFieldTree(tree,
|
|
9806
|
+
growObjectFieldTree(tree, childNodepath, objectFieldNode.value, totalNodes, startNodes);
|
|
9763
9807
|
}
|
|
9764
9808
|
}
|
|
9765
9809
|
}
|
|
@@ -9794,19 +9838,20 @@ function growFieldTree(tree, parentSectionPath, entryNode, parentNode, totalNode
|
|
|
9794
9838
|
}
|
|
9795
9839
|
if (!tree[parentSectionPath].some((field) => field.name === fieldName)) {
|
|
9796
9840
|
tree[parentSectionPath].push({
|
|
9797
|
-
relation: relationType ===
|
|
9798
|
-
relationType ===
|
|
9841
|
+
relation: relationType === PARENT_RELATIONSHIP ||
|
|
9842
|
+
relationType === POLYMORPHIC_PARENT_RELATIONSHIP
|
|
9799
9843
|
? 'parent'
|
|
9800
9844
|
: 'child',
|
|
9801
9845
|
name: fieldName,
|
|
9802
9846
|
});
|
|
9803
|
-
totalNodes.add(fieldName);
|
|
9847
|
+
totalNodes.add(`${parentSectionPath}#${fieldName}`);
|
|
9804
9848
|
}
|
|
9805
9849
|
if (entryNode.selectionSet && entryNode.selectionSet.selections) {
|
|
9806
9850
|
const childNodes = entryNode.selectionSet.selections.filter(isFieldOrInlineFragmentNode);
|
|
9807
9851
|
// recursively build the traversal tree
|
|
9808
9852
|
for (const child of childNodes) {
|
|
9809
|
-
|
|
9853
|
+
const path = `${parentSectionPath}#${fieldName}`;
|
|
9854
|
+
growFieldTree(tree, path, child, entryNode, totalNodes, startNodes);
|
|
9810
9855
|
}
|
|
9811
9856
|
}
|
|
9812
9857
|
}
|
|
@@ -9836,23 +9881,23 @@ function growFieldTree(tree, parentSectionPath, entryNode, parentNode, totalNode
|
|
|
9836
9881
|
}
|
|
9837
9882
|
if (!tree[parentSectionPath].some((field) => field.name === conditionName)) {
|
|
9838
9883
|
tree[parentSectionPath].push({
|
|
9839
|
-
relation: relationType ===
|
|
9840
|
-
relationType ===
|
|
9884
|
+
relation: relationType === PARENT_RELATIONSHIP ||
|
|
9885
|
+
relationType === POLYMORPHIC_PARENT_RELATIONSHIP
|
|
9841
9886
|
? 'parent'
|
|
9842
9887
|
: 'child',
|
|
9843
9888
|
name: conditionName,
|
|
9844
9889
|
});
|
|
9845
|
-
|
|
9890
|
+
const path = `${parentSectionPath}#${conditionName}`;
|
|
9891
|
+
totalNodes.add(path);
|
|
9846
9892
|
}
|
|
9847
9893
|
}
|
|
9848
9894
|
}
|
|
9849
9895
|
// dive deep immediately for 'InlineFragment'
|
|
9850
9896
|
const childNodes = entryNode.selectionSet.selections.filter(isFieldOrInlineFragmentNode);
|
|
9897
|
+
const path = `${parentSectionPath}${entryNode.typeCondition ? '#' + entryNode.typeCondition.name.value : ''}`;
|
|
9851
9898
|
// Navigates into InLineFragment
|
|
9852
9899
|
for (const child of childNodes) {
|
|
9853
|
-
growFieldTree(tree, entryNode
|
|
9854
|
-
? entryNode.typeCondition.name.value
|
|
9855
|
-
: parentSectionPath, child, entryNode, totalNodes, startNodes);
|
|
9900
|
+
growFieldTree(tree, path, child, entryNode, totalNodes, startNodes);
|
|
9856
9901
|
}
|
|
9857
9902
|
}
|
|
9858
9903
|
}
|
|
@@ -9864,7 +9909,7 @@ function growFieldTree(tree, parentSectionPath, entryNode, parentNode, totalNode
|
|
|
9864
9909
|
* @param startNodes start nodes of the tree. It can be used to fetch ObjectInfo immediately
|
|
9865
9910
|
* @param path
|
|
9866
9911
|
*/
|
|
9867
|
-
async function resolveObjectInfos(objectInfotree,
|
|
9912
|
+
async function resolveObjectInfos(objectInfotree, pathToObjectApiNamesMap, startNodes, objectInfoService) {
|
|
9868
9913
|
let objectInfos;
|
|
9869
9914
|
try {
|
|
9870
9915
|
objectInfos = await objectInfoService.getObjectInfos(Array.from(startNodes));
|
|
@@ -9878,9 +9923,9 @@ async function resolveObjectInfos(objectInfotree, objectInfoApiMap, startNodes,
|
|
|
9878
9923
|
throw new Error(`Unable to resolve ObjectInfo(s) for ${Array.from(startNodes)}`);
|
|
9879
9924
|
}
|
|
9880
9925
|
for (const startNode of startNodes) {
|
|
9881
|
-
|
|
9926
|
+
pathToObjectApiNamesMap[startNode] = [startNode];
|
|
9882
9927
|
const children = objectInfotree[startNode];
|
|
9883
|
-
const subObjectInfoMap = await fetchObjectInfos(objectInfotree,
|
|
9928
|
+
const subObjectInfoMap = await fetchObjectInfos(objectInfotree, pathToObjectApiNamesMap, objectInfos, children, startNode, objectInfoService);
|
|
9884
9929
|
objectInfos = { ...objectInfos, ...subObjectInfoMap };
|
|
9885
9930
|
}
|
|
9886
9931
|
return objectInfos;
|
|
@@ -9888,15 +9933,15 @@ async function resolveObjectInfos(objectInfotree, objectInfoApiMap, startNodes,
|
|
|
9888
9933
|
// example 1: 'parentPath': 'ServiceAppointment', 'nodesAtSameLevel': ['Account']
|
|
9889
9934
|
// example 2: 'parentPath': 'ServiceAppointment', 'nodesAtSameLevel': ['Owner'], this example has 2 apiName for the node 'Owner'
|
|
9890
9935
|
// example 3: 'parentPath': 'ServiceAppointment_Owner', 'nodesAtSameLevel': ['User', 'Group']
|
|
9891
|
-
async function fetchObjectInfos(objectInfotree,
|
|
9892
|
-
const objectInfoApiNames =
|
|
9936
|
+
async function fetchObjectInfos(objectInfotree, pathToObjectApiNamesMap, objectInfoMap, nodesAtSameLevel, parentPath, objectInfoService) {
|
|
9937
|
+
const objectInfoApiNames = pathToObjectApiNamesMap[parentPath];
|
|
9893
9938
|
if (!objectInfoApiNames) {
|
|
9894
9939
|
// eslint-disable-next-line
|
|
9895
9940
|
throw new Error(`Object Info does not exist for ${parentPath}`);
|
|
9896
9941
|
}
|
|
9897
9942
|
const validObjectInfoNodes = [];
|
|
9898
9943
|
let updatedObjectInfoMap = {};
|
|
9899
|
-
// InlineFragment and polymorphic field support fits into this scenario
|
|
9944
|
+
// InlineFragment and polymorphic field support fits into this scenario pathToObjectApiNamesMap Entry: 'ServiceAppointment#Owner' -> ['User', 'Group']; ServiceAppointment#Owner#User' -> ['User']
|
|
9900
9945
|
if (objectInfoApiNames.length > 0 &&
|
|
9901
9946
|
nodesAtSameLevel.length > 0 &&
|
|
9902
9947
|
objectInfoApiNames.includes(nodesAtSameLevel[0].name)) {
|
|
@@ -9908,8 +9953,8 @@ async function fetchObjectInfos(objectInfotree, objectInfoApiMap, objectInfoMap,
|
|
|
9908
9953
|
// eslint-disable-next-line
|
|
9909
9954
|
throw new Error(`Condition ${field.name} does not exists for ${parentPath}`);
|
|
9910
9955
|
}
|
|
9911
|
-
const path = `${parentPath}
|
|
9912
|
-
|
|
9956
|
+
const path = `${parentPath}#${field.name}`;
|
|
9957
|
+
pathToObjectApiNamesMap[path] = [field.name];
|
|
9913
9958
|
}
|
|
9914
9959
|
validObjectInfoNodes.push(...nodesAtSameLevel);
|
|
9915
9960
|
updatedObjectInfoMap = { ...objectInfoMap };
|
|
@@ -9924,7 +9969,7 @@ async function fetchObjectInfos(objectInfotree, objectInfoApiMap, objectInfoMap,
|
|
|
9924
9969
|
let apiNames = [];
|
|
9925
9970
|
for (const nodeInfo of nodesAtSameLevel) {
|
|
9926
9971
|
const field = nodeInfo.name;
|
|
9927
|
-
const path = `${parentPath}
|
|
9972
|
+
const path = `${parentPath}#${field}`;
|
|
9928
9973
|
// Handle 'parentRelationship'
|
|
9929
9974
|
if (nodeInfo.relation === 'parent') {
|
|
9930
9975
|
const relationshipId = referenceIdFieldForRelationship(field);
|
|
@@ -9942,21 +9987,21 @@ async function fetchObjectInfos(objectInfotree, objectInfoApiMap, objectInfoMap,
|
|
|
9942
9987
|
}
|
|
9943
9988
|
}
|
|
9944
9989
|
// This is a polymorphic field
|
|
9945
|
-
if (fieldDefinition.referenceToInfos.length > 1 && objectInfotree[
|
|
9990
|
+
if (fieldDefinition.referenceToInfos.length > 1 && objectInfotree[path]) {
|
|
9946
9991
|
// Fields needs to expand and heterogenous entity ObjectInfo needs to be fetched
|
|
9947
|
-
const referencedNodeInfos = objectInfotree[
|
|
9992
|
+
const referencedNodeInfos = objectInfotree[path];
|
|
9948
9993
|
const requestedApiNames = referencedNodeInfos.map((referenceNodeInfo) => referenceNodeInfo.name);
|
|
9949
9994
|
// Fetches requested ObjectInfo only. Some entity's relation field could define more than 6 references. Only references show up in query need to be handled.
|
|
9950
|
-
if (requestedApiNames.length > 0 && objectInfotree[
|
|
9995
|
+
if (requestedApiNames.length > 0 && objectInfotree[path]) {
|
|
9951
9996
|
fieldDefinition.referenceToInfos
|
|
9952
9997
|
.filter((referenceToInfo) => requestedApiNames.includes(referenceToInfo.apiName))
|
|
9953
9998
|
.forEach((ref) => {
|
|
9954
|
-
if (!
|
|
9955
|
-
|
|
9999
|
+
if (!pathToObjectApiNamesMap[path]) {
|
|
10000
|
+
pathToObjectApiNamesMap[path] = [];
|
|
9956
10001
|
}
|
|
9957
10002
|
// 'ServiceAppointment_Owner' ->['User', 'Group']
|
|
9958
|
-
if (!
|
|
9959
|
-
|
|
10003
|
+
if (!pathToObjectApiNamesMap[path].includes(ref.apiName)) {
|
|
10004
|
+
pathToObjectApiNamesMap[path].push(ref.apiName);
|
|
9960
10005
|
}
|
|
9961
10006
|
if (!apiNames.includes(ref.apiName)) {
|
|
9962
10007
|
apiNames.push(ref.apiName);
|
|
@@ -9966,11 +10011,11 @@ async function fetchObjectInfos(objectInfotree, objectInfoApiMap, objectInfoMap,
|
|
|
9966
10011
|
}
|
|
9967
10012
|
else if (fieldDefinition.referenceToInfos.length === 1) {
|
|
9968
10013
|
const ref = fieldDefinition.referenceToInfos[0];
|
|
9969
|
-
if (!
|
|
9970
|
-
|
|
10014
|
+
if (!pathToObjectApiNamesMap[path]) {
|
|
10015
|
+
pathToObjectApiNamesMap[path] = [];
|
|
9971
10016
|
}
|
|
9972
|
-
if (!
|
|
9973
|
-
|
|
10017
|
+
if (!pathToObjectApiNamesMap[path].includes(ref.apiName)) {
|
|
10018
|
+
pathToObjectApiNamesMap[path].push(ref.apiName);
|
|
9974
10019
|
}
|
|
9975
10020
|
if (!apiNames.includes(ref.apiName)) {
|
|
9976
10021
|
apiNames.push(ref.apiName);
|
|
@@ -9981,11 +10026,11 @@ async function fetchObjectInfos(objectInfotree, objectInfoApiMap, objectInfoMap,
|
|
|
9981
10026
|
// handles 'childRelationship'
|
|
9982
10027
|
const childRelationship = parentObjectInfo.childRelationships.find((childRelationship) => childRelationship.relationshipName === field);
|
|
9983
10028
|
if (childRelationship) {
|
|
9984
|
-
if (!
|
|
9985
|
-
|
|
10029
|
+
if (!pathToObjectApiNamesMap[path]) {
|
|
10030
|
+
pathToObjectApiNamesMap[path] = [];
|
|
9986
10031
|
}
|
|
9987
|
-
if (!
|
|
9988
|
-
|
|
10032
|
+
if (!pathToObjectApiNamesMap[path].includes(childRelationship.childObjectApiName)) {
|
|
10033
|
+
pathToObjectApiNamesMap[path].push(childRelationship.childObjectApiName);
|
|
9989
10034
|
}
|
|
9990
10035
|
if (!apiNames.includes(childRelationship.childObjectApiName)) {
|
|
9991
10036
|
apiNames.push(childRelationship.childObjectApiName);
|
|
@@ -10007,10 +10052,10 @@ async function fetchObjectInfos(objectInfotree, objectInfoApiMap, objectInfoMap,
|
|
|
10007
10052
|
}
|
|
10008
10053
|
for (const nodeInfo of validObjectInfoNodes) {
|
|
10009
10054
|
const field = nodeInfo.name;
|
|
10010
|
-
const
|
|
10011
|
-
const
|
|
10055
|
+
const path = `${parentPath}#${field}`;
|
|
10056
|
+
const subLevelFields = objectInfotree[path];
|
|
10012
10057
|
if (subLevelFields && subLevelFields.length > 0) {
|
|
10013
|
-
const subObjectInfos = await fetchObjectInfos(objectInfotree,
|
|
10058
|
+
const subObjectInfos = await fetchObjectInfos(objectInfotree, pathToObjectApiNamesMap, updatedObjectInfoMap, subLevelFields, path, objectInfoService);
|
|
10014
10059
|
updatedObjectInfoMap = { ...updatedObjectInfoMap, ...subObjectInfos };
|
|
10015
10060
|
}
|
|
10016
10061
|
}
|
|
@@ -10025,27 +10070,29 @@ async function fetchObjectInfos(objectInfotree, objectInfoApiMap, objectInfoMap,
|
|
|
10025
10070
|
* 'path' and 'queryNode' is 1 level above the 'filterNode'
|
|
10026
10071
|
* @param filterNode filter node which needs to be injected. For example, 'State' ObjectFieldNode within filter 'where: { State: { eq: "Nova Scotia" }}'
|
|
10027
10072
|
* @param idState ID state will be updated to determine if the ID fields in AST need to be swapped. The swapping happens later.
|
|
10028
|
-
* @param
|
|
10073
|
+
* @param parentPath path to the current filterNode's parent. For example, path could be 'ServiceApointment' when filterNode is 'State'. If the path does not exist in 'pathToObjectApiNamesMap', parent node is not an field of relationship or recordQuery
|
|
10074
|
+
* @param isParentPolymorphic true if parent points to a polymorphic field.
|
|
10029
10075
|
* @param queryNode referece FieldNode which provides the information if 'filterNode' exist in it nor not.
|
|
10030
10076
|
* @param objectInfos ObjectInfo map used in injection. If ObjectInfo misses or field does not exist in ObjectInfo, the error will be thrown
|
|
10031
|
-
* @param
|
|
10077
|
+
* @param pathToObjectApiNamesMap map used to locate the ObjectInfo. The key is path to a field, value is the ObjectInfo's apiName array. In the case of polymorphic fields, the apiName array have 2 or more elements. For example, 'ServiceAppointment' -> ['ServiceAppointment']; 'ServiceAppointment_Account' -> ['Account'], 'ServiceAppointment_Owner' -> ['User', 'Group'].
|
|
10032
10078
|
* @param draftFunctions functions for working with record ids that may be draft-created ids
|
|
10033
10079
|
* @returns an array of nodes with injected fields
|
|
10034
10080
|
*/
|
|
10035
|
-
function injectFilter(filterNode, idState,
|
|
10081
|
+
function injectFilter(filterNode, idState, parentPath, isParentPolymorphic, objectInfos, pathToObjectApiNamesMap, draftFunctions, queryNode) {
|
|
10036
10082
|
const injectedSelections = [];
|
|
10083
|
+
let isPolymorphicField = false;
|
|
10037
10084
|
switch (filterNode.kind) {
|
|
10038
10085
|
case Kind.ARGUMENT:
|
|
10039
10086
|
if (filterNode.value.kind !== 'ObjectValue')
|
|
10040
10087
|
return [];
|
|
10041
10088
|
filterNode.value.fields.forEach((objectFieldNode) => {
|
|
10042
|
-
let subResults = injectFilter(objectFieldNode, idState,
|
|
10089
|
+
let subResults = injectFilter(objectFieldNode, idState, parentPath, isParentPolymorphic, objectInfos, pathToObjectApiNamesMap, draftFunctions, queryNode);
|
|
10043
10090
|
for (const subResult of subResults) {
|
|
10044
10091
|
mergeOrAddToGroup(injectedSelections, subResult);
|
|
10045
10092
|
}
|
|
10046
10093
|
// multiple Ids might need to be swapped. remember their paths for faster write.
|
|
10047
10094
|
if (idState.swapNeeded) {
|
|
10048
|
-
idState.paths.push(
|
|
10095
|
+
idState.paths.push(parentPath);
|
|
10049
10096
|
}
|
|
10050
10097
|
});
|
|
10051
10098
|
return injectedSelections;
|
|
@@ -10054,7 +10101,7 @@ function injectFilter(filterNode, idState, path, objectInfos, objectInfoApiMap,
|
|
|
10054
10101
|
case Kind.LIST: {
|
|
10055
10102
|
filterNode.value.values.filter(isObjectValueNode).forEach((objectValueNode) => {
|
|
10056
10103
|
objectValueNode.fields.forEach((objectFieldNode) => {
|
|
10057
|
-
const subResults = injectFilter(objectFieldNode, idState,
|
|
10104
|
+
const subResults = injectFilter(objectFieldNode, idState, parentPath, isParentPolymorphic, objectInfos, pathToObjectApiNamesMap, draftFunctions, queryNode);
|
|
10058
10105
|
for (const subResult of subResults) {
|
|
10059
10106
|
mergeOrAddToGroup(injectedSelections, subResult);
|
|
10060
10107
|
}
|
|
@@ -10065,7 +10112,7 @@ function injectFilter(filterNode, idState, path, objectInfos, objectInfoApiMap,
|
|
|
10065
10112
|
case Kind.OBJECT: {
|
|
10066
10113
|
if (filterNode.name.value === 'not') {
|
|
10067
10114
|
filterNode.value.fields.forEach((objectFieldNode) => {
|
|
10068
|
-
const subResults = injectFilter(objectFieldNode, idState,
|
|
10115
|
+
const subResults = injectFilter(objectFieldNode, idState, parentPath, isParentPolymorphic, objectInfos, pathToObjectApiNamesMap, draftFunctions, queryNode);
|
|
10069
10116
|
for (const subResult of subResults) {
|
|
10070
10117
|
mergeOrAddToGroup(injectedSelections, subResult);
|
|
10071
10118
|
}
|
|
@@ -10075,15 +10122,15 @@ function injectFilter(filterNode, idState, path, objectInfos, objectInfoApiMap,
|
|
|
10075
10122
|
let apiNames = [];
|
|
10076
10123
|
let isScalarField = false;
|
|
10077
10124
|
//It is possible that this is a polymorphic field
|
|
10078
|
-
apiNames =
|
|
10079
|
-
// example: path: '
|
|
10125
|
+
apiNames = pathToObjectApiNamesMap[parentPath];
|
|
10126
|
+
// example: path: 'ServiceAppointment#LastModifiedDate'; filterNode: '{eq: {literal: LAST_WEEK}}'. queryNode: 'LastModifedDate { value}' FilterNode's parent has been verifed as a valid node
|
|
10080
10127
|
if (apiNames === undefined) {
|
|
10081
10128
|
isScalarField = true;
|
|
10082
10129
|
}
|
|
10083
10130
|
else {
|
|
10084
10131
|
if (apiNames.some((apiName) => objectInfos[apiName] === undefined)) {
|
|
10085
10132
|
// eslint-disable-next-line
|
|
10086
|
-
throw new Error(`ObjectInfo is missing for ${
|
|
10133
|
+
throw new Error(`ObjectInfo is missing for ${parentPath}`);
|
|
10087
10134
|
}
|
|
10088
10135
|
}
|
|
10089
10136
|
if (isScalarField) {
|
|
@@ -10105,29 +10152,19 @@ function injectFilter(filterNode, idState, path, objectInfos, objectInfoApiMap,
|
|
|
10105
10152
|
}
|
|
10106
10153
|
});
|
|
10107
10154
|
let isSpanning = false;
|
|
10155
|
+
// if true, current node is a polymorphic concrete type node. For example, field node `User` under `Owner`
|
|
10108
10156
|
let isInlineFragment = false;
|
|
10109
|
-
let isPolymorphicField = false;
|
|
10110
10157
|
let isTypeNameExisting = false;
|
|
10111
10158
|
let curPath;
|
|
10112
10159
|
let fieldName = filterNode.name.value;
|
|
10113
|
-
curPath = `${
|
|
10114
|
-
if (
|
|
10160
|
+
curPath = `${parentPath}#${fieldName}`;
|
|
10161
|
+
if (pathToObjectApiNamesMap[curPath] &&
|
|
10162
|
+
pathToObjectApiNamesMap[curPath].length > 0) {
|
|
10115
10163
|
isSpanning = true;
|
|
10116
|
-
|
|
10117
|
-
|
|
10118
|
-
|
|
10119
|
-
|
|
10120
|
-
isInlineFragment = true;
|
|
10121
|
-
}
|
|
10122
|
-
}
|
|
10123
|
-
// Checks if the current filter node is a polymorphic field. 'ServiceAppointment_Owner' --> ['User']; 'ServiceAppointment_Owner_User' --> ['User']
|
|
10124
|
-
const childApiName = objectInfoApiMap[curPath][0];
|
|
10125
|
-
const trialApiNames = objectInfoApiMap[`${curPath}_${childApiName}`];
|
|
10126
|
-
if (trialApiNames !== undefined &&
|
|
10127
|
-
trialApiNames.length === 1 &&
|
|
10128
|
-
trialApiNames[0] === childApiName) {
|
|
10129
|
-
isPolymorphicField = true;
|
|
10130
|
-
}
|
|
10164
|
+
isInlineFragment =
|
|
10165
|
+
isParentPolymorphic &&
|
|
10166
|
+
pathToObjectApiNamesMap[curPath].length === 1;
|
|
10167
|
+
isPolymorphicField = isPolymorphicFieldPath(curPath, pathToObjectApiNamesMap, objectInfos);
|
|
10131
10168
|
}
|
|
10132
10169
|
// When filter node is at InLineFragment Level(a concrete entity of polymorphic field), query node is one level up. For example, ObjectFieldNode is ...{User:{...}}, queryNode is Owner:[User]
|
|
10133
10170
|
if (isInlineFragment) {
|
|
@@ -10171,9 +10208,9 @@ function injectFilter(filterNode, idState, path, objectInfos, objectInfoApiMap,
|
|
|
10171
10208
|
throw new Error(`Field ${fieldName} does not exist in ${apiNames[0]}`);
|
|
10172
10209
|
}
|
|
10173
10210
|
}
|
|
10174
|
-
const objectInfoName =
|
|
10175
|
-
?
|
|
10176
|
-
:
|
|
10211
|
+
const objectInfoName = pathToObjectApiNamesMap[curPath] !== undefined
|
|
10212
|
+
? pathToObjectApiNamesMap[curPath][0]
|
|
10213
|
+
: pathToObjectApiNamesMap[parentPath][0];
|
|
10177
10214
|
const isIdField = isFieldAnIdField(filterNode.name.value, objectInfos[objectInfoName]);
|
|
10178
10215
|
if (!isIdField) {
|
|
10179
10216
|
let subSelectionNodes = [];
|
|
@@ -10185,7 +10222,7 @@ function injectFilter(filterNode, idState, path, objectInfos, objectInfoApiMap,
|
|
|
10185
10222
|
updateIDInfo(subFieldNode, idState, draftFunctions);
|
|
10186
10223
|
}
|
|
10187
10224
|
// try injecting the fields within predicate no matter it has relation or not.
|
|
10188
|
-
let subResults = injectFilter(subFieldNode, idState, curPath, objectInfos,
|
|
10225
|
+
let subResults = injectFilter(subFieldNode, idState, curPath, isPolymorphicField, objectInfos, pathToObjectApiNamesMap, draftFunctions, existingFields ? existingFields[0] : undefined);
|
|
10189
10226
|
subSelectionNodes = subSelectionNodes.concat(subResults);
|
|
10190
10227
|
});
|
|
10191
10228
|
if (!subFieldsHasId) {
|
|
@@ -10346,6 +10383,44 @@ function mergeOrAddToGroup(group, element) {
|
|
|
10346
10383
|
}
|
|
10347
10384
|
group.push(element);
|
|
10348
10385
|
}
|
|
10386
|
+
// checks if the path points to a polymorphic field. For example, for the below `pathToObjectApiNamesMap`
|
|
10387
|
+
// {
|
|
10388
|
+
// 'ServiceAppointment' -> ['ServiceAppointment']
|
|
10389
|
+
// 'ServiceAppointment#Owner' --> ['User'],
|
|
10390
|
+
// 'ServiceAppointment#Owner#User' --> ['User']
|
|
10391
|
+
// }
|
|
10392
|
+
// path `ServiceAppointment#Owner` points to a polymorphic field, but path `ServiceAppointment#Owner#User` does not.
|
|
10393
|
+
function isPolymorphicFieldPath(path, pathToObjectApiNamesMap, objectInfos) {
|
|
10394
|
+
const lastSegmentIndex = path.lastIndexOf('#');
|
|
10395
|
+
if (lastSegmentIndex < 0) {
|
|
10396
|
+
return false;
|
|
10397
|
+
}
|
|
10398
|
+
const lastSegment = path.slice(lastSegmentIndex + 1);
|
|
10399
|
+
const parentApiPath = path.slice(0, lastSegmentIndex);
|
|
10400
|
+
if (pathToObjectApiNamesMap[parentApiPath] === undefined) {
|
|
10401
|
+
return false;
|
|
10402
|
+
}
|
|
10403
|
+
const parentObjectApiNames = pathToObjectApiNamesMap[parentApiPath];
|
|
10404
|
+
// If the last segment is a Polymorphic field, its immediate parent is a concrete object entity, which has 1 objectApiName mapped to the parent path in `pathToObjectApiNamesMap`.
|
|
10405
|
+
// For example, we like to check if `ServiceAppointment#Owner` path is polymorphic. The last segment is `Owner` and its parent `ServiceAppointment` has one element (which is also `ServiceAppointment`) array as its value.
|
|
10406
|
+
// Below are the entries in `pathToObjectApiNamesMap`
|
|
10407
|
+
// {
|
|
10408
|
+
// `ServiceAppointmen`t: [`ServiceAppointment`],
|
|
10409
|
+
// `ServiceAppointment#Owner`: [`User`, `Group`],
|
|
10410
|
+
// `ServiceAppointment#Owner#User`: [`User`],
|
|
10411
|
+
// `ServiceAppointment#Owner#Group`: [`Group`],
|
|
10412
|
+
// }
|
|
10413
|
+
if (parentObjectApiNames.length !== 1) {
|
|
10414
|
+
return false;
|
|
10415
|
+
}
|
|
10416
|
+
const parentObjectInfo = objectInfos[parentObjectApiNames[0]];
|
|
10417
|
+
const relationshipField = referenceIdFieldForRelationship(lastSegment);
|
|
10418
|
+
let fieldDefinition = parentObjectInfo.fields[relationshipField];
|
|
10419
|
+
if (fieldDefinition === undefined) {
|
|
10420
|
+
return false;
|
|
10421
|
+
}
|
|
10422
|
+
return fieldDefinition.polymorphicForeignKey;
|
|
10423
|
+
}
|
|
10349
10424
|
function isFieldAnIdField(fieldName, objectInfo) {
|
|
10350
10425
|
if (fieldName === 'Id')
|
|
10351
10426
|
return true;
|
|
@@ -10398,10 +10473,10 @@ function updateIDInfo(fieldNode, idState, draftFunctions) {
|
|
|
10398
10473
|
* @param parentNode parent node of param 1
|
|
10399
10474
|
* @param ancestors ancester of param 1
|
|
10400
10475
|
* @param objectInfos ObjectInfo map used in injection. If ObjectInfo misses or field does not exist in ObjectInfo, the error will be thrown
|
|
10401
|
-
* @param
|
|
10476
|
+
* @param pathToObjectApiNamesMap map used to locate the ObjectInfo. The key is path to a field, value is the ObjectInfo's apiName array. In the case of polymorphic fields, the apiName array have 2 or more elements. For example, 'ServiceAppointment' -> ['ServiceAppointment']; 'ServiceAppointment#Account' -> ['Account'], 'ServiceAppointment#Owner' -> ['User', 'Group'].
|
|
10402
10477
|
* @return injected SelectionNodes used to construct the InlineFragment.
|
|
10403
10478
|
*/
|
|
10404
|
-
function injectFields(selections, parentNode, ancestors, objectInfos,
|
|
10479
|
+
function injectFields(selections, parentNode, parentPath, ancestors, objectInfos, pathToObjectApiNamesMap) {
|
|
10405
10480
|
/**
|
|
10406
10481
|
* 1 parentship can return 2 FieldNode which need to be flattened
|
|
10407
10482
|
* Concact: { ** Contact { ** ContactId {
|
|
@@ -10419,6 +10494,10 @@ function injectFields(selections, parentNode, ancestors, objectInfos, objectInfo
|
|
|
10419
10494
|
if (!selection.selectionSet) {
|
|
10420
10495
|
return selection;
|
|
10421
10496
|
}
|
|
10497
|
+
const segment = isFieldNode(selection)
|
|
10498
|
+
? selection.name.value
|
|
10499
|
+
: selection.typeCondition.name.value;
|
|
10500
|
+
const curPath = `${parentPath}#${segment}`;
|
|
10422
10501
|
const spanningSubSelections = [];
|
|
10423
10502
|
for (const subSelection of selection.selectionSet.selections) {
|
|
10424
10503
|
if (isFieldSpanning(subSelection, selection)) {
|
|
@@ -10426,7 +10505,7 @@ function injectFields(selections, parentNode, ancestors, objectInfos, objectInfo
|
|
|
10426
10505
|
}
|
|
10427
10506
|
}
|
|
10428
10507
|
// Handles multiple level field injection like 'ServiceAppointment' --> 'Account' --> 'Owner'
|
|
10429
|
-
const subInjectedSelections = injectFields(spanningSubSelections, selection, ancestors, objectInfos,
|
|
10508
|
+
const subInjectedSelections = injectFields(spanningSubSelections, selection, curPath, ancestors, objectInfos, pathToObjectApiNamesMap);
|
|
10430
10509
|
if (!selection.selectionSet) {
|
|
10431
10510
|
return selection;
|
|
10432
10511
|
}
|
|
@@ -10469,7 +10548,7 @@ function injectFields(selections, parentNode, ancestors, objectInfos, objectInfo
|
|
|
10469
10548
|
}
|
|
10470
10549
|
}
|
|
10471
10550
|
// For polymorphic fields, the Id field is excluded.
|
|
10472
|
-
const excludeId =
|
|
10551
|
+
const excludeId = isPolymorphicFieldPath(curPath, pathToObjectApiNamesMap, objectInfos);
|
|
10473
10552
|
const idSelection = [];
|
|
10474
10553
|
if (!excludeId && !hasIdAlready) {
|
|
10475
10554
|
idSelection.push({
|
|
@@ -10480,8 +10559,8 @@ function injectFields(selections, parentNode, ancestors, objectInfos, objectInfo
|
|
|
10480
10559
|
},
|
|
10481
10560
|
});
|
|
10482
10561
|
}
|
|
10483
|
-
// Inject '__typename' for
|
|
10484
|
-
// please reference 'removeSyntheticFields'.
|
|
10562
|
+
// Inject '__typename' for InlineFragment. '__typename' field acts as a reference to concrete type of a polymorphic field or a standard field in the returned GQL response, which equals to
|
|
10563
|
+
// `typedCondition` of the InlineFragment in the query AST. It is used to match JSON response with AST node. For more detail, please reference 'removeSyntheticFields'.
|
|
10485
10564
|
if (isInlineFragmentNode(selection) &&
|
|
10486
10565
|
!selection.selectionSet.selections.find((selection) => isFieldNode(selection) && selection.name.value === '__typename')) {
|
|
10487
10566
|
idSelection.push({
|
|
@@ -10515,7 +10594,7 @@ function injectFields(selections, parentNode, ancestors, objectInfos, objectInfo
|
|
|
10515
10594
|
if (isFieldNode(parentNode) && parentNode.selectionSet && parentNode.name.value === 'node') {
|
|
10516
10595
|
if (parentNode.selectionSet.selections
|
|
10517
10596
|
.filter(isFieldOrInlineFragmentNode)
|
|
10518
|
-
.some((selectionNode) => isRelationship(selectionNode,
|
|
10597
|
+
.some((selectionNode) => isRelationship(selectionNode, CHILD_RELATIONSHIP))) {
|
|
10519
10598
|
if (!parentNode.selectionSet.selections
|
|
10520
10599
|
.filter(isFieldNode)
|
|
10521
10600
|
.some((sibling) => sibling.name.value === 'Id')) {
|
|
@@ -10534,15 +10613,15 @@ function injectFields(selections, parentNode, ancestors, objectInfos, objectInfo
|
|
|
10534
10613
|
if (parentInfo.parentIndex >= 0) {
|
|
10535
10614
|
// example node { TimeSheetEntries { edges { node { Id }}}}
|
|
10536
10615
|
const parent = parentInfo.node;
|
|
10537
|
-
if (isRelationship(parent,
|
|
10616
|
+
if (isRelationship(parent, CHILD_RELATIONSHIP)) {
|
|
10538
10617
|
const unVisitedAncestors = ancestors.slice(0, parentInfo.parentIndex);
|
|
10539
10618
|
// path : "TimeSheet"
|
|
10540
10619
|
const grandParentPath = findAncesterPath(unVisitedAncestors);
|
|
10541
|
-
if (
|
|
10542
|
-
|
|
10620
|
+
if (pathToObjectApiNamesMap &&
|
|
10621
|
+
pathToObjectApiNamesMap[grandParentPath] &&
|
|
10543
10622
|
objectInfos &&
|
|
10544
|
-
objectInfos[
|
|
10545
|
-
const grandParentObjectInfo = objectInfos[
|
|
10623
|
+
objectInfos[pathToObjectApiNamesMap[grandParentPath][0]]) {
|
|
10624
|
+
const grandParentObjectInfo = objectInfos[pathToObjectApiNamesMap[grandParentPath][0]];
|
|
10546
10625
|
// exmaple "TimeSheetEntries"
|
|
10547
10626
|
const parentFieldName = parent.name.value;
|
|
10548
10627
|
const targetRelationship = grandParentObjectInfo.childRelationships.find((childRelationship) => {
|
|
@@ -10663,7 +10742,7 @@ const assignedToMeFragmentSelections = [
|
|
|
10663
10742
|
},
|
|
10664
10743
|
value: {
|
|
10665
10744
|
kind: 'StringValue',
|
|
10666
|
-
value:
|
|
10745
|
+
value: CHILD_RELATIONSHIP,
|
|
10667
10746
|
block: false,
|
|
10668
10747
|
},
|
|
10669
10748
|
},
|
|
@@ -10755,7 +10834,7 @@ const assignedToMeFragmentSelections = [
|
|
|
10755
10834
|
},
|
|
10756
10835
|
value: {
|
|
10757
10836
|
kind: 'StringValue',
|
|
10758
|
-
value:
|
|
10837
|
+
value: PARENT_RELATIONSHIP,
|
|
10759
10838
|
block: false,
|
|
10760
10839
|
},
|
|
10761
10840
|
},
|
|
@@ -10892,7 +10971,9 @@ function handleNonArrayJsonProperty(selection, fieldName, jsonInput, jsonOutput)
|
|
|
10892
10971
|
jsonOutput[fieldName] = null;
|
|
10893
10972
|
return;
|
|
10894
10973
|
}
|
|
10895
|
-
jsonOutput[fieldName]
|
|
10974
|
+
if (jsonOutput[fieldName] === undefined) {
|
|
10975
|
+
jsonOutput[fieldName] = {};
|
|
10976
|
+
}
|
|
10896
10977
|
createUserJsonOutput(selection, jsonInput[fieldName], jsonOutput[fieldName]);
|
|
10897
10978
|
}
|
|
10898
10979
|
else {
|
|
@@ -15900,6 +15981,9 @@ class PrimingSession extends EventEmitter {
|
|
|
15900
15981
|
this.ldsRecordRefresher = config.ldsRecordRefresher;
|
|
15901
15982
|
this.networkWorkerPool = new AsyncWorkerPool(this.concurrency);
|
|
15902
15983
|
this.useBatchGQL = ldsPrimingGraphqlBatch.isOpen({ fallback: false });
|
|
15984
|
+
if (this.useBatchGQL) {
|
|
15985
|
+
this.batchSize = this.batchSize / DEFAULT_GQL_QUERY_BATCH_SIZE;
|
|
15986
|
+
}
|
|
15903
15987
|
this.conflictPool = new ConflictPool(config.store, this.objectInfoLoader);
|
|
15904
15988
|
}
|
|
15905
15989
|
// function that enqueues priming work
|
|
@@ -16761,4 +16845,4 @@ register({
|
|
|
16761
16845
|
});
|
|
16762
16846
|
|
|
16763
16847
|
export { getRuntime, registerReportObserver, reportGraphqlQueryParseError };
|
|
16764
|
-
// version: 1.229.0-
|
|
16848
|
+
// version: 1.229.0-dev3-175ac936b
|