@graphql-tools/executor
Version:
Fork of GraphQL.js' execute function
190 lines (189 loc) • 7.84 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.collectIncrementalFields = collectIncrementalFields;
exports.collectIncrementalSubfields = collectIncrementalSubfields;
exports.buildExecutionPlan = buildExecutionPlan;
exports.groupedFieldSetToFieldMap = groupedFieldSetToFieldMap;
exports.rootDeferUsage = rootDeferUsage;
exports.labelForDeferUsageSet = labelForDeferUsageSet;
const graphql_1 = require("graphql");
const utils_1 = require("@graphql-tools/utils");
const getBySet_js_1 = require("./getBySet.js");
const isSameSet_js_1 = require("./isSameSet.js");
class FieldDetailsMap extends Map {
add(key, item) {
const group = this.get(key);
if (group === undefined) {
this.set(key, [item]);
}
else {
group.push(item);
}
}
}
function collectIncrementalFields(schema, fragments, variableValues, runtimeType, selectionSet, parentDeferUsage) {
const groupedFieldSet = new FieldDetailsMap();
const newDeferUsages = [];
const context = {
schema,
fragments,
variableValues,
runtimeType,
visitedFragmentNames: new Map(),
};
collectFieldsImpl(context, selectionSet, groupedFieldSet, newDeferUsages, parentDeferUsage);
return { groupedFieldSet, newDeferUsages };
}
function collectIncrementalSubfields(schema, fragments, variableValues, returnType, fieldNodes, parentDeferUsage, deferUsageByFieldNode) {
const groupedFieldSet = new FieldDetailsMap();
const newDeferUsages = [];
const context = {
schema,
fragments,
variableValues,
runtimeType: returnType,
visitedFragmentNames: new Map(),
};
for (const node of fieldNodes) {
if (node.selectionSet) {
const deferUsage = deferUsageByFieldNode?.has(node)
? deferUsageByFieldNode.get(node)
: parentDeferUsage;
collectFieldsImpl(context, node.selectionSet, groupedFieldSet, newDeferUsages, deferUsage);
}
}
return { groupedFieldSet, newDeferUsages };
}
function collectFieldsImpl(context, selectionSet, groupedFieldSet, newDeferUsages, deferUsage) {
const { schema, fragments, variableValues, runtimeType, visitedFragmentNames } = context;
for (const selection of selectionSet.selections) {
switch (selection.kind) {
case graphql_1.Kind.FIELD: {
if (!(0, utils_1.shouldIncludeNode)(variableValues, selection)) {
continue;
}
groupedFieldSet.add((0, utils_1.getFieldEntryKey)(selection), {
node: selection,
deferUsage,
});
break;
}
case graphql_1.Kind.INLINE_FRAGMENT: {
if (!(0, utils_1.shouldIncludeNode)(variableValues, selection) ||
!(0, utils_1.doesFragmentConditionMatch)(schema, selection, runtimeType)) {
continue;
}
const newDeferUsage = getDeferUsage(variableValues, selection, deferUsage);
if (!newDeferUsage) {
collectFieldsImpl(context, selection.selectionSet, groupedFieldSet, newDeferUsages, deferUsage);
}
else {
newDeferUsages.push(newDeferUsage);
collectFieldsImpl(context, selection.selectionSet, groupedFieldSet, newDeferUsages, newDeferUsage);
}
break;
}
case graphql_1.Kind.FRAGMENT_SPREAD: {
const fragName = selection.name.value;
if (!(0, utils_1.shouldIncludeNode)(variableValues, selection)) {
continue;
}
const fragment = fragments[fragName];
if (!fragment || !(0, utils_1.doesFragmentConditionMatch)(schema, fragment, runtimeType)) {
continue;
}
const newDeferUsage = getDeferUsage(variableValues, selection, deferUsage);
const visitedAsDeferred = visitedFragmentNames.get(fragName);
let maybeNewDeferUsage;
if (!newDeferUsage) {
if (visitedAsDeferred === false) {
continue;
}
visitedFragmentNames.set(fragName, false);
maybeNewDeferUsage = deferUsage;
}
else {
if (visitedAsDeferred !== undefined) {
continue;
}
visitedFragmentNames.set(fragName, true);
newDeferUsages.push(newDeferUsage);
maybeNewDeferUsage = newDeferUsage;
}
collectFieldsImpl(context, fragment.selectionSet, groupedFieldSet, newDeferUsages, maybeNewDeferUsage);
break;
}
}
}
}
function getDeferUsage(variableValues, node, parentDeferUsage) {
const defer = (0, utils_1.getDirectiveValues)(utils_1.GraphQLDeferDirective, node, variableValues);
if (!defer || defer['if'] === false) {
return;
}
return {
label: typeof defer['label'] === 'string' ? defer['label'] : undefined,
parentDeferUsage,
};
}
function buildExecutionPlan(originalGroupedFieldSet, parentDeferUsages = new Set()) {
const groupedFieldSet = new Map();
const newGroupedFieldSets = new Map();
for (const [responseKey, fieldDetailsList] of originalGroupedFieldSet) {
const filteredDeferUsageSet = getFilteredDeferUsageSet(fieldDetailsList);
if ((0, isSameSet_js_1.isSameSet)(filteredDeferUsageSet, parentDeferUsages)) {
groupedFieldSet.set(responseKey, fieldDetailsList);
continue;
}
let newGroupedFieldSet = (0, getBySet_js_1.getBySet)(newGroupedFieldSets, filteredDeferUsageSet);
if (newGroupedFieldSet === undefined) {
newGroupedFieldSet = new Map();
newGroupedFieldSets.set(filteredDeferUsageSet, newGroupedFieldSet);
}
newGroupedFieldSet.set(responseKey, fieldDetailsList);
}
return { groupedFieldSet, newGroupedFieldSets };
}
function getFilteredDeferUsageSet(fieldDetailsList) {
const filteredDeferUsageSet = new Set();
for (const fieldDetails of fieldDetailsList) {
const deferUsage = fieldDetails.deferUsage;
if (deferUsage === undefined) {
filteredDeferUsageSet.clear();
return filteredDeferUsageSet;
}
filteredDeferUsageSet.add(deferUsage);
}
for (const deferUsage of filteredDeferUsageSet) {
let parentDeferUsage = deferUsage.parentDeferUsage;
while (parentDeferUsage !== undefined) {
if (filteredDeferUsageSet.has(parentDeferUsage)) {
filteredDeferUsageSet.delete(deferUsage);
break;
}
parentDeferUsage = parentDeferUsage.parentDeferUsage;
}
}
return filteredDeferUsageSet;
}
function groupedFieldSetToFieldMap(groupedFieldSet, deferUsageByFieldNode) {
const fields = new Map();
for (const [responseKey, fieldDetailsList] of groupedFieldSet) {
fields.set(responseKey, fieldDetailsList.map(details => {
deferUsageByFieldNode?.set(details.node, details.deferUsage);
return details.node;
}));
}
return fields;
}
function rootDeferUsage(deferUsageSet) {
for (const usage of deferUsageSet) {
if (usage.parentDeferUsage == null || !deferUsageSet.has(usage.parentDeferUsage)) {
return usage;
}
}
return undefined;
}
function labelForDeferUsageSet(deferUsageSet) {
return rootDeferUsage(deferUsageSet)?.label;
}