UNPKG

@graphql-tools/delegate

Version:

A set of utils for faster development of GraphQL tools

175 lines (174 loc) • 8.29 kB
"use strict"; 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); } }