Skip to content

Commit

Permalink
Support scalars via resolvers in makeExtendSchemaPlugin (#1760)
Browse files Browse the repository at this point in the history
  • Loading branch information
benjie committed Sep 28, 2023
2 parents 5358591 + 7fb6593 commit 770406f
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 30 deletions.
5 changes: 5 additions & 0 deletions .changeset/early-tables-admire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"graphile-utils": patch
---

makeExtendSchemaPlugin now support specifying scalar details via resolvers/plans
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { constant, grafast } from "grafast";
import { GraphQLScalarType } from "grafast/graphql";
import { buildSchema, QueryPlugin } from "graphile-build";

import { EXPORTABLE, gql, makeExtendSchemaPlugin } from "../src/index.js";

const ExtendPlugin = makeExtendSchemaPlugin({
typeDefs: gql`
scalar Scalar1
scalar Scalar2
extend type Query {
scalar1: Scalar1
scalar2: Scalar2
}
`,
plans: {
Query: {
scalar1() {
return constant("hello world");
},
scalar2() {
return constant("hello world");
},
},
Scalar1: new GraphQLScalarType({
name: "SomethingElse",
serialize: EXPORTABLE(
() =>
function serialize() {
return 1;
},
[],
),
parseLiteral: EXPORTABLE(
() =>
function parseLiteral() {
return 1;
},
[],
),
parseValue: EXPORTABLE(
() =>
function parseValue() {
return 1;
},
[],
),
}),
Scalar2: {
serialize: EXPORTABLE(
() =>
function serialize() {
return 2;
},
[],
),
parseLiteral: EXPORTABLE(
() =>
function parseLiteral() {
return 2;
},
[],
),
parseValue: EXPORTABLE(
() =>
function parseValue() {
return 2;
},
[],
),
},
},
});

it("supports scalars", async () => {
const schema = buildSchema({
plugins: [QueryPlugin, ExtendPlugin],
});
const result = await grafast({
schema,
source: /* GraphQL */ `
{
scalar1
scalar2
}
`,
});
expect(result).toEqual({
data: {
scalar1: 1,
scalar2: 2,
},
});
});
98 changes: 68 additions & 30 deletions graphile-build/graphile-utils/src/makeExtendSchemaPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import type {
GraphQLObjectType,
GraphQLOutputType,
GraphQLScalarType,
GraphQLScalarTypeConfig,
// Union types:
GraphQLType,
GraphQLTypeResolver,
Expand All @@ -49,6 +50,8 @@ import type { GraphileBuild } from "graphile-build";

import { EXPORTABLE } from "./exportable.js";

type Maybe<T> = T | null | undefined;

export interface ObjectFieldConfig<TSource = any, TContext = any> {
plan?: FieldPlanResolver<any, any, any>;
subscribePlan?: FieldPlanResolver<any, any, any>;
Expand Down Expand Up @@ -76,13 +79,26 @@ export interface EnumResolver {
[key: string]: string | number | Array<any> | Record<string, any> | symbol;
}

export interface TypeResolver {
__resolveType?: GraphQLTypeResolver<any, any>;
}

/** @deprecated Use Plans instead */
export interface Resolvers<TSource = any, TContext = any> {
[key: string]: ObjectResolver<TSource, TContext> | EnumResolver;
[key: string]:
| ObjectResolver<TSource, TContext>
| EnumResolver
| TypeResolver
| GraphQLScalarType
| GraphQLScalarTypeConfig<any, any>;
}

export interface Plans<TSource = any, TContext = any> {
[key: string]: ObjectPlan<TSource, TContext> | EnumResolver;
[key: string]:
| ObjectPlan<TSource, TContext>
| EnumResolver
| GraphQLScalarType
| GraphQLScalarTypeConfig<any, any>;
}

export interface ExtensionDefinition {
Expand Down Expand Up @@ -314,7 +330,9 @@ export function makeExtendSchemaPlugin(
const name = getName(definition.name);
const description = getDescription(definition.description);
const directives = getDirectives(definition.directives);
const relevantResolver = plans[name] || resolvers[name] || {};
const relevantResolver = (plans[name] ??
resolvers[name] ??
{}) as EnumResolver;
const values: GraphQLEnumValueConfigMap = (
definition.values ?? []
).reduce(
Expand Down Expand Up @@ -435,9 +453,8 @@ export function makeExtendSchemaPlugin(
directives,
...scopeFromDirectives(directives),
};
const resolveType = resolvers[name]?.__resolveType as
| GraphQLTypeResolver<any, any>
| undefined;
const resolveType = (resolvers[name] as Maybe<TypeResolver>)
?.__resolveType;
build.registerUnionType(
name,
scope,
Expand Down Expand Up @@ -470,9 +487,8 @@ export function makeExtendSchemaPlugin(
directives,
...scopeFromDirectives(directives),
};
const resolveType = resolvers[name]?.__resolveType as
| GraphQLTypeResolver<any, any>
| undefined;
const resolveType = (resolvers[name] as Maybe<TypeResolver>)
?.__resolveType;
build.registerInterfaceType(
name,
scope,
Expand Down Expand Up @@ -506,30 +522,48 @@ export function makeExtendSchemaPlugin(
directives,
...scopeFromDirectives(directives),
};
const possiblePlan = plans[name];
const possibleResolver = resolvers[name] as Maybe<
GraphQLScalarType | GraphQLScalarTypeConfig<any, any>
>;
if (possiblePlan && possibleResolver) {
throw new Error(
`You must set only plans.${name} or resolvers.${name} - not both!`,
);
}
const rawConfig = possiblePlan ?? possibleResolver;
const config = rawConfig
? rawConfig instanceof GraphQLScalarType
? EXPORTABLE((rawConfig) => rawConfig.toConfig(), [rawConfig])
: (rawConfig as GraphQLScalarTypeConfig<any, any>)
: null;
build.registerScalarType(
name,
scope,
() => ({
description,
serialize: EXPORTABLE(
() => (value: any) => String(value),
[],
),
parseValue: EXPORTABLE(
() => (value: any) => String(value),
[],
),
parseLiteral: EXPORTABLE(
(GraphQLError, Kind, name) => (ast: any) => {
if (ast.kind !== Kind.STRING) {
throw new GraphQLError(
`${name} can only parse string values`,
);
}
return ast.value;
},
[GraphQLError, Kind, name],
),
astNode: definition,
extensions: config?.extensions,
specifiedByURL: config?.specifiedByURL,
serialize: config?.serialize
? EXPORTABLE((config) => config.serialize, [config])
: EXPORTABLE(() => (value: any) => String(value), []),
parseValue: config?.parseValue
? EXPORTABLE((config) => config.parseValue, [config])
: EXPORTABLE(() => (value: any) => String(value), []),
parseLiteral: config?.parseLiteral
? EXPORTABLE((config) => config.parseLiteral, [config])
: EXPORTABLE(
(GraphQLError, Kind, name) => (ast: any) => {
if (ast.kind !== Kind.STRING) {
throw new GraphQLError(
`${name} can only parse string values`,
);
}
return ast.value;
},
[GraphQLError, Kind, name],
),
}),
uniquePluginName,
);
Expand Down Expand Up @@ -1001,8 +1035,12 @@ export function makeExtendSchemaPlugin(
* can define 'plan', 'subscribePlan', 'resolve', 'subscribe' and
* other relevant methods.
*/
const possiblePlan = plans[Self.name]?.[fieldName];
const possibleResolver = resolvers[Self.name]?.[fieldName];
const possiblePlan = (
plans[Self.name] as Maybe<ObjectPlan<any, any>>
)?.[fieldName];
const possibleResolver = (
resolvers[Self.name] as Maybe<ObjectResolver>
)?.[fieldName];
if (possiblePlan && possibleResolver) {
throw new Error(
`You must set only plans.${Self.name}.${fieldName} or resolvers.${Self.name}.${fieldName} - not both!`,
Expand Down

0 comments on commit 770406f

Please sign in to comment.