Skip to content

Commit

Permalink
Preserve sources of variable values
Browse files Browse the repository at this point in the history
By way of introducing type `VariableValues`, allows `getVariableValues` to return both the coerced values as well as the original sources, which are then made available in `ExecutionContext`.
  • Loading branch information
leebyron authored and yaacovCR committed Sep 15, 2024
1 parent c7fd3f1 commit 12f30ec
Show file tree
Hide file tree
Showing 6 changed files with 218 additions and 113 deletions.
83 changes: 43 additions & 40 deletions src/execution/collectFields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,18 @@ import type { GraphQLSchema } from '../type/schema.js';
import { typeFromAST } from '../utilities/typeFromAST.js';

import type { GraphQLVariableSignature } from './getVariableSignature.js';
import { experimentalGetArgumentValues, getDirectiveValues } from './values.js';
import type { VariableValues } from './values.js';
import { getDirectiveValues, getFragmentVariableValues } from './values.js';

export interface DeferUsage {
label: string | undefined;
parentDeferUsage: DeferUsage | undefined;
}

export interface FragmentVariables {
signatures: ObjMap<GraphQLVariableSignature>;
values: ObjMap<unknown>;
}

export interface FieldDetails {
node: FieldNode;
deferUsage?: DeferUsage | undefined;
fragmentVariables?: FragmentVariables | undefined;
fragmentVariableValues?: VariableValues | undefined;
}

