Skip to content

Commit 396098e

Browse files
committed
buildSchema custom scalars support
1 parent c634136 commit 396098e

File tree

3 files changed

+69
-16
lines changed

3 files changed

+69
-16
lines changed

src/type/definition.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -610,15 +610,19 @@ export type GraphQLScalarLiteralParser<TInternal> = (
610610
variables: ?ObjMap<mixed>,
611611
) => ?TInternal;
612612

613-
export type GraphQLScalarTypeConfig<TInternal, TExternal> = {|
614-
name: string,
615-
description?: ?string,
613+
export type GraphQLScalarTypeConverters<TInternal, TExternal> = {|
616614
// Serializes an internal value to include in a response.
617615
serialize?: GraphQLScalarSerializer<TExternal>,
618616
// Parses an externally provided value to use as an input.
619617
parseValue?: GraphQLScalarValueParser<TInternal>,
620618
// Parses an externally provided literal value to use as an input.
621619
parseLiteral?: GraphQLScalarLiteralParser<TInternal>,
620+
|};
621+
622+
export type GraphQLScalarTypeConfig<TInternal, TExternal> = {|
623+
name: string,
624+
...GraphQLScalarTypeConverters<TInternal, TExternal>,
625+
description?: ?string,
622626
astNode?: ?ScalarTypeDefinitionNode,
623627
extensionASTNodes?: ?$ReadOnlyArray<ScalarTypeExtensionNode>,
624628
|};

src/utilities/__tests__/buildASTSchema-test.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,38 @@ describe('Schema Builder', () => {
122122
});
123123
});
124124

125+
it('can define custom scalar converters', () => {
126+
const schema = buildSchema(
127+
`
128+
scalar Uppercase
129+
scalar Lowercase
130+
type Query {
131+
hello: Uppercase
132+
lower(str: Lowercase!): String
133+
}
134+
`,
135+
{
136+
resolvers: {
137+
Uppercase: {
138+
serialize: (value: string) => value.toUpperCase(),
139+
},
140+
Lowercase: {
141+
parseValue: (value: string) => value.toLowerCase(),
142+
},
143+
Query: {
144+
lower: (_, { str }: { str: string, ... }) => str,
145+
},
146+
},
147+
},
148+
);
149+
150+
expect(
151+
graphqlSync(schema, '{ hello lower(str: "World") }', { hello: 'hello' }),
152+
).to.deep.equal({
153+
data: { hello: 'HELLO', lower: 'world' },
154+
});
155+
});
156+
125157
it('Empty type', () => {
126158
const sdl = dedent`
127159
type EmptyType

src/utilities/buildASTSchema.js

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import {
4747
type GraphQLEnumValueConfig,
4848
type GraphQLInputFieldConfig,
4949
type GraphQLFieldResolver,
50+
type GraphQLScalarTypeConverters,
5051
GraphQLScalarType,
5152
GraphQLObjectType,
5253
GraphQLInterfaceType,
@@ -73,10 +74,15 @@ import {
7374
GraphQLSchema,
7475
} from '../type/schema';
7576

76-
export type TypeFieldResolverMap = ObjMap<
77-
| ObjMap<GraphQLFieldResolver<mixed, mixed, mixed>> /* type and interface */
78-
| ObjMap<any> /* enum */,
79-
>;
77+
export type TypeFieldResolver =
78+
/* scalars */
79+
| GraphQLScalarTypeConverters<any, any>
80+
/* type and interface */
81+
| ObjMap<GraphQLFieldResolver<mixed, mixed, mixed>>
82+
/* enum */
83+
| ObjMap<any>;
84+
85+
export type TypeFieldResolverMap = ObjMap<TypeFieldResolver>;
8086

8187
export type BuildSchemaOptions = {
8288
...GraphQLSchemaValidationOptions,
@@ -127,6 +133,7 @@ export type BuildSchemaOptions = {
127133
* - resolvers — map of named types
128134
* - Object, Interface — field resolvers
129135
* - Enum — External string → any internal value
136+
* - Scalars — serialize, parseValue, parseLiteral
130137
*
131138
*/
132139
export function buildASTSchema(
@@ -272,7 +279,7 @@ export class ASTDefinitionBuilder {
272279
type: (this.getWrappedType(field.type): any),
273280
description: getDescription(field, this._options),
274281
args: keyByNameNode(field.arguments || [], arg => this.buildArg(arg)),
275-
resolve: this._lookupResolver(typeName, field.name.value),
282+
resolve: this._lookupResolverField(typeName, field.name.value),
276283
deprecationReason: getDeprecationReason(field),
277284
astNode: field,
278285
};
@@ -309,7 +316,7 @@ export class ASTDefinitionBuilder {
309316
typeName?: string,
310317
): GraphQLEnumValueConfig {
311318
return {
312-
value: this._lookupResolver(typeName, value.name.value),
319+
value: this._lookupResolverField(typeName, value.name.value),
313320
description: getDescription(value, this._options),
314321
deprecationReason: getDeprecationReason(value),
315322
astNode: value,
@@ -423,9 +430,17 @@ export class ASTDefinitionBuilder {
423430
}
424431

425432
_makeScalarDef(astNode: ScalarTypeDefinitionNode) {
433+
const name = astNode.name.value;
434+
const resolver = ((this._lookupResolver(
435+
name,
436+
): any): GraphQLScalarTypeConverters<any, any>);
437+
426438
return new GraphQLScalarType({
427-
name: astNode.name.value,
439+
name,
428440
description: getDescription(astNode, this._options),
441+
serialize: (resolver && resolver.serialize) || undefined,
442+
parseValue: (resolver && resolver.parseValue) || undefined,
443+
parseLiteral: (resolver && resolver.parseLiteral) || undefined,
429444
astNode,
430445
});
431446
}
@@ -443,16 +458,18 @@ export class ASTDefinitionBuilder {
443458
});
444459
}
445460

446-
_lookupResolver(typeName: ?string, key: string) {
461+
_lookupResolver(typeName: ?string) {
462+
const opts = this._options;
447463
return (
448-
(typeName &&
449-
this._options &&
450-
this._options.resolvers &&
451-
this._options.resolvers[typeName] &&
452-
this._options.resolvers[typeName][key]) ||
464+
(typeName && opts && opts.resolvers && opts.resolvers[typeName]) ||
453465
undefined
454466
);
455467
}
468+
469+
_lookupResolverField(typeName: ?string, key: string) {
470+
const resolver = this._lookupResolver(typeName);
471+
return (resolver && resolver[key]) || undefined;
472+
}
456473
}
457474

458475
function keyByNameNode<T: { +name: NameNode, ... }, V>(

0 commit comments

Comments
 (0)