diff --git a/src/type/__tests__/introspection-test.js b/src/type/__tests__/introspection-test.js index 43d597671c8..9cb474b6864 100644 --- a/src/type/__tests__/introspection-test.js +++ b/src/type/__tests__/introspection-test.js @@ -847,6 +847,25 @@ describe('Introspection', () => { }, ], }, + { + name: 'specified', + locations: ['FIELD_DEFINITION'], + args: [ + { + defaultValue: null, + name: 'by', + type: { + kind: 'NON_NULL', + name: null, + ofType: { + kind: 'SCALAR', + name: 'String', + ofType: null, + }, + }, + }, + ], + }, { name: 'deprecated', locations: ['FIELD_DEFINITION', 'ENUM_VALUE'], diff --git a/src/type/directives.d.ts b/src/type/directives.d.ts index c6b86898100..67595e6e565 100644 --- a/src/type/directives.d.ts +++ b/src/type/directives.d.ts @@ -54,6 +54,12 @@ export const GraphQLIncludeDirective: GraphQLDirective; */ export const GraphQLSkipDirective: GraphQLDirective; +/** + * Used to provide an RFC3986-compliant URI for specifying the behaviour of + * custom scalar definitions. + */ +export const GraphQLSpecifiedDirective: GraphQLDirective; + /** * Constant string used for default reason for a deprecation. */ diff --git a/src/type/directives.js b/src/type/directives.js index 6b42a5e8709..1d1348d8759 100644 --- a/src/type/directives.js +++ b/src/type/directives.js @@ -167,6 +167,24 @@ export const GraphQLSkipDirective = new GraphQLDirective({ }, }); +/** + * Used to provide an RFC3986-compliant URI for specifying the behaviour of + * custom scalar definitions. + */ +export const GraphQLSpecifiedDirective = new GraphQLDirective({ + name: 'specified', + description: + 'Exposes a RFC3986-compliant URI that specifies the behaviour of this scalar.', + locations: [DirectiveLocation.FIELD_DEFINITION], + args: { + by: { + type: GraphQLNonNull(GraphQLString), + description: + 'The RFC3986-compliant URI that specifies the behaviour of this scalar.', + }, + }, +}); + /** * Constant string used for default reason for a deprecation. */ @@ -195,6 +213,7 @@ export const GraphQLDeprecatedDirective = new GraphQLDirective({ export const specifiedDirectives = Object.freeze([ GraphQLIncludeDirective, GraphQLSkipDirective, + GraphQLSpecifiedDirective, GraphQLDeprecatedDirective, ]); diff --git a/src/utilities/__tests__/buildASTSchema-test.js b/src/utilities/__tests__/buildASTSchema-test.js index 1d82a83f29c..1b64534b92f 100644 --- a/src/utilities/__tests__/buildASTSchema-test.js +++ b/src/utilities/__tests__/buildASTSchema-test.js @@ -15,6 +15,7 @@ import { assertDirective, GraphQLSkipDirective, GraphQLIncludeDirective, + GraphQLSpecifiedDirective, GraphQLDeprecatedDirective, } from '../../type/directives'; import { @@ -182,12 +183,15 @@ describe('Schema Builder', () => { expect(cycleSDL(sdl, { commentDescriptions: true })).to.equal(sdl); }); - it('Maintains @skip & @include', () => { + it('Maintains @include, @skip & @specified', () => { const schema = buildSchema('type Query'); - expect(schema.getDirectives()).to.have.lengthOf(3); + expect(schema.getDirectives()).to.have.lengthOf(4); expect(schema.getDirective('skip')).to.equal(GraphQLSkipDirective); expect(schema.getDirective('include')).to.equal(GraphQLIncludeDirective); + expect(schema.getDirective('specified')).to.equal( + GraphQLSpecifiedDirective, + ); expect(schema.getDirective('deprecated')).to.equal( GraphQLDeprecatedDirective, ); @@ -197,27 +201,32 @@ describe('Schema Builder', () => { const schema = buildSchema(` directive @skip on FIELD directive @include on FIELD + directive @specified on FIELD_DEFINITION directive @deprecated on FIELD_DEFINITION `); - expect(schema.getDirectives()).to.have.lengthOf(3); + expect(schema.getDirectives()).to.have.lengthOf(4); expect(schema.getDirective('skip')).to.not.equal(GraphQLSkipDirective); expect(schema.getDirective('include')).to.not.equal( GraphQLIncludeDirective, ); + expect(schema.getDirective('specified')).to.not.equal( + GraphQLSpecifiedDirective, + ); expect(schema.getDirective('deprecated')).to.not.equal( GraphQLDeprecatedDirective, ); }); - it('Adding directives maintains @skip & @include', () => { + it('Adding directives maintains @include, @skip & @specified', () => { const schema = buildSchema(` directive @foo(arg: Int) on FIELD `); - expect(schema.getDirectives()).to.have.lengthOf(4); + expect(schema.getDirectives()).to.have.lengthOf(5); expect(schema.getDirective('skip')).to.not.equal(undefined); expect(schema.getDirective('include')).to.not.equal(undefined); + expect(schema.getDirective('specified')).to.not.equal(undefined); expect(schema.getDirective('deprecated')).to.not.equal(undefined); }); diff --git a/src/utilities/__tests__/findBreakingChanges-test.js b/src/utilities/__tests__/findBreakingChanges-test.js index b3fd7dae6e9..9dbcbf87f48 100644 --- a/src/utilities/__tests__/findBreakingChanges-test.js +++ b/src/utilities/__tests__/findBreakingChanges-test.js @@ -7,6 +7,7 @@ import { GraphQLSchema } from '../../type/schema'; import { GraphQLSkipDirective, GraphQLIncludeDirective, + GraphQLSpecifiedDirective, GraphQLDeprecatedDirective, } from '../../type/directives'; @@ -790,7 +791,11 @@ describe('findBreakingChanges', () => { const oldSchema = new GraphQLSchema({}); const newSchema = new GraphQLSchema({ - directives: [GraphQLSkipDirective, GraphQLIncludeDirective], + directives: [ + GraphQLSkipDirective, + GraphQLIncludeDirective, + GraphQLSpecifiedDirective, + ], }); expect(findBreakingChanges(oldSchema, newSchema)).to.deep.equal([ diff --git a/src/utilities/__tests__/schemaPrinter-test.js b/src/utilities/__tests__/schemaPrinter-test.js index 7b245ac2c21..5d2a1d24949 100644 --- a/src/utilities/__tests__/schemaPrinter-test.js +++ b/src/utilities/__tests__/schemaPrinter-test.js @@ -590,6 +590,14 @@ describe('Type System Printer', () => { if: Boolean! ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT + """ + Exposes a RFC3986-compliant URI that specifies the behaviour of this scalar. + """ + directive @specified( + """The RFC3986-compliant URI that specifies the behaviour of this scalar.""" + by: String! + ) on FIELD_DEFINITION + """Marks an element of a GraphQL schema as no longer supported.""" directive @deprecated( """ @@ -803,6 +811,12 @@ describe('Type System Printer', () => { if: Boolean! ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT + # Exposes a RFC3986-compliant URI that specifies the behaviour of this scalar. + directive @specified( + # The RFC3986-compliant URI that specifies the behaviour of this scalar. + by: String! + ) on FIELD_DEFINITION + # Marks an element of a GraphQL schema as no longer supported. directive @deprecated( # Explains why this element was deprecated, usually also including a suggestion for how to access supported similar data. Formatted using the Markdown syntax (as specified by [CommonMark](https://commonmark.org/). diff --git a/src/utilities/buildASTSchema.js b/src/utilities/buildASTSchema.js index 4fbb763ffa1..414c8d5c736 100644 --- a/src/utilities/buildASTSchema.js +++ b/src/utilities/buildASTSchema.js @@ -55,6 +55,7 @@ import { GraphQLSkipDirective, GraphQLIncludeDirective, GraphQLDeprecatedDirective, + GraphQLSpecifiedDirective, } from '../type/directives'; import { type GraphQLType, @@ -170,6 +171,10 @@ export function buildASTSchema( directives.push(GraphQLIncludeDirective); } + if (!directives.some(directive => directive.name === 'specified')) { + directives.push(GraphQLSpecifiedDirective); + } + if (!directives.some(directive => directive.name === 'deprecated')) { directives.push(GraphQLDeprecatedDirective); }