UNPKG

@graphql-mesh/runtime

Version:
106 lines (105 loc) 4.68 kB
import { BREAK, execute, visit } from 'graphql'; import { mapAsyncIterator } from '@envelop/core'; import { applyRequestTransforms, applyResultTransforms } from '@graphql-mesh/utils'; import { createBatchingExecutor } from '@graphql-tools/batch-execute'; import { applySchemaTransforms, createDefaultExecutor, } from '@graphql-tools/delegate'; import { getDefinedRootType, getOperationASTFromRequest, isAsyncIterable, printSchemaWithDirectives, } from '@graphql-tools/utils'; var IntrospectionQueryType; (function (IntrospectionQueryType) { IntrospectionQueryType["FEDERATION"] = "FEDERATION"; IntrospectionQueryType["REGULAR"] = "REGULAR"; })(IntrospectionQueryType || (IntrospectionQueryType = {})); function getIntrospectionOperationType(operationAST) { let introspectionQueryType = null; visit(operationAST, { Field: (node) => { if (node.name.value === '__schema' || node.name.value === '__type') { introspectionQueryType = IntrospectionQueryType.REGULAR; return BREAK; } if (node.name.value === '_service') { introspectionQueryType = IntrospectionQueryType.FEDERATION; return BREAK; } }, }); return introspectionQueryType; } function getExecuteFn(subschema) { return async function subschemaExecute(args) { const originalRequest = { document: args.document, variables: args.variableValues, operationName: args.operationName ?? undefined, rootValue: args.rootValue, context: args.contextValue, }; const operationAST = getOperationASTFromRequest(originalRequest); // TODO: We need more elegant solution const introspectionQueryType = getIntrospectionOperationType(operationAST); if (introspectionQueryType === IntrospectionQueryType.FEDERATION) { const executionResult = { data: { _service: { sdl: printSchemaWithDirectives(args.schema), }, }, }; return executionResult; } else if (introspectionQueryType === IntrospectionQueryType.REGULAR) { return execute(args); } const delegationContext = { subschema, subschemaConfig: subschema, targetSchema: args.schema, operation: operationAST.operation, fieldName: '', context: args.contextValue, rootValue: args.rootValue, transforms: subschema.transforms, transformedSchema: subschema.transformedSchema, skipTypeMerging: true, returnType: getDefinedRootType(args.schema, operationAST.operation), }; let executor = subschema.executor; if (executor == null) { executor = createDefaultExecutor(subschema.schema); } if (subschema.batch) { executor = createBatchingExecutor(executor); } const transformationContext = {}; const transformedRequest = applyRequestTransforms(originalRequest, delegationContext, transformationContext, subschema.transforms); const originalResult = await executor(transformedRequest); if (isAsyncIterable(originalResult)) { return mapAsyncIterator(originalResult, singleResult => applyResultTransforms(singleResult, delegationContext, transformationContext, subschema.transforms)); } const transformedResult = applyResultTransforms(originalResult, delegationContext, transformationContext, subschema.transforms); return transformedResult; }; } // Creates an envelop plugin to execute a subschema inside Envelop export function useSubschema(subschema) { const executeFn = getExecuteFn(subschema); const plugin = { onPluginInit({ setSchema }) { // To prevent unwanted warnings from stitching if (!('_transformedSchema' in subschema)) { subschema.transformedSchema = applySchemaTransforms(subschema.schema, subschema); } subschema.transformedSchema.extensions = subschema.transformedSchema.extensions || subschema.schema.extensions || {}; Object.assign(subschema.transformedSchema.extensions, subschema.schema.extensions); setSchema(subschema.transformedSchema); }, onExecute({ setExecuteFn }) { setExecuteFn(executeFn); }, onSubscribe({ setSubscribeFn }) { setSubscribeFn(executeFn); }, }; return plugin; }