Skip to content

Commit

Permalink
Graphql 17 support (#185)
Browse files Browse the repository at this point in the history
* bumped graphql dependency to 17, migrated to new GraphQLError/getRootType syntax since old versions were deprecated and removed from graphql@17

* bump dependencies

* Test with GraphQL 17

* Specify alpha separately

* graphql-17: support graphql version 16

* graphql-17: support graphql version 15

* graphql-17: updated yarn.lock

* graphql-17: returned back to compat.ts

* graphql-17: fix some TS errors

* graphql-17: decrease coverage threshold for branches from 92 to 91

* graphql-17: code review feedback

* graphql-17: fix failing tests

* Remove idea modules file

---------

Co-authored-by: Oskari Porkka <oskari-veikko.porkka@zalando.de>
Co-authored-by: Oskari Porkka <oskari.porkka@zalando.de>
  • Loading branch information
3 people committed Mar 13, 2023
1 parent f207b57 commit 3d2675a
Show file tree
Hide file tree
Showing 10 changed files with 154 additions and 85 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
matrix:
node-version: [14, 16, 18]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
graphql-version: [15, 16]
graphql-version: [15, 16, 17.0.0-alpha.2]

steps:
- uses: actions/checkout@v2
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
},
"coverageThreshold": {
"global": {
"branches": 92,
"branches": 91,
"functions": 96,
"lines": 96,
"statements": 96
Expand All @@ -50,7 +50,7 @@
"graphql": ">=15"
},
"devDependencies": {
"@graphql-tools/schema": "^8.3.1",
"@graphql-tools/schema": "^9.0.8",
"@stryker-mutator/core": "^2.0.0",
"@stryker-mutator/jest-runner": "^2.0.0",
"@stryker-mutator/typescript": "^2.0.0",
Expand Down
3 changes: 1 addition & 2 deletions src/__tests__/json.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import fastJson from "fast-json-stringify";
import {
formatError,
GraphQLBoolean,
GraphQLError,
GraphQLID,
Expand All @@ -11,13 +10,13 @@ import {
GraphQLString,
parse,
GraphQLInt,
GraphQLScalarType,
versionInfo
} from "graphql";
import { buildExecutionContext } from "graphql/execution/execute";
import { compileQuery } from "../index";
import { queryToJSONSchema } from "../json";
import { makeExecutableSchema } from "@graphql-tools/schema";
import { formatError } from "../compat";

describe("json schema creator", () => {
const BlogAuthor = new GraphQLObjectType({
Expand Down
3 changes: 1 addition & 2 deletions src/__tests__/schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ import {
GraphQLString,
IntrospectionQuery,
parse,
printSchema,
versionInfo
printSchema
} from "graphql";
import { compileQuery, isCompiledQuery } from "../index";

Expand Down
6 changes: 3 additions & 3 deletions src/__tests__/subscription.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@
*/

import {
ExecutionArgs,
ExecutionResult,
GraphQLBoolean,
GraphQLInt,
GraphQLList,
GraphQLObjectType,
GraphQLSchema,
GraphQLString,
parse,
SubscriptionArgs
parse
} from "graphql";
import { compileQuery, isAsyncIterable, isCompiledQuery } from "../execution";

Expand Down Expand Up @@ -68,7 +68,7 @@ async function subscribe({
rootValue,
contextValue,
variableValues
}: SubscriptionArgs): Promise<
}: ExecutionArgs): Promise<
AsyncIterableIterator<ExecutionResult> | ExecutionResult
> {
const prepared = compileQuery(schema, document, operationName || "");
Expand Down
7 changes: 5 additions & 2 deletions src/__tests__/variables.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import {
GraphQLScalarType,
GraphQLSchema,
GraphQLString,
parse
parse,
versionInfo
} from "graphql";
import { GraphQLArgumentConfig } from "graphql/type/definition";
import { compileQuery, isCompiledQuery } from "../index";
Expand Down Expand Up @@ -283,7 +284,9 @@ describe("Execute: Handles inputs", () => {
errors: [
{
message:
'Argument "input" of type "TestInputObject" has invalid value {b: ["A", null, "C"], c: false}.',
versionInfo.major < 17
? 'Argument "input" of type "TestInputObject" has invalid value {b: ["A", null, "C"], c: false}.'
: 'Argument "input" of type "TestInputObject" has invalid value { b: ["A", null, "C"], c: false }.',
locations: [{ line: 3, column: 41 }]
}
]
Expand Down
44 changes: 12 additions & 32 deletions src/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,13 @@ import {
typeFromAST,
valueFromASTUntyped,
ValueNode,
VariableNode,
versionInfo
VariableNode
} from "graphql";
import { getFieldDef } from "graphql/execution/execute";
import { Kind, SelectionNode, TypeNode } from "graphql/language";
import { isAbstractType } from "graphql/type";
import { CompilationContext, GLOBAL_VARIABLES_NAME } from "./execution";
import createInspect from "./inspect";
import { Maybe } from "./types";
import { getGraphQLErrorOptions, resolveFieldDef } from "./compat";

export interface JitFieldNode extends FieldNode {
__internalShouldInclude?: string;
Expand Down Expand Up @@ -414,7 +412,7 @@ function compileSkipIncludeDirective(
if (ifNode == null) {
throw new GraphQLError(
`Directive '${directive.name.value}' is missing required arguments: 'if'`,
[directive]
getGraphQLErrorOptions([directive])
);
}

Expand All @@ -431,7 +429,7 @@ function compileSkipIncludeDirective(
}' has an invalid value (${valueFromASTUntyped(
ifNode.value
)}). Expected type 'Boolean!'`,
[ifNode]
getGraphQLErrorOptions([ifNode])
);
}
}
Expand All @@ -454,9 +452,10 @@ function validateSkipIncludeVariableType(
(it) => it.variable.name.value === variable.name.value
);
if (variableDefinition == null) {
throw new GraphQLError(`Variable '${variable.name.value}' is not defined`, [
variable
]);
throw new GraphQLError(
`Variable '${variable.name.value}' is not defined`,
getGraphQLErrorOptions([variable])
);
}

// Part of Spec text: https://spec.graphql.org/June2018/#sec-All-Variable-Usages-are-Allowed
Expand All @@ -478,7 +477,7 @@ function validateSkipIncludeVariableType(
`Variable '${variable.name.value}' of type '${typeNodeToString(
variableDefinition.type
)}' used in position expecting type 'Boolean!'`,
[variableDefinition]
getGraphQLErrorOptions([variableDefinition])
);
}
}
Expand Down Expand Up @@ -534,26 +533,7 @@ function getFieldEntryKey(node: FieldNode): string {
return node.alias ? node.alias.value : node.name.value;
}

/**
* Resolves the field on the given source object. In particular, this
* figures out the value that the field returns by calling its resolve function,
* then calls completeValue to complete promises, serialize scalars, or execute
* the sub-selection-set for objects.
*/
export function resolveFieldDef(
compilationContext: CompilationContext,
parentType: GraphQLObjectType,
fieldNodes: FieldNode[]
): Maybe<GraphQLField<any, any>> {
const fieldNode = fieldNodes[0];

if (versionInfo.major < 16) {
const fieldName = fieldNode.name.value;
return getFieldDef(compilationContext.schema, parentType, fieldName as any);
}

return getFieldDef(compilationContext.schema, parentType, fieldNode as any);
}
export { resolveFieldDef };

/**
* A memoized collection of relevant subfields in the context of the return
Expand Down Expand Up @@ -694,7 +674,7 @@ export function getArgumentDefs(
`Argument "${name}" of type "${argType}" has invalid value ${print(
argumentNode.value
)}.`,
argumentNode.value
getGraphQLErrorOptions(argumentNode.value)
);
}

Expand All @@ -717,7 +697,7 @@ export function getArgumentDefs(
`"${argType}" must not be null.`
: `Argument "${name}" of required type ` +
`"${argType}" was not provided.`,
node
getGraphQLErrorOptions(node)
);
}
}
Expand Down
110 changes: 99 additions & 11 deletions src/compat.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
import * as GraphQL from "graphql";
import {
GraphQLSchema,
GraphQLError,
versionInfo,
FieldNode,
GraphQLField
} from "graphql";
import { GraphQLObjectType } from "graphql/type/definition";
import { Maybe } from "graphql/jsutils/Maybe";

import { ASTNode, OperationDefinitionNode } from "graphql/language/ast";
import * as errorUtilities from "graphql/error";
import * as utilities from "graphql/utilities";
import { GraphQLFormattedError } from "graphql/error";
import { CompilationContext } from "./execution";
import * as execute from "graphql/execution/execute";

/**
* A helper file to support backward compatibility for different versions of graphql-js.
*/

/**
* A helper to support backward compatibility for different versions of graphql-js.
*
* v15 does not have schema.getRootType
* v16 has both
* v17 will not have getOperationRootType
Expand All @@ -15,13 +32,84 @@ import * as GraphQL from "graphql";
* GraphQL v17 would remove getOperationRootType.
*/
export function getOperationRootType(
schema: GraphQL.GraphQLSchema,
operation: GraphQL.OperationDefinitionNode
) {
if (GraphQL.getOperationRootType) {
return GraphQL.getOperationRootType(schema, operation);
} else {
// the use of any is to support graphql v15 types which will not use this codepath
return (schema as any).getRootType(operation.operation)!;
schema: GraphQLSchema,
operation: OperationDefinitionNode
): GraphQLObjectType {
if (versionInfo.major < 16) {
return (utilities as any).getOperationRootType(schema, operation);
}

const type = (schema as any).getRootType(operation.operation);

if (!type) {
throw new Error(`No root type for operation ${operation.operation}`);
}

return type;
}

/**
* v16 and lower versions don't have .toJSON method on GraphQLError
* v17 does have .toJSON and doesn't have "formatError" export anymore
*/
export function formatError(error: GraphQLError): GraphQLFormattedError {
if (versionInfo.major < 16) {
return (errorUtilities as any).formatError(error);
}

return (error as any).toJSON();
}

/**
* v17 dropped support for positional arguments in GraphQLError constructor
* https://github.com/graphql/graphql-js/pull/3577
*/
export function getGraphQLErrorOptions(
nodes: Maybe<ReadonlyArray<ASTNode> | ASTNode>
): ConstructorParameters<typeof GraphQLError>[1] {
if (versionInfo.major < 16) {
return nodes as any;
}

return { nodes } as any;
}

/**
* Resolves the field on the given source object. In particular, this
* figures out the value that the field returns by calling its resolve function,
* then calls completeValue to complete promises, serialize scalars, or execute
* the sub-selection-set for objects.
*
* v15 has getFieldDef that accepts field name
* v16 has getFieldDef that accepts field node
* v17 drops getFieldDef support and adds getField method
*/
export function resolveFieldDef(
compilationContext: CompilationContext,
parentType: GraphQLObjectType,
fieldNodes: FieldNode[]
): Maybe<GraphQLField<any, any>> {
const fieldNode = fieldNodes[0];

if (versionInfo.major < 16) {
const fieldName = fieldNode.name.value;
return (execute as any).getFieldDef(
compilationContext.schema,
parentType,
fieldName as any
);
}

if (versionInfo.major < 17) {
return (execute as any).getFieldDef(
compilationContext.schema,
parentType,
fieldNode as any
);
}

return (compilationContext.schema as any).getField(
parentType,
fieldNode.name.value
);
}
4 changes: 2 additions & 2 deletions src/execution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ import {
ObjectPath,
resolveFieldDef
} from "./ast";
import { getOperationRootType } from "./compat";
import { GraphQLError as GraphqlJitError } from "./error";
import createInspect from "./inspect";
import { queryToJSONSchema } from "./json";
Expand All @@ -62,6 +61,7 @@ import {
compileVariableParsing,
failToParseVariables
} from "./variables";
import { getGraphQLErrorOptions, getOperationRootType } from "./compat";

const inspect = createInspect();

Expand Down Expand Up @@ -1706,7 +1706,7 @@ function compileSubscriptionOperation(
if (!field) {
throw new GraphQLError(
`The subscription field "${fieldName}" is not defined.`,
fieldNodes
getGraphQLErrorOptions(fieldNodes)
);
}

Expand Down
Loading

0 comments on commit 3d2675a

Please sign in to comment.