Skip to content

Commit 8a097e5

Browse files
IvanGoncharovleebyron
authored andcommitted
extendSchema: add support for extending scalars (#1392)
1 parent f9e6d81 commit 8a097e5

File tree

3 files changed

+58
-8
lines changed

3 files changed

+58
-8
lines changed

src/type/definition.js

+4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import type {
2626
EnumTypeDefinitionNode,
2727
EnumValueDefinitionNode,
2828
InputObjectTypeDefinitionNode,
29+
ScalarTypeExtensionNode,
2930
ObjectTypeExtensionNode,
3031
InterfaceTypeExtensionNode,
3132
UnionTypeExtensionNode,
@@ -535,13 +536,15 @@ export class GraphQLScalarType {
535536
name: string;
536537
description: ?string;
537538
astNode: ?ScalarTypeDefinitionNode;
539+
extensionASTNodes: ?$ReadOnlyArray<ScalarTypeExtensionNode>;
538540

539541
_scalarConfig: GraphQLScalarTypeConfig<*, *>;
540542

541543
constructor(config: GraphQLScalarTypeConfig<*, *>): void {
542544
this.name = config.name;
543545
this.description = config.description;
544546
this.astNode = config.astNode;
547+
this.extensionASTNodes = config.extensionASTNodes;
545548
this._scalarConfig = config;
546549
invariant(typeof config.name === 'string', 'Must provide name.');
547550
invariant(
@@ -593,6 +596,7 @@ export type GraphQLScalarTypeConfig<TInternal, TExternal> = {
593596
name: string,
594597
description?: ?string,
595598
astNode?: ?ScalarTypeDefinitionNode,
599+
extensionASTNodes?: ?$ReadOnlyArray<ScalarTypeExtensionNode>,
596600
serialize: (value: mixed) => ?TExternal,
597601
parseValue?: (value: mixed) => ?TInternal,
598602
parseLiteral?: (

src/utilities/__tests__/extendSchema-test.js

+27-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { Kind } from '../../language/kinds';
1515
import { graphqlSync } from '../../';
1616
import {
1717
GraphQLSchema,
18+
GraphQLScalarType,
1819
GraphQLObjectType,
1920
GraphQLInterfaceType,
2021
GraphQLUnionType,
@@ -32,6 +33,11 @@ import {
3233
} from '../../type';
3334

3435
// Test schema.
36+
const SomeScalarType = new GraphQLScalarType({
37+
name: 'SomeScalar',
38+
serialize: x => x,
39+
});
40+
3541
const SomeInterfaceType = new GraphQLInterfaceType({
3642
name: 'SomeInterface',
3743
fields: () => ({
@@ -92,6 +98,7 @@ const testSchema = new GraphQLSchema({
9298
name: 'Query',
9399
fields: () => ({
94100
foo: { type: FooType },
101+
someScalar: { type: SomeScalarType },
95102
someUnion: { type: SomeUnionType },
96103
someEnum: { type: SomeEnumType },
97104
someInterface: {
@@ -294,12 +301,26 @@ describe('extendSchema', () => {
294301
expect(fooDirective.args[0].type).to.equal(someInputType);
295302
});
296303

304+
it('extends scalars by adding new directives', () => {
305+
const extendedSchema = extendTestSchema(`
306+
extend scalar SomeScalar @foo
307+
`);
308+
309+
const someScalar = extendedSchema.getType('SomeScalar');
310+
expect(someScalar.extensionASTNodes).to.have.lengthOf(1);
311+
expect(print(someScalar.extensionASTNodes[0])).to.equal(
312+
'extend scalar SomeScalar @foo',
313+
);
314+
});
315+
297316
it('correctly assign AST nodes to new and extended types', () => {
298317
const extendedSchema = extendTestSchema(`
299318
extend type Query {
300319
newField(testArg: TestInput): TestEnum
301320
}
302321
322+
extend scalar SomeScalar @foo
323+
303324
extend enum SomeEnum {
304325
NEW_VALUE
305326
}
@@ -327,6 +348,8 @@ describe('extendSchema', () => {
327348
oneMoreNewField: TestUnion
328349
}
329350
351+
extend scalar SomeScalar @test
352+
330353
extend enum SomeEnum {
331354
ONE_MORE_NEW_VALUE
332355
}
@@ -351,11 +374,12 @@ describe('extendSchema', () => {
351374
interfaceField: String
352375
}
353376
354-
directive @test(arg: Int) on FIELD
377+
directive @test(arg: Int) on FIELD | SCALAR
355378
`);
356379
const extendedTwiceSchema = extendSchema(extendedSchema, ast);
357380

358381
const query = extendedTwiceSchema.getType('Query');
382+
const someScalar = extendedTwiceSchema.getType('SomeScalar');
359383
const someEnum = extendedTwiceSchema.getType('SomeEnum');
360384
const someUnion = extendedTwiceSchema.getType('SomeUnion');
361385
const someInput = extendedTwiceSchema.getType('SomeInput');
@@ -369,6 +393,7 @@ describe('extendSchema', () => {
369393
const testDirective = extendedTwiceSchema.getDirective('test');
370394

371395
expect(query.extensionASTNodes).to.have.lengthOf(2);
396+
expect(someScalar.extensionASTNodes).to.have.lengthOf(2);
372397
expect(someEnum.extensionASTNodes).to.have.lengthOf(2);
373398
expect(someUnion.extensionASTNodes).to.have.lengthOf(2);
374399
expect(someInput.extensionASTNodes).to.have.lengthOf(2);
@@ -384,6 +409,7 @@ describe('extendSchema', () => {
384409
kind: Kind.DOCUMENT,
385410
definitions: [
386411
...query.extensionASTNodes,
412+
...someScalar.extensionASTNodes,
387413
...someEnum.extensionASTNodes,
388414
...someUnion.extensionASTNodes,
389415
...someInput.extensionASTNodes,

src/utilities/extendSchema.js

+27-7
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { ASTDefinitionBuilder } from './buildASTSchema';
1515
import { GraphQLError } from '../error/GraphQLError';
1616
import { isSchema, GraphQLSchema } from '../type/schema';
1717
import { isIntrospectionType } from '../type/introspection';
18+
import { isSpecifiedScalarType } from '../type/scalars';
1819

1920
import type { GraphQLSchemaValidationOptions } from '../type/schema';
2021

@@ -24,6 +25,7 @@ import type {
2425
} from '../type/definition';
2526

2627
import {
28+
isScalarType,
2729
isObjectType,
2830
isInterfaceType,
2931
isUnionType,
@@ -33,6 +35,7 @@ import {
3335
isInputObjectType,
3436
GraphQLList,
3537
GraphQLNonNull,
38+
GraphQLScalarType,
3639
GraphQLObjectType,
3740
GraphQLInterfaceType,
3841
GraphQLUnionType,
@@ -135,6 +138,7 @@ export function extendSchema(
135138
}
136139
typeDefinitionMap[typeName] = def;
137140
break;
141+
case Kind.SCALAR_TYPE_EXTENSION:
138142
case Kind.OBJECT_TYPE_EXTENSION:
139143
case Kind.INTERFACE_TYPE_EXTENSION:
140144
case Kind.ENUM_TYPE_EXTENSION:
@@ -170,10 +174,6 @@ export function extendSchema(
170174
}
171175
directiveDefinitions.push(def);
172176
break;
173-
case Kind.SCALAR_TYPE_EXTENSION:
174-
throw new Error(
175-
`The ${def.kind} kind is not yet supported by extendSchema().`,
176-
);
177177
}
178178
}
179179

@@ -280,14 +280,16 @@ export function extendSchema(
280280
}
281281

282282
function extendNamedType<T: GraphQLNamedType>(type: T): T {
283-
if (isIntrospectionType(type)) {
284-
// Introspection types are not extended.
283+
if (isIntrospectionType(type) || isSpecifiedScalarType(type)) {
284+
// Builtin types are not extended.
285285
return type;
286286
}
287287

288288
const name = type.name;
289289
if (!extendTypeCache[name]) {
290-
if (isObjectType(type)) {
290+
if (isScalarType(type)) {
291+
extendTypeCache[name] = extendScalarType(type);
292+
} else if (isObjectType(type)) {
291293
extendTypeCache[name] = extendObjectType(type);
292294
} else if (isInterfaceType(type)) {
293295
extendTypeCache[name] = extendInterfaceType(type);
@@ -425,6 +427,24 @@ export function extendSchema(
425427
return newValueMap;
426428
}
427429

430+
function extendScalarType(type: GraphQLScalarType): GraphQLScalarType {
431+
const name = type.name;
432+
const extensionASTNodes = typeExtensionsMap[name]
433+
? type.extensionASTNodes
434+
? type.extensionASTNodes.concat(typeExtensionsMap[name])
435+
: typeExtensionsMap[name]
436+
: type.extensionASTNodes;
437+
return new GraphQLScalarType({
438+
name,
439+
description: type.description,
440+
astNode: type.astNode,
441+
extensionASTNodes,
442+
serialize: type._scalarConfig.serialize,
443+
parseValue: type._scalarConfig.parseValue,
444+
parseLiteral: type._scalarConfig.parseLiteral,
445+
});
446+
}
447+
428448
function extendObjectType(type: GraphQLObjectType): GraphQLObjectType {
429449
const name = type.name;
430450
const extensionASTNodes = typeExtensionsMap[name]

0 commit comments

Comments
 (0)