export type FieldGroup = ReadonlyArray<FieldDetails>;
Expand All @@ -55,7 +51,7 @@ export interface FragmentDetails {
interface CollectFieldsContext {
schema: GraphQLSchema;
fragments: ObjMap<FragmentDetails>;
variableValues: { [variable: string]: unknown };
variableValues: VariableValues;
operation: OperationDefinitionNode;
runtimeType: GraphQLObjectType;
visitedFragmentNames: Set<string>;
Expand All @@ -73,7 +69,7 @@ interface CollectFieldsContext {
export function collectFields(
schema: GraphQLSchema,
fragments: ObjMap<FragmentDetails>,
variableValues: { [variable: string]: unknown },
variableValues: VariableValues,
runtimeType: GraphQLObjectType,
operation: OperationDefinitionNode,
): {
Expand Down Expand Up @@ -114,7 +110,7 @@ export function collectFields(
export function collectSubfields(
schema: GraphQLSchema,
fragments: ObjMap<FragmentDetails>,
variableValues: { [variable: string]: unknown },
variableValues: VariableValues,
operation: OperationDefinitionNode,
returnType: GraphQLObjectType,
fieldGroup: FieldGroup,
Expand All @@ -136,14 +132,14 @@ export function collectSubfields(
for (const fieldDetail of fieldGroup) {
const selectionSet = fieldDetail.node.selectionSet;
if (selectionSet) {
const { deferUsage, fragmentVariables } = fieldDetail;
const { deferUsage, fragmentVariableValues } = fieldDetail;
collectFieldsImpl(
context,
selectionSet,
subGroupedFieldSet,
newDeferUsages,
deferUsage,
fragmentVariables,
fragmentVariableValues,
);
}
}
Expand All @@ -161,7 +157,7 @@ function collectFieldsImpl(
groupedFieldSet: AccumulatorMap<string, FieldDetails>,
newDeferUsages: Array<DeferUsage>,
deferUsage?: DeferUsage,
fragmentVariables?: FragmentVariables,
fragmentVariableValues?: VariableValues,
): void {
const {
schema,
Expand All @@ -175,19 +171,25 @@ function collectFieldsImpl(
for (const selection of selectionSet.selections) {
switch (selection.kind) {
case Kind.FIELD: {
if (!shouldIncludeNode(selection, variableValues, fragmentVariables)) {
if (
!shouldIncludeNode(selection, variableValues, fragmentVariableValues)
) {
continue;
}
groupedFieldSet.add(getFieldEntryKey(selection), {
node: selection,
deferUsage,
fragmentVariables,
fragmentVariableValues,
});
break;
}
case Kind.INLINE_FRAGMENT: {
if (
!shouldIncludeNode(selection, variableValues, fragmentVariables) ||
!shouldIncludeNode(
selection,
variableValues,
fragmentVariableValues,
) ||
!doesFragmentConditionMatch(schema, selection, runtimeType)
) {
continue;
Expand All @@ -196,7 +198,7 @@ function collectFieldsImpl(
const newDeferUsage = getDeferUsage(
operation,
variableValues,
fragmentVariables,
fragmentVariableValues,
selection,
deferUsage,
);
Expand All @@ -208,7 +210,7 @@ function collectFieldsImpl(
groupedFieldSet,
newDeferUsages,
deferUsage,
fragmentVariables,
fragmentVariableValues,
);
} else {
newDeferUsages.push(newDeferUsage);
Expand All @@ -218,7 +220,7 @@ function collectFieldsImpl(
groupedFieldSet,
newDeferUsages,
newDeferUsage,
fragmentVariables,
fragmentVariableValues,
);
}

Expand All @@ -230,15 +232,19 @@ function collectFieldsImpl(
const newDeferUsage = getDeferUsage(
operation,
variableValues,
fragmentVariables,
fragmentVariableValues,
selection,
deferUsage,
);

if (
!newDeferUsage &&
(visitedFragmentNames.has(fragName) ||
!shouldIncludeNode(selection, variableValues, fragmentVariables))
!shouldIncludeNode(
selection,
variableValues,
fragmentVariableValues,
))
) {
continue;
}
Expand All @@ -252,17 +258,14 @@ function collectFieldsImpl(
}

const fragmentVariableSignatures = fragment.variableSignatures;
let newFragmentVariables: FragmentVariables | undefined;
let newFragmentVariableValues: VariableValues | undefined;
if (fragmentVariableSignatures) {
newFragmentVariables = {
signatures: fragmentVariableSignatures,
values: experimentalGetArgumentValues(
selection,
Object.values(fragmentVariableSignatures),
variableValues,
fragmentVariables,
),
};
newFragmentVariableValues = getFragmentVariableValues(
selection,
fragmentVariableSignatures,
variableValues,
fragmentVariableValues,
);
}

if (!newDeferUsage) {
Expand All @@ -273,7 +276,7 @@ function collectFieldsImpl(
groupedFieldSet,
newDeferUsages,
deferUsage,
newFragmentVariables,
newFragmentVariableValues,
);
} else {
newDeferUsages.push(newDeferUsage);
Expand All @@ -283,7 +286,7 @@ function collectFieldsImpl(
groupedFieldSet,
newDeferUsages,
newDeferUsage,
newFragmentVariables,
newFragmentVariableValues,
);
}
break;
Expand All @@ -299,16 +302,16 @@ function collectFieldsImpl(
*/
function getDeferUsage(
operation: OperationDefinitionNode,
variableValues: { [variable: string]: unknown },
fragmentVariables: FragmentVariables | undefined,
variableValues: VariableValues,
fragmentVariableValues: VariableValues | undefined,
node: FragmentSpreadNode | InlineFragmentNode,
parentDeferUsage: DeferUsage | undefined,
): DeferUsage | undefined {
const defer = getDirectiveValues(
GraphQLDeferDirective,
node,
variableValues,
fragmentVariables,
fragmentVariableValues,
);

if (!defer) {
Expand Down Expand Up @@ -336,14 +339,14 @@ function getDeferUsage(
*/
function shouldIncludeNode(
node: FragmentSpreadNode | FieldNode | InlineFragmentNode,
variableValues: { [variable: string]: unknown },
fragmentVariables: FragmentVariables | undefined,
variableValues: VariableValues,
fragmentVariableValues: VariableValues | undefined,
): boolean {
const skip = getDirectiveValues(
GraphQLSkipDirective,
node,
variableValues,
fragmentVariables,
fragmentVariableValues,
);
if (skip?.if === true) {
return false;
Expand All @@ -353,7 +356,7 @@ function shouldIncludeNode(
GraphQLIncludeDirective,
node,
variableValues,
fragmentVariables,
fragmentVariableValues,
);
if (include?.if === false) {
return false;
Expand Down
19 changes: 10 additions & 9 deletions src/execution/execute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ import type {
StreamRecord,
} from './types.js';
import { DeferredFragmentRecord } from './types.js';
import type { VariableValues } from './values.js';
import {
experimentalGetArgumentValues,
getArgumentValues,
Expand Down Expand Up @@ -139,7 +140,7 @@ export interface ExecutionContext {
rootValue: unknown;
contextValue: unknown;
operation: OperationDefinitionNode;
variableValues: { [variable: string]: unknown };
variableValues: VariableValues;
fieldResolver: GraphQLFieldResolver<any, any>;
typeResolver: GraphQLTypeResolver<any, any>;
subscribeFieldResolver: GraphQLFieldResolver<any, any>;
Expand Down Expand Up @@ -510,15 +511,15 @@ export function buildExecutionContext(
/* c8 ignore next */
const variableDefinitions = operation.variableDefinitions ?? [];

const coercedVariableValues = getVariableValues(
const variableValuesOrErrors = getVariableValues(
schema,
variableDefinitions,
rawVariableValues ?? {},
{ maxErrors: 50 },
);

if (coercedVariableValues.errors) {
return coercedVariableValues.errors;
if (variableValuesOrErrors.errors) {
return variableValuesOrErrors.errors;
}

return {
Expand All @@ -527,7 +528,7 @@ export function buildExecutionContext(
rootValue,
contextValue,
operation,
variableValues: coercedVariableValues.coerced,
variableValues: variableValuesOrErrors.variableValues,
fieldResolver: fieldResolver ?? defaultFieldResolver,
typeResolver: typeResolver ?? defaultTypeResolver,
subscribeFieldResolver: subscribeFieldResolver ?? defaultFieldResolver,
Expand Down Expand Up @@ -753,7 +754,7 @@ function executeField(
fieldGroup[0].node,
fieldDef.args,
exeContext.variableValues,
fieldGroup[0].fragmentVariables,
fieldGroup[0].fragmentVariableValues,
);

// The resolve function's optional third argument is a context value that
Expand Down Expand Up @@ -842,7 +843,7 @@ export function buildResolveInfo(
),
rootValue: exeContext.rootValue,
operation: exeContext.operation,
variableValues: exeContext.variableValues,
variableValues: exeContext.variableValues.coerced,
};
}

Expand Down Expand Up @@ -1062,7 +1063,7 @@ function getStreamUsage(
GraphQLStreamDirective,
fieldGroup[0].node,
exeContext.variableValues,
fieldGroup[0].fragmentVariables,
fieldGroup[0].fragmentVariableValues,
);

if (!stream) {
Expand Down Expand Up @@ -1091,7 +1092,7 @@ function getStreamUsage(
const streamedFieldGroup: FieldGroup = fieldGroup.map((fieldDetails) => ({
node: fieldDetails.node,
deferUsage: undefined,
fragmentVariables: fieldDetails.fragmentVariables,
fragmentVariables: fieldDetails.fragmentVariableValues,
}));

const streamUsage = {
Expand Down
Loading

0 comments on commit 12f30ec

Please sign in to comment.