@graphql-tools/delegate
Version:
A set of utils for faster development of GraphQL tools
175 lines (174 loc) • 8.29 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.mergeFields = exports.getActualFieldNodes = exports.getUnpathedErrors = exports.getSubschema = exports.annotateExternalObject = exports.isExternalObject = void 0;
const graphql_1 = require("graphql");
const utils_1 = require("@graphql-tools/utils");
const debugging_js_1 = require("./debugging.js");
const symbols_js_1 = require("./symbols.js");
function isExternalObject(data) {
return data[symbols_js_1.UNPATHED_ERRORS_SYMBOL] !== undefined;
}
exports.isExternalObject = isExternalObject;
function annotateExternalObject(object, errors, subschema, subschemaMap) {
Object.defineProperties(object, {
[symbols_js_1.OBJECT_SUBSCHEMA_SYMBOL]: { value: subschema },
[symbols_js_1.FIELD_SUBSCHEMA_MAP_SYMBOL]: { value: subschemaMap },
[symbols_js_1.UNPATHED_ERRORS_SYMBOL]: { value: errors },
});
return object;
}
exports.annotateExternalObject = annotateExternalObject;
function getSubschema(object, responseKey) {
return object[symbols_js_1.FIELD_SUBSCHEMA_MAP_SYMBOL][responseKey] ?? object[symbols_js_1.OBJECT_SUBSCHEMA_SYMBOL];
}
exports.getSubschema = getSubschema;
function getUnpathedErrors(object) {
return object[symbols_js_1.UNPATHED_ERRORS_SYMBOL];
}
exports.getUnpathedErrors = getUnpathedErrors;
const EMPTY_ARRAY = [];
const EMPTY_OBJECT = Object.create(null);
exports.getActualFieldNodes = (0, utils_1.memoize1)(function (fieldNode) {
return [fieldNode];
});
function mergeFields(mergedTypeInfo, object, sourceSubschema, context, info) {
const delegationMaps = mergedTypeInfo.delegationPlanBuilder(info.schema, sourceSubschema, info.variableValues != null && Object.keys(info.variableValues).length > 0
? info.variableValues
: EMPTY_OBJECT, info.fragments != null && Object.keys(info.fragments).length > 0
? info.fragments
: EMPTY_OBJECT, info.fieldNodes?.length
? info.fieldNodes.length === 1
? (0, exports.getActualFieldNodes)(info.fieldNodes[0])
: info.fieldNodes
: EMPTY_ARRAY);
let logFn;
if ((0, debugging_js_1.isDelegationDebugging)()) {
logFn = debugging_js_1.logFnForContext.get(context);
let delegationPlanInfos = debugging_js_1.delegationPlanInfosByContext.get(context);
if (!delegationPlanInfos) {
delegationPlanInfos = new Set();
debugging_js_1.delegationPlanInfosByContext.set(context, delegationPlanInfos);
}
const delegationPlanInfo = (0, debugging_js_1.getDelegationInfo)(context, delegationMaps, mergedTypeInfo, sourceSubschema, info);
delegationPlanInfos.add(delegationPlanInfo);
logFn?.({
status: 'PLAN',
plan: delegationPlanInfo,
});
}
const res$ = delegationMaps.reduce((prev, delegationMap) => {
function executeFn() {
return executeDelegationStage(mergedTypeInfo, delegationMap, object, context, info, logFn
? (data) => logFn({
stageId: debugging_js_1.delegationStageIdMap.get(delegationMap),
...data,
})
: undefined);
}
if ((0, utils_1.isPromise)(prev)) {
return prev.then(executeFn);
}
return executeFn();
}, undefined);
if ((0, utils_1.isPromise)(res$)) {
return res$.then(() => object);
}
return object;
}
exports.mergeFields = mergeFields;
function handleResolverResult(resolverResult, subschema, selectionSet, object, combinedFieldSubschemaMap, info, path, combinedErrors) {
if (resolverResult instanceof Error || resolverResult == null) {
const schema = subschema.transformedSchema || info.schema;
const type = schema.getType(object.__typename);
const { fields } = (0, utils_1.collectFields)(schema, EMPTY_OBJECT, EMPTY_OBJECT, type, selectionSet);
const nullResult = {};
for (const [responseKey, fieldNodes] of fields) {
const combinedPath = [...path, responseKey];
if (resolverResult instanceof graphql_1.GraphQLError) {
nullResult[responseKey] = (0, utils_1.relocatedError)(resolverResult, combinedPath);
}
else if (resolverResult instanceof Error) {
nullResult[responseKey] = (0, graphql_1.locatedError)(resolverResult, fieldNodes, combinedPath);
}
else {
nullResult[responseKey] = null;
}
}
resolverResult = nullResult;
}
else {
if (resolverResult[symbols_js_1.UNPATHED_ERRORS_SYMBOL]) {
combinedErrors.push(...resolverResult[symbols_js_1.UNPATHED_ERRORS_SYMBOL]);
}
}
const objectSubschema = resolverResult[symbols_js_1.OBJECT_SUBSCHEMA_SYMBOL];
const fieldSubschemaMap = resolverResult[symbols_js_1.FIELD_SUBSCHEMA_MAP_SYMBOL];
for (const responseKey in resolverResult) {
if (responseKey === '__proto__') {
continue;
}
const existingPropValue = object[responseKey];
const sourcePropValue = resolverResult[responseKey];
if (sourcePropValue != null || existingPropValue == null) {
if (existingPropValue != null && typeof existingPropValue === 'object') {
if (Array.isArray(existingPropValue) &&
Array.isArray(sourcePropValue) &&
existingPropValue.length === sourcePropValue.length) {
object[responseKey] = existingPropValue.map((existingElement, index) => (0, utils_1.mergeDeep)([existingElement, sourcePropValue[index]]));
}
else {
object[responseKey] = (0, utils_1.mergeDeep)([existingPropValue, sourcePropValue]);
}
}
else {
object[responseKey] = sourcePropValue;
}
}
combinedFieldSubschemaMap[responseKey] = fieldSubschemaMap?.[responseKey] ?? objectSubschema;
}
}
function executeDelegationStage(mergedTypeInfo, delegationMap, object, context, info, logFn) {
const combinedErrors = object[symbols_js_1.UNPATHED_ERRORS_SYMBOL];
const path = (0, graphql_1.responsePathAsArray)(info.path);
const combinedFieldSubschemaMap = object[symbols_js_1.FIELD_SUBSCHEMA_MAP_SYMBOL];
const jobs = [];
let delegationIndex = -1;
for (const [subschema, selectionSet] of delegationMap) {
const schema = subschema.transformedSchema || info.schema;
const type = schema.getType(object.__typename);
if (logFn) {
delegationIndex++;
logFn({
status: 'EXECUTE_DELEGATION',
subschema: subschema.name,
typeName: type.name || mergedTypeInfo.typeName,
path,
selectionSet: (0, graphql_1.print)(selectionSet),
contextId: debugging_js_1.contextIdMap.get(context),
stageId: debugging_js_1.delegationStageIdMap.get(delegationMap),
delegationIndex,
});
}
const resolver = mergedTypeInfo.resolvers.get(subschema);
if (resolver) {
try {
const resolverResult$ = resolver(object, context, info, subschema, selectionSet, undefined, type);
if ((0, utils_1.isPromise)(resolverResult$)) {
jobs.push(resolverResult$.then(resolverResult => handleResolverResult(resolverResult, subschema, selectionSet, object, combinedFieldSubschemaMap, info, path, combinedErrors)).catch(error => handleResolverResult(error, subschema, selectionSet, object, combinedFieldSubschemaMap, info, path, combinedErrors)));
}
else {
handleResolverResult(resolverResult$, subschema, selectionSet, object, combinedFieldSubschemaMap, info, path, combinedErrors);
}
}
catch (error) {
handleResolverResult(error, subschema, selectionSet, object, combinedFieldSubschemaMap, info, path, combinedErrors);
}
}
}
if (jobs.length) {
if (jobs.length === 1) {
return jobs[0];
}
return Promise.all(jobs);
}
}