Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
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
  • Loading branch information
yaacovCR committed Jul 12, 2021
1 parent fed00e3 commit dae6dc7
Show file tree
Hide file tree
Showing 24 changed files with 156 additions and 147 deletions.
24 changes: 24 additions & 0 deletions .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.
2 changes: 1 addition & 1 deletion packages/batch-delegate/tests/withTransforms.test.ts
Expand Up @@ -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
Expand Down
43 changes: 19 additions & 24 deletions packages/batch-execute/src/createBatchingExecutor.ts
Expand Up @@ -4,59 +4,57 @@ 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(
executor: Executor,
dataLoaderOptions?: DataLoader.Options<any, any, any>,
extensionsReducer: (
mergedExtensions: Record<string, any>,
executionParams: ExecutionParams
request: Request
) => Record<string, any> = 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<string, any>, executionParams: ExecutionParams) => Record<string, any>
extensionsReducer: (mergedExtensions: Record<string, any>, request: Request) => Record<string, any>
) {
return async (execs: ReadonlyArray<ExecutionParams>): Promise<Array<ExecutionResult>> => {
const execBatches: Array<Array<ExecutionParams>> = [];
return async (requests: ReadonlyArray<Request>): Promise<Array<ExecutionResult>> => {
const execBatches: Array<Array<Request>> = [];
let index = 0;
const exec = execs[index];
let currentBatch: Array<ExecutionParams> = [exec];
const request = requests[index];
let currentBatch: Array<Request> = [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<ValueOrPromise<ExecutionResult>> = 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)
Expand All @@ -70,11 +68,8 @@ function createLoadFn(
};
}

function defaultExtensionsReducer(
mergedExtensions: Record<string, any>,
executionParams: ExecutionParams
): Record<string, any> {
const newExtensions = executionParams.extensions;
function defaultExtensionsReducer(mergedExtensions: Record<string, any>, request: Request): Record<string, any> {
const newExtensions = request.extensions;
if (newExtensions != null) {
Object.assign(mergedExtensions, newExtensions);
}
Expand Down
6 changes: 2 additions & 4 deletions packages/batch-execute/src/getBatchingExecutor.ts
@@ -1,16 +1,14 @@
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';

export const getBatchingExecutor = memoize2of4(function (
_context: Record<string, any>,
executor: Executor,
dataLoaderOptions?: DataLoader.Options<any, any, any> | undefined,
extensionsReducer?:
| undefined
| ((mergedExtensions: Record<string, any>, executionParams: ExecutionParams) => Record<string, any>)
extensionsReducer?: undefined | ((mergedExtensions: Record<string, any>, request: Request) => Record<string, any>)
): Executor {
return createBatchingExecutor(executor, dataLoaderOptions, extensionsReducer);
});
Expand Up @@ -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';

Expand Down Expand Up @@ -56,10 +56,10 @@ import { createPrefix } from './prefix';
* }
* }
*/
export function mergeExecutionParams(
execs: Array<ExecutionParams>,
extensionsReducer: (mergedExtensions: Record<string, any>, executionParams: ExecutionParams) => Record<string, any>
): ExecutionParams {
export function mergeRequests(
requests: Array<Request>,
extensionsReducer: (mergedExtensions: Record<string, any>, request: Request) => Record<string, any>
): Request {
const mergedVariables: Record<string, any> = Object.create(null);
const mergedVariableDefinitions: Array<VariableDefinitionNode> = [];
const mergedSelections: Array<SelectionNode> = [];
Expand All @@ -68,11 +68,11 @@ export function mergeExecutionParams(

let operation: Maybe<OperationTypeNode>;

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);
Expand All @@ -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) {
Expand All @@ -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, {
Expand Down
4 changes: 4 additions & 0 deletions packages/delegate/src/createRequest.ts
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -59,6 +61,7 @@ export function createRequest({
fragments,
variableDefinitions,
variableValues,
targetRootValue,
targetOperationName,
targetOperation,
targetFieldName,
Expand Down Expand Up @@ -171,6 +174,7 @@ export function createRequest({
return {
document,
variables: newVariables,
rootValue: targetRootValue,
operationName: targetOperationName,
};
}
Expand Down

0 comments on commit dae6dc7

Please sign in to comment.