UNPKG

@graphql-tools/delegate

Version:

A set of utils for faster development of GraphQL tools

151 lines (150 loc) • 7.33 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.handleResolverResult = 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 leftOver_js_1 = require("./leftOver.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); const leftOver = leftOver_js_1.leftOverByDelegationPlan.get(delegationMaps); if (leftOver) { object[leftOver_js_1.PLAN_LEFT_OVER] = leftOver; } const res$ = delegationMaps.reduce((prev, delegationMap) => { function executeFn() { return executeDelegationStage(mergedTypeInfo, delegationMap, object, context, info); } 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' && Object.keys(existingPropValue).length > 0) { if (Array.isArray(existingPropValue) && Array.isArray(sourcePropValue) && existingPropValue.length === sourcePropValue.length) { object[responseKey] = existingPropValue.map((existingElement, index) => sourcePropValue instanceof Error ? existingElement : (0, utils_1.mergeDeep)([existingElement, sourcePropValue[index]])); } else if (!(sourcePropValue instanceof Error)) { object[responseKey] = (0, utils_1.mergeDeep)([existingPropValue, sourcePropValue]); } } else { object[responseKey] = sourcePropValue; } } combinedFieldSubschemaMap[responseKey] = fieldSubschemaMap?.[responseKey] ?? objectSubschema ?? subschema; } } exports.handleResolverResult = handleResolverResult; function executeDelegationStage(mergedTypeInfo, delegationMap, object, context, info) { 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 = []; for (const [subschema, selectionSet] of delegationMap) { const schema = subschema.transformedSchema || info.schema; const type = schema.getType(object.__typename); 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); } }