From f4b1d3055860ea28acc86673e2a67a1f1ba3ad97 Mon Sep 17 00:00:00 2001 From: ksmithson Date: Wed, 24 Oct 2018 11:23:32 -0700 Subject: [PATCH 1/9] Add deprecated directive to arguments and input values --- src/type/__tests__/introspection-test.js | 57 +++++++++++++++++++-- src/type/definition.js | 13 +++++ src/type/directives.js | 7 ++- src/type/introspection.js | 39 ++++++++++++-- src/utilities/__tests__/printSchema-test.js | 10 ++-- 5 files changed, 116 insertions(+), 10 deletions(-) diff --git a/src/type/__tests__/introspection-test.js b/src/type/__tests__/introspection-test.js index 842072de1b..7d07200f10 100644 --- a/src/type/__tests__/introspection-test.js +++ b/src/type/__tests__/introspection-test.js @@ -342,7 +342,17 @@ describe('Introspection', () => { }, { name: 'inputFields', - args: [], + args: [ + { + name: 'includeDeprecated', + type: { + kind: 'SCALAR', + name: 'Boolean', + ofType: null, + }, + defaultValue: 'false', + }, + ], type: { kind: 'LIST', name: null, @@ -460,7 +470,17 @@ describe('Introspection', () => { }, { name: 'args', - args: [], + args: [ + { + name: 'includeDeprecated', + type: { + kind: 'SCALAR', + name: 'Boolean', + ofType: null, + }, + defaultValue: 'false', + }, + ], type: { kind: 'NON_NULL', name: null, @@ -585,6 +605,32 @@ describe('Introspection', () => { isDeprecated: false, deprecationReason: null, }, + { + name: 'isDeprecated', + args: [], + type: { + kind: 'NON_NULL', + name: null, + ofType: { + kind: 'SCALAR', + name: 'Boolean', + ofType: null, + }, + }, + isDeprecated: false, + deprecationReason: null, + }, + { + name: 'deprecationReason', + args: [], + type: { + kind: 'SCALAR', + name: 'String', + ofType: null, + }, + isDeprecated: false, + deprecationReason: null, + }, ], inputFields: null, interfaces: [], @@ -903,7 +949,12 @@ describe('Introspection', () => { { name: 'deprecated', isRepeatable: false, - locations: ['FIELD_DEFINITION', 'ENUM_VALUE'], + locations: [ + 'FIELD_DEFINITION', + 'ENUM_VALUE', + 'ARGUMENT_DEFINITION', + 'INPUT_FIELD_DEFINITION', + ], args: [ { defaultValue: '"No longer supported"', diff --git a/src/type/definition.js b/src/type/definition.js index ab94313263..877217c7f8 100644 --- a/src/type/definition.js +++ b/src/type/definition.js @@ -978,6 +978,7 @@ export type GraphQLArgumentConfig = {| type: GraphQLInputType, defaultValue?: mixed, extensions?: ?ReadOnlyObjMapLike, + deprecationReason?: ?string, astNode?: ?InputValueDefinitionNode, |}; @@ -1007,6 +1008,8 @@ export type GraphQLArgument = {| description: ?string, type: GraphQLInputType, defaultValue: mixed, + isDeprecated?:boolean, + deprecationReason?: ? string, extensions: ?ReadOnlyObjMap, astNode: ?InputValueDefinitionNode, |}; @@ -1566,13 +1569,20 @@ function defineInputFieldMap( isPlainObj(fieldMap), `${config.name} fields must be an object with field names as keys or a function which returns such an object.`, ); + return mapValue(fieldMap, (fieldConfig, fieldName) => { devAssert( !('resolve' in fieldConfig), `${config.name}.${fieldName} field has a resolve property, but Input Types cannot define resolvers.`, ); + devAssert( + !fieldConfig.hasOwnProperty('isDeprecated'), + `${config.name}.${fieldName} should provide "deprecationReason" `+ + 'instead of "isDeprecated".', + ); return { + isDeprecated: Boolean(fieldConfig.deprecationReason), name: fieldName, description: fieldConfig.description, type: fieldConfig.type, @@ -1596,6 +1606,7 @@ export type GraphQLInputFieldConfig = {| description?: ?string, type: GraphQLInputType, defaultValue?: mixed, + deprecationReason?: ? string, extensions?: ?ReadOnlyObjMapLike, astNode?: ?InputValueDefinitionNode, |}; @@ -1607,6 +1618,8 @@ export type GraphQLInputField = {| description: ?string, type: GraphQLInputType, defaultValue: mixed, + isDeprecated?:boolean, + deprecationReason?: ?string, extensions: ?ReadOnlyObjMap, astNode: ?InputValueDefinitionNode, |}; diff --git a/src/type/directives.js b/src/type/directives.js index b5074b8266..f956526063 100644 --- a/src/type/directives.js +++ b/src/type/directives.js @@ -180,7 +180,12 @@ export const DEFAULT_DEPRECATION_REASON = 'No longer supported'; export const GraphQLDeprecatedDirective = new GraphQLDirective({ name: 'deprecated', description: 'Marks an element of a GraphQL schema as no longer supported.', - locations: [DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.ENUM_VALUE], + locations: [ + DirectiveLocation.FIELD_DEFINITION, + DirectiveLocation.ENUM_VALUE, + DirectiveLocation.ARGUMENT_DEFINITION, + DirectiveLocation.INPUT_FIELD_DEFINITION, + ], args: { reason: { type: GraphQLString, diff --git a/src/type/introspection.js b/src/type/introspection.js index be86f4e5ac..83a47cf450 100644 --- a/src/type/introspection.js +++ b/src/type/introspection.js @@ -293,9 +293,19 @@ export const __Type = new GraphQLObjectType({ }, inputFields: { type: GraphQLList(GraphQLNonNull(__InputValue)), - resolve(type) { + args: { + includeDeprecated: { + type: GraphQLBoolean, + defaultValue: false, + }, + }, + resolve(type, {includeDeprecated}) { if (isInputObjectType(type)) { - return objectValues(type.getFields()); + let values=objectValues(type.getFields()); + if (!includeDeprecated) { + values=values.filter(value => !value.deprecationReason); + } + return values; } }, }, @@ -323,7 +333,22 @@ export const __Field = new GraphQLObjectType({ }, args: { type: GraphQLNonNull(GraphQLList(GraphQLNonNull(__InputValue))), - resolve: (field) => field.args, + args: { + includeDeprecated: { + type: GraphQLBoolean, + defaultValue: false, + }, + }, + // resolve: field => field.args || [], + resolve(field, {includeDeprecated}) { + let args=field.args||[]; + + if (!includeDeprecated) { + args=args.filter(arg => !arg.deprecationReason); + } + + return args; + }, }, type: { type: GraphQLNonNull(__Type), @@ -368,6 +393,14 @@ export const __InputValue = new GraphQLObjectType({ return valueAST ? print(valueAST) : null; }, }, + isDeprecated: { + type: GraphQLNonNull(GraphQLBoolean), + resolve: obj => obj.isDeprecated, + }, + deprecationReason: { + type: GraphQLString, + resolve: obj => obj.deprecationReason, + }, }: GraphQLFieldConfigMap), }); diff --git a/src/utilities/__tests__/printSchema-test.js b/src/utilities/__tests__/printSchema-test.js index 6b5bbd7414..145c9aa67f 100644 --- a/src/utilities/__tests__/printSchema-test.js +++ b/src/utilities/__tests__/printSchema-test.js @@ -629,7 +629,7 @@ describe('Type System Printer', () => { 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/). """ reason: String = "No longer supported" - ) on FIELD_DEFINITION | ENUM_VALUE + ) on FIELD_DEFINITION | ENUM_VALUE | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION """Exposes a URL that specifies the behaviour of this scalar.""" directive @specifiedBy( @@ -720,7 +720,7 @@ describe('Type System Printer', () => { type __Field { name: String! description: String - args: [__InputValue!]! + args(includeDeprecated: Boolean = false): [__InputValue!]! type: __Type! isDeprecated: Boolean! deprecationReason: String @@ -738,6 +738,8 @@ describe('Type System Printer', () => { A GraphQL-formatted string representing the default value for this input value. """ defaultValue: String + isDeprecated: Boolean! + deprecationReason: String } """ @@ -850,7 +852,7 @@ describe('Type System Printer', () => { 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/). reason: String = "No longer supported" - ) on FIELD_DEFINITION | ENUM_VALUE + ) on FIELD_DEFINITION | ENUM_VALUE | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION # Exposes a URL that specifies the behaviour of this scalar. directive @specifiedBy( @@ -939,6 +941,8 @@ describe('Type System Printer', () => { # A GraphQL-formatted string representing the default value for this input value. defaultValue: String + isDeprecated: Boolean! + deprecationReason: String } # One possible value for a given Enum. Enum values are unique values, not a placeholder for a string or numeric value. However an Enum value is returned in a JSON response as a string. From 69640fa86b5d4c6e4af28a49c3672785822cf2cf Mon Sep 17 00:00:00 2001 From: ksmithson Date: Wed, 24 Oct 2018 14:52:24 -0700 Subject: [PATCH 2/9] Add test and support for deprecated args --- src/type/definition.js | 2 ++ src/utilities/__tests__/buildASTSchema-test.js | 14 ++++++++++++++ src/utilities/extendSchema.js | 1 + src/utilities/printSchema.js | 2 +- 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/type/definition.js b/src/type/definition.js index 877217c7f8..150b7ee370 100644 --- a/src/type/definition.js +++ b/src/type/definition.js @@ -854,6 +854,8 @@ function defineFieldMap( type: argConfig.type, defaultValue: argConfig.defaultValue, extensions: argConfig.extensions && toObjMap(argConfig.extensions), + isDeprecated: Boolean(argConfig.deprecationReason), + deprecationReason: argConfig.deprecationReason, astNode: argConfig.astNode, })); diff --git a/src/utilities/__tests__/buildASTSchema-test.js b/src/utilities/__tests__/buildASTSchema-test.js index 2bb6a32e21..be01557cd6 100644 --- a/src/utilities/__tests__/buildASTSchema-test.js +++ b/src/utilities/__tests__/buildASTSchema-test.js @@ -746,6 +746,8 @@ describe('Schema Builder', () => { field1: String @deprecated field2: Int @deprecated(reason: "Because I said so") enum: MyEnum + field3(oldArg: String @deprecated, arg: String): String + field4(oldArg: String @deprecated(reason: "why not?"), arg: String): String } `; expect(cycleSDL(sdl)).to.equal(sdl); @@ -817,6 +819,18 @@ describe('Schema Builder', () => { `); expect(printAllASTNodes(someScalar)).to.equal(scalarSDL); + const rootFields=assertObjectType(schema.getType('Query')).getFields(); + + expect(rootFields.field2.isDeprecated).to.equal(true); + expect(rootFields.field2.deprecationReason).to.equal('Because I said so'); + + const field3OldArg = rootFields.field3.args[0]; + expect(field3OldArg.isDeprecated).to.equal(true); + expect(field3OldArg.deprecationReason).to.equal('No longer supported'); + + const field4OldArg = rootFields.field4.args[0]; + expect(field4OldArg.isDeprecated).to.equal(true); + expect(field4OldArg.deprecationReason).to.equal('why not?'); }); it('Correctly extend object type', () => { diff --git a/src/utilities/extendSchema.js b/src/utilities/extendSchema.js index 2950441576..5596ee7c44 100644 --- a/src/utilities/extendSchema.js +++ b/src/utilities/extendSchema.js @@ -538,6 +538,7 @@ export function extendSchemaImpl( type, description: getDescription(field, options), defaultValue: valueFromAST(field.defaultValue, type), + deprecationReason: getDeprecationReason(field), astNode: field, }; } diff --git a/src/utilities/printSchema.js b/src/utilities/printSchema.js index 73e6afb277..d40a529259 100644 --- a/src/utilities/printSchema.js +++ b/src/utilities/printSchema.js @@ -301,7 +301,7 @@ function printInputValue(arg) { if (defaultAST) { argDecl += ` = ${print(defaultAST)}`; } - return argDecl; + return argDecl + printDeprecated(arg); } function printDirective(directive, options) { From a6e9130c42bbbdbdbb9e43d2df7e7bcb52a65c99 Mon Sep 17 00:00:00 2001 From: ksmithson Date: Thu, 25 Oct 2018 07:00:12 -0700 Subject: [PATCH 3/9] Add test for input arguments being deprecated --- .../__tests__/buildASTSchema-test.js | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/utilities/__tests__/buildASTSchema-test.js b/src/utilities/__tests__/buildASTSchema-test.js index be01557cd6..5c84e8e375 100644 --- a/src/utilities/__tests__/buildASTSchema-test.js +++ b/src/utilities/__tests__/buildASTSchema-test.js @@ -742,12 +742,19 @@ describe('Schema Builder', () => { OTHER_VALUE @deprecated(reason: "Terrible reasons") } + input MyInput { + oldInput: String @deprecated + otherInput: String @deprecated(reason: "Use newInput") + newInput: String + } + type Query { field1: String @deprecated field2: Int @deprecated(reason: "Because I said so") enum: MyEnum field3(oldArg: String @deprecated, arg: String): String field4(oldArg: String @deprecated(reason: "why not?"), arg: String): String + field5(arg: MyInput): String } `; expect(cycleSDL(sdl)).to.equal(sdl); @@ -831,6 +838,20 @@ describe('Schema Builder', () => { const field4OldArg = rootFields.field4.args[0]; expect(field4OldArg.isDeprecated).to.equal(true); expect(field4OldArg.deprecationReason).to.equal('why not?'); + + const myInput = schema.getType('MyInput'); + const inputFields = myInput.getFields(); + + const newInput = inputFields.newInput; + expect(newInput.isDeprecated).to.equal(false); + + const oldInput = inputFields.oldInput; + expect(oldInput.isDeprecated).to.equal(true); + expect(oldInput.deprecationReason).to.equal('No longer supported'); + + const otherInput = inputFields.otherInput; + expect(otherInput.isDeprecated).to.equal(true); + expect(otherInput.deprecationReason).to.equal('Use newInput'); }); it('Correctly extend object type', () => { From c9e500b53aae4bc5e69183e80903230cd8d06076 Mon Sep 17 00:00:00 2001 From: jonathan schatz Date: Tue, 11 Feb 2020 10:33:14 -0800 Subject: [PATCH 4/9] eslint/prettier fixes --- src/type/definition.js | 14 +++++++------- src/type/introspection.js | 12 ++++++------ src/utilities/__tests__/buildASTSchema-test.js | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/type/definition.js b/src/type/definition.js index 150b7ee370..4efd58b5cc 100644 --- a/src/type/definition.js +++ b/src/type/definition.js @@ -1010,8 +1010,8 @@ export type GraphQLArgument = {| description: ?string, type: GraphQLInputType, defaultValue: mixed, - isDeprecated?:boolean, - deprecationReason?: ? string, + isDeprecated?: boolean, + deprecationReason?: ?string, extensions: ?ReadOnlyObjMap, astNode: ?InputValueDefinitionNode, |}; @@ -1578,9 +1578,9 @@ function defineInputFieldMap( `${config.name}.${fieldName} field has a resolve property, but Input Types cannot define resolvers.`, ); devAssert( - !fieldConfig.hasOwnProperty('isDeprecated'), - `${config.name}.${fieldName} should provide "deprecationReason" `+ - 'instead of "isDeprecated".', + !('isDeprecated' in fieldConfig), + `${config.name}.${fieldName} should provide "deprecationReason" ` + + 'instead of "isDeprecated".', ); return { @@ -1608,7 +1608,7 @@ export type GraphQLInputFieldConfig = {| description?: ?string, type: GraphQLInputType, defaultValue?: mixed, - deprecationReason?: ? string, + deprecationReason?: ?string, extensions?: ?ReadOnlyObjMapLike, astNode?: ?InputValueDefinitionNode, |}; @@ -1620,7 +1620,7 @@ export type GraphQLInputField = {| description: ?string, type: GraphQLInputType, defaultValue: mixed, - isDeprecated?:boolean, + isDeprecated?: boolean, deprecationReason?: ?string, extensions: ?ReadOnlyObjMap, astNode: ?InputValueDefinitionNode, diff --git a/src/type/introspection.js b/src/type/introspection.js index 83a47cf450..14884947c7 100644 --- a/src/type/introspection.js +++ b/src/type/introspection.js @@ -299,11 +299,11 @@ export const __Type = new GraphQLObjectType({ defaultValue: false, }, }, - resolve(type, {includeDeprecated}) { + resolve(type, { includeDeprecated }) { if (isInputObjectType(type)) { - let values=objectValues(type.getFields()); + let values = objectValues(type.getFields()); if (!includeDeprecated) { - values=values.filter(value => !value.deprecationReason); + values = values.filter(value => !value.deprecationReason); } return values; } @@ -340,11 +340,11 @@ export const __Field = new GraphQLObjectType({ }, }, // resolve: field => field.args || [], - resolve(field, {includeDeprecated}) { - let args=field.args||[]; + resolve(field, { includeDeprecated }) { + let args = field.args || []; if (!includeDeprecated) { - args=args.filter(arg => !arg.deprecationReason); + args = args.filter(arg => !arg.deprecationReason); } return args; diff --git a/src/utilities/__tests__/buildASTSchema-test.js b/src/utilities/__tests__/buildASTSchema-test.js index 5c84e8e375..ae296c3f73 100644 --- a/src/utilities/__tests__/buildASTSchema-test.js +++ b/src/utilities/__tests__/buildASTSchema-test.js @@ -826,7 +826,7 @@ describe('Schema Builder', () => { `); expect(printAllASTNodes(someScalar)).to.equal(scalarSDL); - const rootFields=assertObjectType(schema.getType('Query')).getFields(); + const rootFields = assertObjectType(schema.getType('Query')).getFields(); expect(rootFields.field2.isDeprecated).to.equal(true); expect(rootFields.field2.deprecationReason).to.equal('Because I said so'); From 6b48310b6646c36c20cc5779a46e4cc53a551202 Mon Sep 17 00:00:00 2001 From: jonathan schatz Date: Tue, 11 Feb 2020 11:15:22 -0800 Subject: [PATCH 5/9] more cleanup --- src/type/__tests__/definition-test.js | 4 ++ src/type/definition.d.ts | 2 + src/type/definition.js | 4 +- .../__tests__/buildASTSchema-test.js | 49 +++++++++---------- src/utilities/__tests__/printSchema-test.js | 6 +-- src/utilities/extendSchema.js | 5 +- src/utilities/printSchema.js | 2 +- 7 files changed, 39 insertions(+), 33 deletions(-) diff --git a/src/type/__tests__/definition-test.js b/src/type/__tests__/definition-test.js index c54b589611..7827ea39ef 100644 --- a/src/type/__tests__/definition-test.js +++ b/src/type/__tests__/definition-test.js @@ -281,6 +281,8 @@ describe('Type System: Objects', () => { description: undefined, type: ScalarType, defaultValue: undefined, + deprecationReason: undefined, + isDeprecated: false, extensions: undefined, astNode: undefined, }, @@ -772,6 +774,7 @@ describe('Type System: Input Objects', () => { description: undefined, type: ScalarType, defaultValue: undefined, + isDeprecated: false, extensions: undefined, astNode: undefined, }, @@ -792,6 +795,7 @@ describe('Type System: Input Objects', () => { type: ScalarType, defaultValue: undefined, extensions: undefined, + isDeprecated: false, astNode: undefined, }, }); diff --git a/src/type/definition.d.ts b/src/type/definition.d.ts index aa3177a69d..e570933b35 100644 --- a/src/type/definition.d.ts +++ b/src/type/definition.d.ts @@ -546,6 +546,8 @@ export interface GraphQLArgumentConfig { description?: Maybe; type: GraphQLInputType; defaultValue?: any; + isDeprecated?: boolean; + deprecationReason?: Maybe; extensions?: Maybe>; astNode?: Maybe; } diff --git a/src/type/definition.js b/src/type/definition.js index 4efd58b5cc..bac1c17407 100644 --- a/src/type/definition.js +++ b/src/type/definition.js @@ -854,7 +854,7 @@ function defineFieldMap( type: argConfig.type, defaultValue: argConfig.defaultValue, extensions: argConfig.extensions && toObjMap(argConfig.extensions), - isDeprecated: Boolean(argConfig.deprecationReason), + isDeprecated: argConfig.deprecationReason != null, deprecationReason: argConfig.deprecationReason, astNode: argConfig.astNode, })); @@ -1584,7 +1584,7 @@ function defineInputFieldMap( ); return { - isDeprecated: Boolean(fieldConfig.deprecationReason), + isDeprecated: fieldConfig.deprecationReason != null, name: fieldName, description: fieldConfig.description, type: fieldConfig.type, diff --git a/src/utilities/__tests__/buildASTSchema-test.js b/src/utilities/__tests__/buildASTSchema-test.js index ae296c3f73..8b4804bc58 100644 --- a/src/utilities/__tests__/buildASTSchema-test.js +++ b/src/utilities/__tests__/buildASTSchema-test.js @@ -787,6 +787,29 @@ describe('Schema Builder', () => { isDeprecated: true, deprecationReason: 'Because I said so', }); + + const inputFields = assertInputObjectType( + schema.getType('MyInput'), + ).getFields(); + + const newInput = inputFields.newInput; + expect(newInput.isDeprecated).to.equal(false); + + const oldInput = inputFields.oldInput; + expect(oldInput.isDeprecated).to.equal(true); + expect(oldInput.deprecationReason).to.equal('No longer supported'); + + const otherInput = inputFields.otherInput; + expect(otherInput.isDeprecated).to.equal(true); + expect(otherInput.deprecationReason).to.equal('Use newInput'); + + const field3OldArg = rootFields.field3.args[0]; + expect(field3OldArg.isDeprecated).to.equal(true); + expect(field3OldArg.deprecationReason).to.equal('No longer supported'); + + const field4OldArg = rootFields.field4.args[0]; + expect(field4OldArg.isDeprecated).to.equal(true); + expect(field4OldArg.deprecationReason).to.equal('why not?'); }); it('Supports @specifiedBy', () => { @@ -826,32 +849,6 @@ describe('Schema Builder', () => { `); expect(printAllASTNodes(someScalar)).to.equal(scalarSDL); - const rootFields = assertObjectType(schema.getType('Query')).getFields(); - - expect(rootFields.field2.isDeprecated).to.equal(true); - expect(rootFields.field2.deprecationReason).to.equal('Because I said so'); - - const field3OldArg = rootFields.field3.args[0]; - expect(field3OldArg.isDeprecated).to.equal(true); - expect(field3OldArg.deprecationReason).to.equal('No longer supported'); - - const field4OldArg = rootFields.field4.args[0]; - expect(field4OldArg.isDeprecated).to.equal(true); - expect(field4OldArg.deprecationReason).to.equal('why not?'); - - const myInput = schema.getType('MyInput'); - const inputFields = myInput.getFields(); - - const newInput = inputFields.newInput; - expect(newInput.isDeprecated).to.equal(false); - - const oldInput = inputFields.oldInput; - expect(oldInput.isDeprecated).to.equal(true); - expect(oldInput.deprecationReason).to.equal('No longer supported'); - - const otherInput = inputFields.otherInput; - expect(otherInput.isDeprecated).to.equal(true); - expect(otherInput.deprecationReason).to.equal('Use newInput'); }); it('Correctly extend object type', () => { diff --git a/src/utilities/__tests__/printSchema-test.js b/src/utilities/__tests__/printSchema-test.js index 145c9aa67f..743df4b993 100644 --- a/src/utilities/__tests__/printSchema-test.js +++ b/src/utilities/__tests__/printSchema-test.js @@ -677,7 +677,7 @@ describe('Type System Printer', () => { interfaces: [__Type!] possibleTypes: [__Type!] enumValues(includeDeprecated: Boolean = false): [__EnumValue!] - inputFields: [__InputValue!] + inputFields(includeDeprecated: Boolean = false): [__InputValue!] ofType: __Type } @@ -892,7 +892,7 @@ describe('Type System Printer', () => { interfaces: [__Type!] possibleTypes: [__Type!] enumValues(includeDeprecated: Boolean = false): [__EnumValue!] - inputFields: [__InputValue!] + inputFields(includeDeprecated: Boolean = false): [__InputValue!] ofType: __Type } @@ -927,7 +927,7 @@ describe('Type System Printer', () => { type __Field { name: String! description: String - args: [__InputValue!]! + args(includeDeprecated: Boolean = false): [__InputValue!]! type: __Type! isDeprecated: Boolean! deprecationReason: String diff --git a/src/utilities/extendSchema.js b/src/utilities/extendSchema.js index 5596ee7c44..45253f094d 100644 --- a/src/utilities/extendSchema.js +++ b/src/utilities/extendSchema.js @@ -707,7 +707,10 @@ const stdTypeMap = keyMap( * deprecation reason. */ function getDeprecationReason( - node: EnumValueDefinitionNode | FieldDefinitionNode, + node: + | EnumValueDefinitionNode + | FieldDefinitionNode + | InputValueDefinitionNode, ): ?string { const deprecated = getDirectiveValues(GraphQLDeprecatedDirective, node); return (deprecated?.reason: any); diff --git a/src/utilities/printSchema.js b/src/utilities/printSchema.js index d40a529259..7a8687ba72 100644 --- a/src/utilities/printSchema.js +++ b/src/utilities/printSchema.js @@ -317,7 +317,7 @@ function printDirective(directive, options) { } function printDeprecated(fieldOrEnumVal) { - if (!fieldOrEnumVal.isDeprecated) { + if (fieldOrEnumVal.deprecationReason == null) { return ''; } const reason = fieldOrEnumVal.deprecationReason; From 19834d282c82b48e8d15fb1fc96e4d9524b1790a Mon Sep 17 00:00:00 2001 From: jonathan schatz Date: Tue, 11 Feb 2020 12:03:13 -0800 Subject: [PATCH 6/9] more wip --- src/type/__tests__/definition-test.js | 2 ++ src/type/definition.js | 6 +++- .../__tests__/buildASTSchema-test.js | 28 +++++++++++++------ 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/type/__tests__/definition-test.js b/src/type/__tests__/definition-test.js index 7827ea39ef..f4f8c5f3d8 100644 --- a/src/type/__tests__/definition-test.js +++ b/src/type/__tests__/definition-test.js @@ -774,6 +774,7 @@ describe('Type System: Input Objects', () => { description: undefined, type: ScalarType, defaultValue: undefined, + deprecationReason: undefined, isDeprecated: false, extensions: undefined, astNode: undefined, @@ -796,6 +797,7 @@ describe('Type System: Input Objects', () => { defaultValue: undefined, extensions: undefined, isDeprecated: false, + deprecationReason: undefined, astNode: undefined, }, }); diff --git a/src/type/definition.js b/src/type/definition.js index bac1c17407..a87315b4b8 100644 --- a/src/type/definition.js +++ b/src/type/definition.js @@ -904,6 +904,8 @@ export function argsToArgsConfig( description: arg.description, type: arg.type, defaultValue: arg.defaultValue, + deprecationReason: arg.deprecationReason, + isDeprecated: arg.deprecationReason != null, extensions: arg.extensions, astNode: arg.astNode, }), @@ -981,6 +983,7 @@ export type GraphQLArgumentConfig = {| defaultValue?: mixed, extensions?: ?ReadOnlyObjMapLike, deprecationReason?: ?string, + isDeprecated?: boolean, astNode?: ?InputValueDefinitionNode, |}; @@ -1584,11 +1587,12 @@ function defineInputFieldMap( ); return { - isDeprecated: fieldConfig.deprecationReason != null, name: fieldName, description: fieldConfig.description, type: fieldConfig.type, defaultValue: fieldConfig.defaultValue, + deprecationReason: fieldConfig.deprecationReason, + isDeprecated: fieldConfig.deprecationReason != null, extensions: fieldConfig.extensions && toObjMap(fieldConfig.extensions), astNode: fieldConfig.astNode, }; diff --git a/src/utilities/__tests__/buildASTSchema-test.js b/src/utilities/__tests__/buildASTSchema-test.js index 8b4804bc58..13aa8b1cbf 100644 --- a/src/utilities/__tests__/buildASTSchema-test.js +++ b/src/utilities/__tests__/buildASTSchema-test.js @@ -793,23 +793,33 @@ describe('Schema Builder', () => { ).getFields(); const newInput = inputFields.newInput; - expect(newInput.isDeprecated).to.equal(false); + expect(newInput).to.include({ + isDeprecated: false, + }); const oldInput = inputFields.oldInput; - expect(oldInput.isDeprecated).to.equal(true); - expect(oldInput.deprecationReason).to.equal('No longer supported'); + expect(oldInput).to.include({ + isDeprecated: true, + deprecationReason: 'No longer supported', + }); const otherInput = inputFields.otherInput; - expect(otherInput.isDeprecated).to.equal(true); - expect(otherInput.deprecationReason).to.equal('Use newInput'); + expect(otherInput).to.include({ + isDeprecated: true, + deprecationReason: 'Use newInput', + }); const field3OldArg = rootFields.field3.args[0]; - expect(field3OldArg.isDeprecated).to.equal(true); - expect(field3OldArg.deprecationReason).to.equal('No longer supported'); + expect(field3OldArg).to.include({ + isDeprecated: true, + deprecationReason: 'No longer supported', + }); const field4OldArg = rootFields.field4.args[0]; - expect(field4OldArg.isDeprecated).to.equal(true); - expect(field4OldArg.deprecationReason).to.equal('why not?'); + expect(field4OldArg).to.include({ + isDeprecated: true, + deprecationReason: 'Why not?', + }); }); it('Supports @specifiedBy', () => { From c5af3375db2c1b113f6cf1bdef0afaa5e87de097 Mon Sep 17 00:00:00 2001 From: jonathan schatz Date: Wed, 26 Feb 2020 12:29:23 -0800 Subject: [PATCH 7/9] fix last spec --- src/utilities/__tests__/buildASTSchema-test.js | 2 +- src/utilities/extendSchema.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utilities/__tests__/buildASTSchema-test.js b/src/utilities/__tests__/buildASTSchema-test.js index 13aa8b1cbf..7d95c2715a 100644 --- a/src/utilities/__tests__/buildASTSchema-test.js +++ b/src/utilities/__tests__/buildASTSchema-test.js @@ -753,7 +753,7 @@ describe('Schema Builder', () => { field2: Int @deprecated(reason: "Because I said so") enum: MyEnum field3(oldArg: String @deprecated, arg: String): String - field4(oldArg: String @deprecated(reason: "why not?"), arg: String): String + field4(oldArg: String @deprecated(reason: "Why not?"), arg: String): String field5(arg: MyInput): String } `; diff --git a/src/utilities/extendSchema.js b/src/utilities/extendSchema.js index 45253f094d..3bb6a088e6 100644 --- a/src/utilities/extendSchema.js +++ b/src/utilities/extendSchema.js @@ -512,6 +512,7 @@ export function extendSchemaImpl( type, description: getDescription(arg, options), defaultValue: valueFromAST(arg.defaultValue, type), + deprecationReason: getDeprecationReason(arg), astNode: arg, }; } From 171aec5dbd817220d8f460c0a7e70cb4b5f6e6a3 Mon Sep 17 00:00:00 2001 From: Ivan Goncharov Date: Thu, 2 Jul 2020 19:11:37 +0300 Subject: [PATCH 8/9] Prettier fixes --- src/type/introspection.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/type/introspection.js b/src/type/introspection.js index 14884947c7..02a21fbc95 100644 --- a/src/type/introspection.js +++ b/src/type/introspection.js @@ -303,7 +303,7 @@ export const __Type = new GraphQLObjectType({ if (isInputObjectType(type)) { let values = objectValues(type.getFields()); if (!includeDeprecated) { - values = values.filter(value => !value.deprecationReason); + values = values.filter((value) => !value.deprecationReason); } return values; } @@ -344,7 +344,7 @@ export const __Field = new GraphQLObjectType({ let args = field.args || []; if (!includeDeprecated) { - args = args.filter(arg => !arg.deprecationReason); + args = args.filter((arg) => !arg.deprecationReason); } return args; @@ -395,11 +395,11 @@ export const __InputValue = new GraphQLObjectType({ }, isDeprecated: { type: GraphQLNonNull(GraphQLBoolean), - resolve: obj => obj.isDeprecated, + resolve: (obj) => obj.isDeprecated, }, deprecationReason: { type: GraphQLString, - resolve: obj => obj.deprecationReason, + resolve: (obj) => obj.deprecationReason, }, }: GraphQLFieldConfigMap), }); From 08374e763ee2b05a17ae686ccb3d4d17be26ecb5 Mon Sep 17 00:00:00 2001 From: Ivan Goncharov Date: Thu, 2 Jul 2020 19:14:09 +0300 Subject: [PATCH 9/9] Various fixes --- src/type/__tests__/definition-test.js | 4 +- src/type/__tests__/directive-test.js | 4 ++ src/type/__tests__/introspection-test.js | 2 +- src/type/__tests__/predicate-test.js | 4 ++ src/type/definition.d.ts | 6 ++- src/type/definition.js | 42 +++++++++++---------- src/type/directives.js | 27 ++++++++----- src/type/introspection.js | 2 + src/utilities/__tests__/printSchema-test.js | 4 +- src/utilities/printSchema.js | 11 +++--- 10 files changed, 66 insertions(+), 40 deletions(-) diff --git a/src/type/__tests__/definition-test.js b/src/type/__tests__/definition-test.js index f4f8c5f3d8..a86b2b9d4c 100644 --- a/src/type/__tests__/definition-test.js +++ b/src/type/__tests__/definition-test.js @@ -281,8 +281,8 @@ describe('Type System: Objects', () => { description: undefined, type: ScalarType, defaultValue: undefined, - deprecationReason: undefined, isDeprecated: false, + deprecationReason: undefined, extensions: undefined, astNode: undefined, }, @@ -774,8 +774,8 @@ describe('Type System: Input Objects', () => { description: undefined, type: ScalarType, defaultValue: undefined, - deprecationReason: undefined, isDeprecated: false, + deprecationReason: undefined, extensions: undefined, astNode: undefined, }, diff --git a/src/type/__tests__/directive-test.js b/src/type/__tests__/directive-test.js index 178ca4db91..13099bc63c 100644 --- a/src/type/__tests__/directive-test.js +++ b/src/type/__tests__/directive-test.js @@ -39,6 +39,8 @@ describe('Type System: Directive', () => { description: undefined, type: GraphQLString, defaultValue: undefined, + isDeprecated: false, + deprecationReason: undefined, extensions: undefined, astNode: undefined, }, @@ -47,6 +49,8 @@ describe('Type System: Directive', () => { description: undefined, type: GraphQLInt, defaultValue: undefined, + isDeprecated: false, + deprecationReason: undefined, extensions: undefined, astNode: undefined, }, diff --git a/src/type/__tests__/introspection-test.js b/src/type/__tests__/introspection-test.js index 7d07200f10..1d731027b9 100644 --- a/src/type/__tests__/introspection-test.js +++ b/src/type/__tests__/introspection-test.js @@ -951,8 +951,8 @@ describe('Introspection', () => { isRepeatable: false, locations: [ 'FIELD_DEFINITION', - 'ENUM_VALUE', 'ARGUMENT_DEFINITION', + 'ENUM_VALUE', 'INPUT_FIELD_DEFINITION', ], args: [ diff --git a/src/type/__tests__/predicate-test.js b/src/type/__tests__/predicate-test.js index 40dcca7ee8..7f09752254 100644 --- a/src/type/__tests__/predicate-test.js +++ b/src/type/__tests__/predicate-test.js @@ -549,6 +549,8 @@ describe('Type predicates', () => { name: 'someArg', description: undefined, defaultValue: undefined, + isDeprecated: false, + deprecationReason: null, extensions: undefined, astNode: undefined, ...config, @@ -593,6 +595,8 @@ describe('Type predicates', () => { name: 'someInputField', description: undefined, defaultValue: undefined, + isDeprecated: false, + deprecationReason: null, extensions: undefined, astNode: undefined, ...config, diff --git a/src/type/definition.d.ts b/src/type/definition.d.ts index e570933b35..c4626e8a6b 100644 --- a/src/type/definition.d.ts +++ b/src/type/definition.d.ts @@ -546,7 +546,6 @@ export interface GraphQLArgumentConfig { description?: Maybe; type: GraphQLInputType; defaultValue?: any; - isDeprecated?: boolean; deprecationReason?: Maybe; extensions?: Maybe>; astNode?: Maybe; @@ -578,6 +577,8 @@ export interface GraphQLArgument { description: Maybe; type: GraphQLInputType; defaultValue: any; + isDeprecated: boolean; + deprecationReason: Maybe; extensions: Maybe>; astNode: Maybe; } @@ -917,6 +918,7 @@ export interface GraphQLInputFieldConfig { description?: Maybe; type: GraphQLInputType; defaultValue?: any; + deprecationReason?: Maybe; extensions?: Maybe>; astNode?: Maybe; } @@ -930,6 +932,8 @@ export interface GraphQLInputField { description?: Maybe; type: GraphQLInputType; defaultValue?: any; + isDeprecated: boolean; + deprecationReason: Maybe; extensions: Maybe>; astNode?: Maybe; } diff --git a/src/type/definition.js b/src/type/definition.js index a87315b4b8..27c0902e4d 100644 --- a/src/type/definition.js +++ b/src/type/definition.js @@ -848,16 +848,23 @@ function defineFieldMap( `${config.name}.${fieldName} args must be an object with argument names as keys.`, ); - const args = objectEntries(argsConfig).map(([argName, argConfig]) => ({ - name: argName, - description: argConfig.description, - type: argConfig.type, - defaultValue: argConfig.defaultValue, - extensions: argConfig.extensions && toObjMap(argConfig.extensions), - isDeprecated: argConfig.deprecationReason != null, - deprecationReason: argConfig.deprecationReason, - astNode: argConfig.astNode, - })); + const args = objectEntries(argsConfig).map(([argName, argConfig]) => { + devAssert( + !('isDeprecated' in argConfig), + `${config.name}.${fieldName}.${argName} should provide "deprecationReason" instead of "isDeprecated".`, + ); + + return { + name: argName, + description: argConfig.description, + type: argConfig.type, + defaultValue: argConfig.defaultValue, + isDeprecated: argConfig.deprecationReason != null, + deprecationReason: argConfig.deprecationReason, + extensions: argConfig.extensions && toObjMap(argConfig.extensions), + astNode: argConfig.astNode, + }; + }); return { name: fieldName, @@ -905,7 +912,6 @@ export function argsToArgsConfig( type: arg.type, defaultValue: arg.defaultValue, deprecationReason: arg.deprecationReason, - isDeprecated: arg.deprecationReason != null, extensions: arg.extensions, astNode: arg.astNode, }), @@ -983,7 +989,6 @@ export type GraphQLArgumentConfig = {| defaultValue?: mixed, extensions?: ?ReadOnlyObjMapLike, deprecationReason?: ?string, - isDeprecated?: boolean, astNode?: ?InputValueDefinitionNode, |}; @@ -1013,8 +1018,8 @@ export type GraphQLArgument = {| description: ?string, type: GraphQLInputType, defaultValue: mixed, - isDeprecated?: boolean, - deprecationReason?: ?string, + isDeprecated: boolean, + deprecationReason: ?string, extensions: ?ReadOnlyObjMap, astNode: ?InputValueDefinitionNode, |}; @@ -1582,8 +1587,7 @@ function defineInputFieldMap( ); devAssert( !('isDeprecated' in fieldConfig), - `${config.name}.${fieldName} should provide "deprecationReason" ` + - 'instead of "isDeprecated".', + `${config.name}.${fieldName} should provide "deprecationReason" instead of "isDeprecated".`, ); return { @@ -1591,8 +1595,8 @@ function defineInputFieldMap( description: fieldConfig.description, type: fieldConfig.type, defaultValue: fieldConfig.defaultValue, - deprecationReason: fieldConfig.deprecationReason, isDeprecated: fieldConfig.deprecationReason != null, + deprecationReason: fieldConfig.deprecationReason, extensions: fieldConfig.extensions && toObjMap(fieldConfig.extensions), astNode: fieldConfig.astNode, }; @@ -1624,8 +1628,8 @@ export type GraphQLInputField = {| description: ?string, type: GraphQLInputType, defaultValue: mixed, - isDeprecated?: boolean, - deprecationReason?: ?string, + isDeprecated: boolean, + deprecationReason: ?string, extensions: ?ReadOnlyObjMap, astNode: ?InputValueDefinitionNode, |}; diff --git a/src/type/directives.js b/src/type/directives.js index f956526063..bb4f57abb9 100644 --- a/src/type/directives.js +++ b/src/type/directives.js @@ -75,14 +75,23 @@ export class GraphQLDirective { `@${config.name} args must be an object with argument names as keys.`, ); - this.args = objectEntries(args).map(([argName, argConfig]) => ({ - name: argName, - description: argConfig.description, - type: argConfig.type, - defaultValue: argConfig.defaultValue, - extensions: argConfig.extensions && toObjMap(argConfig.extensions), - astNode: argConfig.astNode, - })); + this.args = objectEntries(args).map(([argName, argConfig]) => { + devAssert( + !('isDeprecated' in argConfig), + `@${config.name}.${argName} should provide "deprecationReason" instead of "isDeprecated".`, + ); + + return { + name: argName, + description: argConfig.description, + type: argConfig.type, + defaultValue: argConfig.defaultValue, + isDeprecated: argConfig.deprecationReason != null, + deprecationReason: argConfig.deprecationReason, + extensions: argConfig.extensions && toObjMap(argConfig.extensions), + astNode: argConfig.astNode, + }; + }); } toConfig(): {| @@ -182,8 +191,8 @@ export const GraphQLDeprecatedDirective = new GraphQLDirective({ description: 'Marks an element of a GraphQL schema as no longer supported.', locations: [ DirectiveLocation.FIELD_DEFINITION, - DirectiveLocation.ENUM_VALUE, DirectiveLocation.ARGUMENT_DEFINITION, + DirectiveLocation.ENUM_VALUE, DirectiveLocation.INPUT_FIELD_DEFINITION, ], args: { diff --git a/src/type/introspection.js b/src/type/introspection.js index 02a21fbc95..ef2cf90b49 100644 --- a/src/type/introspection.js +++ b/src/type/introspection.js @@ -512,6 +512,8 @@ export const TypeMetaFieldDef: GraphQLField = { description: undefined, type: GraphQLNonNull(GraphQLString), defaultValue: undefined, + isDeprecated: false, + deprecationReason: undefined, extensions: undefined, astNode: undefined, }, diff --git a/src/utilities/__tests__/printSchema-test.js b/src/utilities/__tests__/printSchema-test.js index 743df4b993..e5d460f8e5 100644 --- a/src/utilities/__tests__/printSchema-test.js +++ b/src/utilities/__tests__/printSchema-test.js @@ -629,7 +629,7 @@ describe('Type System Printer', () => { 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/). """ reason: String = "No longer supported" - ) on FIELD_DEFINITION | ENUM_VALUE | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION + ) on FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM_VALUE | INPUT_FIELD_DEFINITION """Exposes a URL that specifies the behaviour of this scalar.""" directive @specifiedBy( @@ -852,7 +852,7 @@ describe('Type System Printer', () => { 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/). reason: String = "No longer supported" - ) on FIELD_DEFINITION | ENUM_VALUE | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION + ) on FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM_VALUE | INPUT_FIELD_DEFINITION # Exposes a URL that specifies the behaviour of this scalar. directive @specifiedBy( diff --git a/src/utilities/printSchema.js b/src/utilities/printSchema.js index 7a8687ba72..0ce13037d6 100644 --- a/src/utilities/printSchema.js +++ b/src/utilities/printSchema.js @@ -232,7 +232,7 @@ function printEnum(type: GraphQLEnumType, options): string { printDescription(options, value, ' ', !i) + ' ' + value.name + - printDeprecated(value), + printDeprecated(value.deprecationReason), ); return ( @@ -259,7 +259,7 @@ function printFields(options, type) { printArgs(options, f.args, ' ') + ': ' + String(f.type) + - printDeprecated(f), + printDeprecated(f.deprecationReason), ); return printBlock(fields); } @@ -301,7 +301,7 @@ function printInputValue(arg) { if (defaultAST) { argDecl += ` = ${print(defaultAST)}`; } - return argDecl + printDeprecated(arg); + return argDecl + printDeprecated(arg.deprecationReason); } function printDirective(directive, options) { @@ -316,11 +316,10 @@ function printDirective(directive, options) { ); } -function printDeprecated(fieldOrEnumVal) { - if (fieldOrEnumVal.deprecationReason == null) { +function printDeprecated(reason) { + if (reason == null) { return ''; } - const reason = fieldOrEnumVal.deprecationReason; const reasonAST = astFromValue(reason, GraphQLString); if (reasonAST && reason !== DEFAULT_DEPRECATION_REASON) { return ' @deprecated(reason: ' + print(reasonAST) + ')';