From dae6dc7bc07e58f73a46c37e83ae53ddf1c07cc2 Mon Sep 17 00:00:00 2001 From: Yaacov Rydzinski Date: Mon, 12 Jul 2021 18:36:41 +0300 Subject: [PATCH] consolidate Request and ExecutionParams types (#3180) * consolidate Request and ExecutionParams - also affixes rootValue to Request - when delegating with delegateToSchema, rootValue can be set multiple ways: * when using a custom executor, the custom executor can utilize a rootValue in whichever custom way it specifies. * when using the default executor (execute/subscribe from graphql-js): -- rootValue can be passed to delegateToSchema via a named option -- rootValue can be included within a subschemaConfig -- otherwise, rootValue is inferred from the originating schema - when using wrapSchema/stitchSchemas, a subschemaConfig can specify the createProxyingResolver function which can pass whatever rootValue it wants to delegateToSchema as above. * add changeset * test --- .changeset/fresh-queens-watch.md | 24 ++++++++ .../tests/withTransforms.test.ts | 2 +- .../src/createBatchingExecutor.ts | 43 +++++++------- .../batch-execute/src/getBatchingExecutor.ts | 6 +- ...rgeExecutionParams.ts => mergeRequests.ts} | 34 +++++------ packages/delegate/src/createRequest.ts | 4 ++ packages/delegate/src/delegateToSchema.ts | 57 ++++++++----------- packages/delegate/src/index.ts | 2 +- .../src/transforms/AddArgumentsAsVariables.ts | 2 +- .../delegate/src/transforms/FilterToSchema.ts | 18 +++--- packages/delegate/src/types.ts | 17 +++--- .../delegate/tests/batchExecution.test.ts | 10 ++-- packages/links/src/linkToExecutor.ts | 6 +- packages/loaders/url/src/index.ts | 6 +- packages/stitch/tests/fixtures/schemas.ts | 4 +- packages/utils/src/Interfaces.ts | 24 +++++--- packages/utils/src/executor.ts | 24 ++------ packages/wrap/src/transforms/MapLeafValues.ts | 2 +- .../transforms/TransformInputObjectFields.ts | 2 +- packages/wrap/tests/fixtures/schemas.ts | 4 +- packages/wrap/tests/requests.test.ts | 1 + website/docs/remote-schemas.md | 5 +- website/docs/stitch-combining-schemas.md | 4 +- website/docs/stitch-type-merging.md | 2 +- 24 files changed, 156 insertions(+), 147 deletions(-) create mode 100644 .changeset/fresh-queens-watch.md rename packages/batch-execute/src/{mergeExecutionParams.ts => mergeRequests.ts} (88%) diff --git a/.changeset/fresh-queens-watch.md b/.changeset/fresh-queens-watch.md new file mode 100644 index 0000000000..baa9a52de8 --- /dev/null +++ b/.changeset/fresh-queens-watch.md @@ -0,0 +1,24 @@ +--- +'@graphql-tools/batch-delegate': major +'@graphql-tools/batch-execute': major +'@graphql-tools/delegate': major +'@graphql-tools/links': major +'@graphql-tools/url-loader': major +'@graphql-tools/stitch': major +'@graphql-tools/utils': major +'@graphql-tools/wrap': major +--- + +refactor: ExecutionParams type replaced by Request type + +rootValue property is now a part of the Request type. + +When delegating with delegateToSchema, rootValue can be set multiple ways: + +- when using a custom executor, the custom executor can utilize a rootValue in whichever custom way it specifies. +- when using the default executor (execute/subscribe from graphql-js): + -- rootValue can be passed to delegateToSchema via a named option + -- rootValue can be included within a subschemaConfig + -- otherwise, rootValue is inferred from the originating schema + +When using wrapSchema/stitchSchemas, a subschemaConfig can specify the createProxyingResolver function which can pass whatever rootValue it wants to delegateToSchema as above. diff --git a/packages/batch-delegate/tests/withTransforms.test.ts b/packages/batch-delegate/tests/withTransforms.test.ts index 66f64086a8..844e9c2946 100644 --- a/packages/batch-delegate/tests/withTransforms.test.ts +++ b/packages/batch-delegate/tests/withTransforms.test.ts @@ -73,7 +73,7 @@ describe('works with complex transforms', () => { ] }), resultTransformer: (results, delegationContext) => { - const userIds = delegationContext.args['userIds']; + const userIds = delegationContext.args?.['userIds']; const booksByUserIds = results.reduce( (acc: any, { userId, books }: { userId: string, books: any[] }) => { acc[userId] = books diff --git a/packages/batch-execute/src/createBatchingExecutor.ts b/packages/batch-execute/src/createBatchingExecutor.ts index 5f1bd30687..98c5e859cd 100644 --- a/packages/batch-execute/src/createBatchingExecutor.ts +++ b/packages/batch-execute/src/createBatchingExecutor.ts @@ -4,9 +4,9 @@ import DataLoader from 'dataloader'; import { ValueOrPromise } from 'value-or-promise'; -import { ExecutionParams, Executor, ExecutionResult } from '@graphql-tools/utils'; +import { Request, Executor, ExecutionResult } from '@graphql-tools/utils'; -import { mergeExecutionParams } from './mergeExecutionParams'; +import { mergeRequests } from './mergeRequests'; import { splitResult } from './splitResult'; export function createBatchingExecutor( @@ -14,49 +14,47 @@ export function createBatchingExecutor( dataLoaderOptions?: DataLoader.Options, extensionsReducer: ( mergedExtensions: Record, - executionParams: ExecutionParams + request: Request ) => Record = defaultExtensionsReducer ): Executor { const loader = new DataLoader(createLoadFn(executor, extensionsReducer), dataLoaderOptions); - return (executionParams: ExecutionParams) => - executionParams.info?.operation.operation === 'subscription' - ? executor(executionParams) - : loader.load(executionParams); + return (request: Request) => + request.info?.operation.operation === 'subscription' ? executor(request) : loader.load(request); } function createLoadFn( executor: Executor, - extensionsReducer: (mergedExtensions: Record, executionParams: ExecutionParams) => Record + extensionsReducer: (mergedExtensions: Record, request: Request) => Record ) { - return async (execs: ReadonlyArray): Promise> => { - const execBatches: Array> = []; + return async (requests: ReadonlyArray): Promise> => { + const execBatches: Array> = []; let index = 0; - const exec = execs[index]; - let currentBatch: Array = [exec]; + const request = requests[index]; + let currentBatch: Array = [request]; execBatches.push(currentBatch); - const operationType = getOperationAST(exec.document, undefined)?.operation; + const operationType = getOperationAST(request.document, undefined)?.operation; if (operationType == null) { throw new Error('Could not identify operation type of document.'); } - while (++index < execs.length) { - const currentOperationType = getOperationAST(execs[index].document, undefined)?.operation; + while (++index < requests.length) { + const currentOperationType = getOperationAST(requests[index].document, undefined)?.operation; if (operationType == null) { throw new Error('Could not identify operation type of document.'); } if (operationType === currentOperationType) { - currentBatch.push(execs[index]); + currentBatch.push(requests[index]); } else { - currentBatch = [execs[index]]; + currentBatch = [requests[index]]; execBatches.push(currentBatch); } } const executionResults: Array> = execBatches.map(execBatch => { - const mergedExecutionParams = mergeExecutionParams(execBatch, extensionsReducer); - return new ValueOrPromise(() => executor(mergedExecutionParams) as ExecutionResult); + const mergedRequests = mergeRequests(execBatch, extensionsReducer); + return new ValueOrPromise(() => executor(mergedRequests) as ExecutionResult); }); return ValueOrPromise.all(executionResults) @@ -70,11 +68,8 @@ function createLoadFn( }; } -function defaultExtensionsReducer( - mergedExtensions: Record, - executionParams: ExecutionParams -): Record { - const newExtensions = executionParams.extensions; +function defaultExtensionsReducer(mergedExtensions: Record, request: Request): Record { + const newExtensions = request.extensions; if (newExtensions != null) { Object.assign(mergedExtensions, newExtensions); } diff --git a/packages/batch-execute/src/getBatchingExecutor.ts b/packages/batch-execute/src/getBatchingExecutor.ts index 6d5e5de32c..e315ee3b1a 100644 --- a/packages/batch-execute/src/getBatchingExecutor.ts +++ b/packages/batch-execute/src/getBatchingExecutor.ts @@ -1,6 +1,6 @@ import DataLoader from 'dataloader'; -import { ExecutionParams, Executor } from '@graphql-tools/utils'; +import { Request, Executor } from '@graphql-tools/utils'; import { createBatchingExecutor } from './createBatchingExecutor'; import { memoize2of4 } from './memoize'; @@ -8,9 +8,7 @@ export const getBatchingExecutor = memoize2of4(function ( _context: Record, executor: Executor, dataLoaderOptions?: DataLoader.Options | undefined, - extensionsReducer?: - | undefined - | ((mergedExtensions: Record, executionParams: ExecutionParams) => Record) + extensionsReducer?: undefined | ((mergedExtensions: Record, request: Request) => Record) ): Executor { return createBatchingExecutor(executor, dataLoaderOptions, extensionsReducer); }); diff --git a/packages/batch-execute/src/mergeExecutionParams.ts b/packages/batch-execute/src/mergeRequests.ts similarity index 88% rename from packages/batch-execute/src/mergeExecutionParams.ts rename to packages/batch-execute/src/mergeRequests.ts index 80e2fce52f..6c14b16899 100644 --- a/packages/batch-execute/src/mergeExecutionParams.ts +++ b/packages/batch-execute/src/mergeRequests.ts @@ -18,7 +18,7 @@ import { OperationTypeNode, } from 'graphql'; -import { ExecutionParams, Maybe } from '@graphql-tools/utils'; +import { Request, Maybe } from '@graphql-tools/utils'; import { createPrefix } from './prefix'; @@ -56,10 +56,10 @@ import { createPrefix } from './prefix'; * } * } */ -export function mergeExecutionParams( - execs: Array, - extensionsReducer: (mergedExtensions: Record, executionParams: ExecutionParams) => Record -): ExecutionParams { +export function mergeRequests( + requests: Array, + extensionsReducer: (mergedExtensions: Record, request: Request) => Record +): Request { const mergedVariables: Record = Object.create(null); const mergedVariableDefinitions: Array = []; const mergedSelections: Array = []; @@ -68,11 +68,11 @@ export function mergeExecutionParams( let operation: Maybe; - for (const index in execs) { - const executionParams = execs[index]; - const prefixedExecutionParams = prefixExecutionParams(createPrefix(index), executionParams); + for (const index in requests) { + const request = requests[index]; + const prefixedRequests = prefixRequest(createPrefix(index), request); - for (const def of prefixedExecutionParams.document.definitions) { + for (const def of prefixedRequests.document.definitions) { if (isOperationDefinition(def)) { operation = def.operation; mergedSelections.push(...def.selectionSet.selections); @@ -84,8 +84,8 @@ export function mergeExecutionParams( mergedFragmentDefinitions.push(def); } } - Object.assign(mergedVariables, prefixedExecutionParams.variables); - mergedExtensions = extensionsReducer(mergedExtensions, executionParams); + Object.assign(mergedVariables, prefixedRequests.variables); + mergedExtensions = extensionsReducer(mergedExtensions, request); } if (operation == null) { @@ -109,18 +109,18 @@ export function mergeExecutionParams( }, variables: mergedVariables, extensions: mergedExtensions, - context: execs[0].context, - info: execs[0].info, + context: requests[0].context, + info: requests[0].info, }; } -function prefixExecutionParams(prefix: string, executionParams: ExecutionParams): ExecutionParams { - let document = aliasTopLevelFields(prefix, executionParams.document); - const executionVariables = executionParams.variables ?? {}; +function prefixRequest(prefix: string, request: Request): Request { + let document = aliasTopLevelFields(prefix, request.document); + const executionVariables = request.variables ?? {}; const variableNames = Object.keys(executionVariables); if (variableNames.length === 0) { - return { ...executionParams, document }; + return { ...request, document }; } document = visit(document, { diff --git a/packages/delegate/src/createRequest.ts b/packages/delegate/src/createRequest.ts index b27546f55c..4a298a073e 100644 --- a/packages/delegate/src/createRequest.ts +++ b/packages/delegate/src/createRequest.ts @@ -31,6 +31,7 @@ export function getDelegatingOperation(parentType: GraphQLObjectType, schema: Gr export function createRequestFromInfo({ info, + rootValue, operationName, operation = getDelegatingOperation(info.parentType, info.schema), fieldName = info.fieldName, @@ -44,6 +45,7 @@ export function createRequestFromInfo({ fragments: info.fragments, variableDefinitions: info.operation.variableDefinitions, variableValues: info.variableValues, + targetRootValue: rootValue, targetOperationName: operationName, targetOperation: operation, targetFieldName: fieldName, @@ -59,6 +61,7 @@ export function createRequest({ fragments, variableDefinitions, variableValues, + targetRootValue, targetOperationName, targetOperation, targetFieldName, @@ -171,6 +174,7 @@ export function createRequest({ return { document, variables: newVariables, + rootValue: targetRootValue, operationName: targetOperationName, }; } diff --git a/packages/delegate/src/delegateToSchema.ts b/packages/delegate/src/delegateToSchema.ts index ada9b9cf13..60ce50c1c0 100644 --- a/packages/delegate/src/delegateToSchema.ts +++ b/packages/delegate/src/delegateToSchema.ts @@ -18,7 +18,7 @@ import { getBatchingExecutor } from '@graphql-tools/batch-execute'; import { mapAsyncIterator, Executor, - ExecutionParams, + Request, Maybe, AggregateError, isAsyncIterable, @@ -43,10 +43,11 @@ export function delegateToSchema, TArgs = any>( ): any { const { info, + schema, + rootValue, operationName, operation = getDelegatingOperation(info.parentType, info.schema), fieldName = info.fieldName, - returnType = info.returnType, selectionSet, fieldNodes, } = options; @@ -57,15 +58,13 @@ export function delegateToSchema, TArgs = any>( fieldName, selectionSet, fieldNodes, + rootValue: rootValue ?? (schema as SubschemaConfig).rootValue, operationName, }); return delegateRequest({ ...options, request, - operation, - fieldName, - returnType, }); } @@ -75,7 +74,6 @@ function getDelegationReturnType( fieldName: string ): GraphQLOutputType { const rootType = getDefinedRootType(targetSchema, operation); - return rootType.getFields()[fieldName].type; } @@ -115,27 +113,22 @@ export function delegateRequest, TArgs = any>( .resolve(); } -const emptyObject = {}; - function getDelegationContext({ request, schema, operation, fieldName, returnType, - args = {}, + args, context, info, - rootValue = emptyObject, transforms = [], transformedSchema, skipTypeMerging = false, - operationName, }: IDelegateRequestOptions): DelegationContext { let operationDefinition: Maybe; let targetOperation: Maybe; let targetFieldName: string; - let targetOperationName: string | undefined; if (operation == null) { operationDefinition = getOperationAST(request.document, request.operationName); @@ -154,16 +147,6 @@ function getDelegationContext({ targetFieldName = fieldName; } - if (operationName == null) { - if (request.operationName) { - targetOperationName = request.operationName; - } else if (operationDefinition?.name?.value) { - targetOperationName = operationDefinition.name.value; - } - } else { - targetOperationName = operationName; - } - const stitchingInfo: Maybe> = info?.schema.extensions?.['stitchingInfo']; const subschemaOrSubschemaConfig: GraphQLSchema | SubschemaConfig = @@ -176,12 +159,10 @@ function getDelegationContext({ subschemaConfig: subschemaOrSubschemaConfig, targetSchema, operation: targetOperation, - operationName: targetOperationName, fieldName: targetFieldName, args, context, info, - rootValue: rootValue ?? emptyObject, returnType: returnType ?? info?.returnType ?? getDelegationReturnType(targetSchema, targetOperation, targetFieldName), transforms: @@ -204,7 +185,6 @@ function getDelegationContext({ args, context, info, - rootValue: rootValue, returnType: returnType ?? info?.returnType ?? @@ -245,17 +225,26 @@ function getExecutor(delegationContext: DelegationContext): return executor; } -const createDefaultExecutor = (schema: GraphQLSchema, operation: OperationTypeNode) => - (({ document, context, variables, rootValue }: ExecutionParams) => { - const executionParams = { +function createDefaultExecutor(schema: GraphQLSchema, operation: OperationTypeNode): Executor { + if (operation === 'subscription') { + return (({ document, context, variables, rootValue, operationName }: Request) => + subscribe({ + schema, + document, + contextValue: context, + variableValues: variables, + rootValue, + operationName, + })) as Executor; + } + + return (({ document, context, variables, rootValue, operationName }: Request) => + execute({ schema, document, contextValue: context, variableValues: variables, rootValue, - }; - if (operation === 'subscription') { - return subscribe(executionParams); - } - return execute(executionParams); - }) as Executor; + operationName, + })) as Executor; +} diff --git a/packages/delegate/src/index.ts b/packages/delegate/src/index.ts index 030fed028b..68f0066911 100644 --- a/packages/delegate/src/index.ts +++ b/packages/delegate/src/index.ts @@ -9,4 +9,4 @@ export * from './resolveExternalValue'; export * from './subschemaConfig'; export * from './transforms'; export * from './types'; -export { Executor, AsyncExecutor, SyncExecutor, ExecutionParams } from '@graphql-tools/utils'; +export { Executor, AsyncExecutor, SyncExecutor, Request } from '@graphql-tools/utils'; diff --git a/packages/delegate/src/transforms/AddArgumentsAsVariables.ts b/packages/delegate/src/transforms/AddArgumentsAsVariables.ts index 398a3131f5..dfc77be839 100644 --- a/packages/delegate/src/transforms/AddArgumentsAsVariables.ts +++ b/packages/delegate/src/transforms/AddArgumentsAsVariables.ts @@ -51,7 +51,7 @@ function addVariablesToRootField( variables: Record; } { const document = originalRequest.document; - const variableValues = originalRequest.variables; + const variableValues = originalRequest.variables ?? {}; const operations: Array = document.definitions.filter( def => def.kind === Kind.OPERATION_DEFINITION diff --git a/packages/delegate/src/transforms/FilterToSchema.ts b/packages/delegate/src/transforms/FilterToSchema.ts index 8dd738e37e..bb1f6767c5 100644 --- a/packages/delegate/src/transforms/FilterToSchema.ts +++ b/packages/delegate/src/transforms/FilterToSchema.ts @@ -41,7 +41,7 @@ export default class FilterToSchema implements Transform { function filterToSchema( targetSchema: GraphQLSchema, document: DocumentNode, - variables: Record + variables?: Record ): { document: DocumentNode; variables: Record } { const operations: Array = document.definitions.filter( def => def.kind === Kind.OPERATION_DEFINITION @@ -105,13 +105,15 @@ function filterToSchema( }); } - const newVariables = usedVariables.reduce((acc, variableName) => { - const variableValue = variables[variableName]; - if (variableValue !== undefined) { - acc[variableName] = variableValue; - } - return acc; - }, {}); + const newVariables = variables + ? usedVariables.reduce((acc, variableName) => { + const variableValue = variables[variableName]; + if (variableValue !== undefined) { + acc[variableName] = variableValue; + } + return acc; + }, {}) + : {}; return { document: { diff --git a/packages/delegate/src/types.ts b/packages/delegate/src/types.ts index 1e12ddd959..17c101a03c 100644 --- a/packages/delegate/src/types.ts +++ b/packages/delegate/src/types.ts @@ -14,7 +14,7 @@ import { import DataLoader from 'dataloader'; -import { ExecutionParams, ExecutionResult, Executor, Request, TypeMap } from '@graphql-tools/utils'; +import { Request, ExecutionResult, Executor, TypeMap } from '@graphql-tools/utils'; import { Subschema } from './Subschema'; import { OBJECT_SUBSCHEMA_SYMBOL, FIELD_SUBSCHEMA_MAP_SYMBOL, UNPATHED_ERRORS_SYMBOL } from './symbols'; @@ -47,12 +47,12 @@ export interface DelegationContext> { targetSchema: GraphQLSchema; operation: OperationTypeNode; fieldName: string; - args: Record; + args?: Record; context?: TContext; info: GraphQLResolveInfo; - rootValue?: Record; returnType: GraphQLOutputType; onLocatedError?: (originalError: GraphQLError) => GraphQLError; + rootValue?: any; transforms: Array>; transformedSchema: GraphQLSchema; skipTypeMerging: boolean; @@ -75,7 +75,7 @@ export interface IDelegateToSchemaOptions, TArgs fieldNodes?: ReadonlyArray; context?: TContext; info: GraphQLResolveInfo; - rootValue?: Record; + rootValue?: any; transforms?: Array>; transformedSchema?: GraphQLSchema; validateRequest?: boolean; @@ -90,9 +90,10 @@ export interface IDelegateRequestOptions, TArgs = export interface ICreateRequestFromInfo { info: GraphQLResolveInfo; + rootValue?: any; operationName?: string; - operation: OperationTypeNode; - fieldName: string; + operation?: OperationTypeNode; + fieldName?: string; selectionSet?: SelectionSetNode; fieldNodes?: ReadonlyArray; } @@ -105,6 +106,7 @@ export interface ICreateRequest { variableDefinitions?: ReadonlyArray; variableValues?: Record; targetOperation: OperationTypeNode; + targetRootValue?: any; targetOperationName?: string; targetFieldName: string; selectionSet?: SelectionSetNode; @@ -135,13 +137,14 @@ export type CreateProxyingResolverFn> = ( ) => GraphQLFieldResolver; export interface BatchingOptions { - extensionsReducer?: (mergedExtensions: Record, executionParams: ExecutionParams) => Record; + extensionsReducer?: (mergedExtensions: Record, request: Request) => Record; dataLoaderOptions?: DataLoader.Options; } export interface SubschemaConfig> { schema: GraphQLSchema; createProxyingResolver?: CreateProxyingResolverFn; + rootValue?: any; transforms?: Array>; merge?: Record>; executor?: Executor; diff --git a/packages/delegate/tests/batchExecution.test.ts b/packages/delegate/tests/batchExecution.test.ts index 59119b8ff8..93c82b8a0a 100644 --- a/packages/delegate/tests/batchExecution.test.ts +++ b/packages/delegate/tests/batchExecution.test.ts @@ -1,7 +1,7 @@ import { graphql, execute, ExecutionResult } from 'graphql'; import { makeExecutableSchema } from '@graphql-tools/schema'; -import { delegateToSchema, SubschemaConfig, ExecutionParams, SyncExecutor, Executor } from '../src'; +import { delegateToSchema, SubschemaConfig, Request, SyncExecutor, Executor } from '../src'; import { stitchSchemas } from '@graphql-tools/stitch'; import { FilterObjectFields } from '@graphql-tools/wrap'; @@ -27,9 +27,9 @@ describe('batch execution', () => { const innerSubschemaConfig: SubschemaConfig = { schema: innerSchema, batch: true, - executor: ((params: ExecutionParams): ExecutionResult => { + executor: ((request: Request): ExecutionResult => { executions++; - return execute(innerSchema, params.document, undefined, params.context, params.variables) as ExecutionResult; + return execute(innerSchema, request.document, undefined, request.context, request.variables) as ExecutionResult; }) as SyncExecutor } @@ -104,9 +104,9 @@ describe('batch execution', () => { let executions = 0; - const executor = ((params: ExecutionParams): ExecutionResult => { + const executor = ((request: Request): ExecutionResult => { executions++; - return execute(innerSchemaA, params.document, undefined, params.context, params.variables) as ExecutionResult; + return execute(innerSchemaA, request.document, undefined, request.context, request.variables) as ExecutionResult; }) as Executor; const innerSubschemaConfigA: Array = [{ diff --git a/packages/links/src/linkToExecutor.ts b/packages/links/src/linkToExecutor.ts index 1f8e579da6..685cd37612 100644 --- a/packages/links/src/linkToExecutor.ts +++ b/packages/links/src/linkToExecutor.ts @@ -2,12 +2,12 @@ import { toPromise } from '@apollo/client/core'; import { ApolloLink, execute } from '@apollo/client/link/core'; import { Observable } from '@apollo/client/utilities'; -import { Executor, ExecutionParams, ExecutionResult, observableToAsyncIterable } from '@graphql-tools/utils'; +import { Executor, Request, ExecutionResult, observableToAsyncIterable } from '@graphql-tools/utils'; export const linkToExecutor = (link: ApolloLink): Executor => - async (params: ExecutionParams) => { - const { document, variables, extensions, context, info, operationName } = params; + async (request: Request) => { + const { document, variables, extensions, context, info, operationName } = request; const observable = execute(link, { query: document, variables, diff --git a/packages/loaders/url/src/index.ts b/packages/loaders/url/src/index.ts index e86c1fb44d..413cc40374 100644 --- a/packages/loaders/url/src/index.ts +++ b/packages/loaders/url/src/index.ts @@ -11,7 +11,7 @@ import { BaseLoaderOptions, observableToAsyncIterable, isAsyncIterable, - ExecutionParams, + Request, mapAsyncIterator, withCancel, parseGraphQLSDL, @@ -295,7 +295,7 @@ export class UrlLoader implements Loader { variables, operationName, extensions, - }: ExecutionParams) => { + }: Request) => { const controller = new AbortController(); let method = defaultMethod; if (options?.useGETForQueries) { @@ -463,7 +463,7 @@ export class UrlLoader implements Loader { webSocketImpl ); - return async ({ document, variables, operationName }: ExecutionParams) => { + return async ({ document, variables, operationName }: Request) => { return observableToAsyncIterable( subscriptionClient.request({ query: document, diff --git a/packages/stitch/tests/fixtures/schemas.ts b/packages/stitch/tests/fixtures/schemas.ts index bab2fb0b1b..9364d9747d 100644 --- a/packages/stitch/tests/fixtures/schemas.ts +++ b/packages/stitch/tests/fixtures/schemas.ts @@ -25,7 +25,7 @@ import { } from '@graphql-tools/utils'; import { makeExecutableSchema } from '@graphql-tools/schema'; -import { ExecutionParams, SubschemaConfig } from '@graphql-tools/delegate'; +import { Request, SubschemaConfig } from '@graphql-tools/delegate'; export class CustomError extends GraphQLError { constructor(message: string, extensions: Record) { @@ -682,7 +682,7 @@ export const subscriptionSchema: GraphQLSchema = makeExecutableSchema({ }); function makeExecutorFromSchema(schema: GraphQLSchema) { - return async ({ document, variables, context, info }: ExecutionParams) => { + return async ({ document, variables, context, info }: Request) => { if (info?.operation.operation === 'subscription') { const result = subscribe( schema, diff --git a/packages/utils/src/Interfaces.ts b/packages/utils/src/Interfaces.ts index 23190a3c41..22cc148dc3 100644 --- a/packages/utils/src/Interfaces.ts +++ b/packages/utils/src/Interfaces.ts @@ -58,6 +58,23 @@ export interface ExecutionResult> extends GraphQLExe extensions?: Record; } +export interface Request< + TArgs extends Record = Record, + TContext = any, + TRootValue = any, + TExtensions = Record +> { + document: DocumentNode; + variables?: TArgs; + extensions?: TExtensions; + operationName?: string; + // If the request will be executed locally, it may contain a rootValue + rootValue?: TRootValue; + // If the request originates within execution of a parent request, it may contain the parent context and info + context?: TContext; + info?: GraphQLResolveInfo; +} + // graphql-js non-exported typings export type TypeMap = Record; @@ -309,13 +326,6 @@ export type IDefaultValueIteratorFn = (type: GraphQLInputType, value: any) => vo export type NextResolverFn = () => Promise; -export interface Request { - document: DocumentNode; - variables: Record; - operationName?: string; - extensions?: Record; -} - export type VisitableSchemaType = | GraphQLSchema | GraphQLObjectType diff --git a/packages/utils/src/executor.ts b/packages/utils/src/executor.ts index ac89baeb32..45fd4e1874 100644 --- a/packages/utils/src/executor.ts +++ b/packages/utils/src/executor.ts @@ -1,24 +1,8 @@ -import { DocumentNode, GraphQLResolveInfo } from 'graphql'; -import { ExecutionResult } from './Interfaces'; +import { ExecutionResult, Request } from './Interfaces'; type MaybePromise = Promise | T; type MaybeAsyncIterableIterator = AsyncIterableIterator | T; -export interface ExecutionParams< - TArgs extends Record = Record, - TContext = any, - TRootValue = any, - TExtensions = Record -> { - document: DocumentNode; - variables?: TArgs; - extensions?: TExtensions; - context?: TContext; - info?: GraphQLResolveInfo; - rootValue?: TRootValue; - operationName?: string; -} - export type AsyncExecutor, TBaseExtensions = Record> = < TReturn = any, TArgs = Record, @@ -26,7 +10,7 @@ export type AsyncExecutor, TBaseExtensions = TRoot = any, TExtensions extends TBaseExtensions = TBaseExtensions >( - params: ExecutionParams + request: Request ) => Promise>>; export type SyncExecutor, TBaseExtensions = Record> = < @@ -36,7 +20,7 @@ export type SyncExecutor, TBaseExtensions = R TRoot = any, TExtensions extends TBaseExtensions = TBaseExtensions >( - params: ExecutionParams + request: Request ) => ExecutionResult; export type Executor, TBaseExtensions = Record> = < @@ -46,5 +30,5 @@ export type Executor, TBaseExtensions = Recor TRoot = any, TExtensions extends TBaseExtensions = TBaseExtensions >( - params: ExecutionParams + request: Request ) => MaybePromise>>; diff --git a/packages/wrap/src/transforms/MapLeafValues.ts b/packages/wrap/src/transforms/MapLeafValues.ts index 82da267e02..a8d7d6bc23 100644 --- a/packages/wrap/src/transforms/MapLeafValues.ts +++ b/packages/wrap/src/transforms/MapLeafValues.ts @@ -88,7 +88,7 @@ export default class MapLeafValues implements Transform = document.definitions.filter( def => def.kind === Kind.OPERATION_DEFINITION diff --git a/packages/wrap/src/transforms/TransformInputObjectFields.ts b/packages/wrap/src/transforms/TransformInputObjectFields.ts index 0c0bf526c8..0971306579 100644 --- a/packages/wrap/src/transforms/TransformInputObjectFields.ts +++ b/packages/wrap/src/transforms/TransformInputObjectFields.ts @@ -78,7 +78,7 @@ export default class TransformInputObjectFields implements Transform { delegationContext: DelegationContext, _transformationContext: Record ): Request { - const variableValues = originalRequest.variables; + const variableValues = originalRequest.variables ?? {}; const fragments = Object.create(null); const operations: Array = []; diff --git a/packages/wrap/tests/fixtures/schemas.ts b/packages/wrap/tests/fixtures/schemas.ts index 22edcc4f7f..172ea0debf 100644 --- a/packages/wrap/tests/fixtures/schemas.ts +++ b/packages/wrap/tests/fixtures/schemas.ts @@ -22,7 +22,7 @@ import { isAsyncIterable, } from '@graphql-tools/utils'; import { makeExecutableSchema } from '@graphql-tools/schema'; -import { SubschemaConfig, ExecutionParams } from '@graphql-tools/delegate'; +import { SubschemaConfig, Request } from '@graphql-tools/delegate'; export class CustomError extends GraphQLError { constructor(message: string, extensions: Record) { @@ -678,7 +678,7 @@ export const subscriptionSchema: GraphQLSchema = makeExecutableSchema({ }); function makeExecutorFromSchema(schema: GraphQLSchema) { - return async ({ document, variables, context, info }: ExecutionParams) => { + return async ({ document, variables, context, info }: Request) => { if (info?.operation.operation === 'subscription') { const result = await subscribe( schema, diff --git a/packages/wrap/tests/requests.test.ts b/packages/wrap/tests/requests.test.ts index a4f308c211..3a87fbb520 100644 --- a/packages/wrap/tests/requests.test.ts +++ b/packages/wrap/tests/requests.test.ts @@ -48,6 +48,7 @@ describe('requests', () => { } } `), + rootValue: undefined, variables: {}, operationName: 'test' }); diff --git a/website/docs/remote-schemas.md b/website/docs/remote-schemas.md index a9fbbad968..4b5967e894 100644 --- a/website/docs/remote-schemas.md +++ b/website/docs/remote-schemas.md @@ -27,9 +27,9 @@ You can use an executor with an HTTP Client implementation (like cross-fetch). A We've chosen to split this functionality up to give you the flexibility to choose when to do the introspection step. For example, you might already have the remote schema information, allowing you to skip the `introspectSchema` step entirely. Here's a complete example: ```js -type Executor = (operation: ExecutionParams) => Promise; +type Executor = (request: Request) => Promise; -type ExecutionParams = { +type Request = { document: DocumentNode, variables?: Object, context?: Object, @@ -213,7 +213,6 @@ import { delegateToSchema } from '@graphql-tools/delegate'; export function defaultCreateProxyingResolver({ subschemaConfig, operation, - transforms, transformedSchema, }: ICreateProxyingResolverOptions): GraphQLFieldResolver { return (_parent, _args, context, info) => diff --git a/website/docs/stitch-combining-schemas.md b/website/docs/stitch-combining-schemas.md index 648fb96bfc..f34cb5168a 100644 --- a/website/docs/stitch-combining-schemas.md +++ b/website/docs/stitch-combining-schemas.md @@ -86,16 +86,16 @@ In the example above, the extra "subschema" wrapper objects may look verbose at ```js export interface SubschemaConfig { schema: GraphQLSchema; - rootValue?: Record; executor?: Executor; createProxyingResolver?: CreateProxyingResolverFn; + rootValue?: Record; transforms?: Array; merge?: Record; batch?: boolean; batchingOptions?: { extensionsReducer?: ( mergedExtensions: Record, - executionParams: ExecutionParams + request: Request ) => Record, dataLoaderOptions?: DataLoader.Options, }; diff --git a/website/docs/stitch-type-merging.md b/website/docs/stitch-type-merging.md index 9602a10f47..f6a08c9742 100644 --- a/website/docs/stitch-type-merging.md +++ b/website/docs/stitch-type-merging.md @@ -253,7 +253,7 @@ Query batching will collect all queries made during an execution cycle and combi ```ts batchingOptions?: { dataLoaderOptions?: DataLoader.Options; - extensionsReducer?: (mergedExtensions: Record, executionParams: ExecutionParams) => Record; + extensionsReducer?: (mergedExtensions: Record, request: Request) => Record; } ```