diff --git a/packages/core/__tests__/diff/argument.ts b/packages/core/__tests__/diff/argument.ts index 9f72d67a6b..287975bf42 100644 --- a/packages/core/__tests__/diff/argument.ts +++ b/packages/core/__tests__/diff/argument.ts @@ -24,6 +24,40 @@ describe('argument', () => { expect(change.message).toEqual("Argument 'b: Boolean!' (with default value) added to field 'Query.a'"); }); + test('deprecation added / removed', async () => { + const a = buildSchema(/* GraphQL */ ` + type Foo { + a(someArg: String): String + b( + someArg: String @deprecated + ): String + } + `); + const b = buildSchema(/* GraphQL */ ` + type Foo { + a( + someArg: String @deprecated + ): String + b(someArg: String): String + } + `); + + const changes = await diff(a, b); + const change = { + a: findFirstChangeByPath(changes, 'Foo.a'), + b: findFirstChangeByPath(changes, 'Foo.b'), + }; + + // Added + expect(change.a.criticality.level).toEqual(CriticalityLevel.NonBreaking); + expect(change.a.type).toEqual('FIELD_ARGUMENT_DEPRECATION_ADDED'); + expect(change.a.message).toEqual("Argument 'someArg' on field 'Foo.a' is deprecated"); + // Removed + expect(change.b.criticality.level).toEqual(CriticalityLevel.NonBreaking); + expect(change.b.type).toEqual('FIELD_ARGUMENT_DEPRECATION_REMOVED'); + expect(change.b.message).toEqual("Argument 'someArg' on field 'Foo.b' is no longer deprecated"); + }); + describe('default value', () => { test('added', async () => { const a = buildSchema(/* GraphQL */ ` diff --git a/packages/core/__tests__/diff/input.ts b/packages/core/__tests__/diff/input.ts index 4dc0cafe03..28c60a8fa4 100644 --- a/packages/core/__tests__/diff/input.ts +++ b/packages/core/__tests__/diff/input.ts @@ -310,6 +310,36 @@ describe('input', () => { expect(change.c.type).toEqual('INPUT_FIELD_TYPE_CHANGED'); expect(change.c.message).toEqual("Input field 'Foo.c' changed type from '[String]!' to '[String!]!'"); }); + + test('deprecation added / removed', async () => { + const a = buildSchema(/* GraphQL */ ` + input Foo { + a: String + b: String @deprecated + } + `); + const b = buildSchema(/* GraphQL */ ` + input Foo { + a: String @deprecated + b: String + } + `); + + const changes = await diff(a, b); + const change = { + a: findFirstChangeByPath(changes, 'Foo.a'), + b: findFirstChangeByPath(changes, 'Foo.b'), + }; + + // Added + expect(change.a.criticality.level).toEqual(CriticalityLevel.NonBreaking); + expect(change.a.type).toEqual('INPUT_FIELD_DEPRECATION_ADDED'); + expect(change.a.message).toEqual("Input field 'Foo.a' is deprecated"); + // Removed + expect(change.b.criticality.level).toEqual(CriticalityLevel.NonBreaking); + expect(change.b.type).toEqual('INPUT_FIELD_DEPRECATION_REMOVED'); + expect(change.b.message).toEqual("Input field 'Foo.b' is no longer deprecated"); + }); }); test('removal of a deprecated field', async () => { diff --git a/packages/core/src/diff/argument.ts b/packages/core/src/diff/argument.ts index 33beccb799..ce49afd466 100644 --- a/packages/core/src/diff/argument.ts +++ b/packages/core/src/diff/argument.ts @@ -1,10 +1,13 @@ import { GraphQLArgument, GraphQLObjectType, GraphQLField, GraphQLInterfaceType } from 'graphql'; import { diffArrays, isNotEqual } from '../utils/compare'; +import { isDeprecated } from '../utils/isDeprecated'; import { fieldArgumentDescriptionChanged, fieldArgumentDefaultChanged, fieldArgumentTypeChanged, + fieldArgumentDeprecationAdded, + fieldArgumentDeprecationRemoved, } from './changes/argument'; import { AddChange } from './schema'; @@ -33,4 +36,13 @@ export function changesInArgument( if (isNotEqual(oldArg.type.toString(), newArg.type.toString())) { addChange(fieldArgumentTypeChanged(type, field, oldArg, newArg)); } + + if (isNotEqual(isDeprecated(oldArg), isDeprecated(newArg))) { + if (isDeprecated(newArg)) { + addChange(fieldArgumentDeprecationAdded(type, field, newArg)); + } + else { + addChange(fieldArgumentDeprecationRemoved(type, field, oldArg)); + } + } } diff --git a/packages/core/src/diff/changes/argument.ts b/packages/core/src/diff/changes/argument.ts index 795e7b9f8e..714b8d4b20 100644 --- a/packages/core/src/diff/changes/argument.ts +++ b/packages/core/src/diff/changes/argument.ts @@ -66,3 +66,25 @@ export function fieldArgumentTypeChanged( path: [type.name, field.name, oldArg.name].join('.'), }; } + +export function fieldArgumentDeprecationAdded(type: GraphQLObjectType | GraphQLInterfaceType, field: GraphQLField, arg: GraphQLArgument) { + return { + criticality: { + level: CriticalityLevel.NonBreaking, + }, + type: ChangeType.FieldArgumentDeprecationAdded, + message: `Argument '${arg.name}' on field '${type.name}.${field.name}' is deprecated`, + path: [type.name, field.name].join('.'), + }; +} + +export function fieldArgumentDeprecationRemoved(type: GraphQLObjectType | GraphQLInterfaceType, field: GraphQLField, arg: GraphQLArgument) { + return { + criticality: { + level: CriticalityLevel.NonBreaking, + }, + type: ChangeType.FieldArgumentDeprecationRemoved, + message: `Argument '${arg.name}' on field '${type.name}.${field.name}' is no longer deprecated`, + path: [type.name, field.name].join('.'), + }; +} \ No newline at end of file diff --git a/packages/core/src/diff/changes/change.ts b/packages/core/src/diff/changes/change.ts index 4ee6a654de..a57e85b8b5 100644 --- a/packages/core/src/diff/changes/change.ts +++ b/packages/core/src/diff/changes/change.ts @@ -3,6 +3,8 @@ export enum ChangeType { FieldArgumentDescriptionChanged = 'FIELD_ARGUMENT_DESCRIPTION_CHANGED', FieldArgumentDefaultChanged = 'FIELD_ARGUMENT_DEFAULT_CHANGED', FieldArgumentTypeChanged = 'FIELD_ARGUMENT_TYPE_CHANGED', + FieldArgumentDeprecationAdded = "FIELD_ARGUMENT_DEPRECATION_ADDED", + FieldArgumentDeprecationRemoved = "FIELD_ARGUMENT_DEPRECATION_REMOVED", // Directive DirectiveRemoved = 'DIRECTIVE_REMOVED', DirectiveAdded = 'DIRECTIVE_ADDED', @@ -43,6 +45,8 @@ export enum ChangeType { InputFieldDescriptionChanged = 'INPUT_FIELD_DESCRIPTION_CHANGED', InputFieldDefaultValueChanged = 'INPUT_FIELD_DEFAULT_VALUE_CHANGED', InputFieldTypeChanged = 'INPUT_FIELD_TYPE_CHANGED', + InputFieldDeprecationAdded = "INPUT_FIELD_DEPRECATION_ADDED", + InputFieldDeprecationRemoved = "INPUT_FIELD_DEPRECATION_REMOVED", // Type ObjectTypeInterfaceAdded = 'OBJECT_TYPE_INTERFACE_ADDED', ObjectTypeInterfaceRemoved = 'OBJECT_TYPE_INTERFACE_REMOVED', diff --git a/packages/core/src/diff/changes/input.ts b/packages/core/src/diff/changes/input.ts index b140d61d75..3714f0c587 100644 --- a/packages/core/src/diff/changes/input.ts +++ b/packages/core/src/diff/changes/input.ts @@ -114,3 +114,25 @@ export function inputFieldTypeChanged( path: [input.name, oldField.name].join('.'), }; } + +export function inputFieldDeprecationAdded(input: GraphQLInputObjectType, field: GraphQLInputField) { + return { + criticality: { + level: CriticalityLevel.NonBreaking, + }, + type: ChangeType.InputFieldDeprecationAdded, + message: `Input field '${input.name}.${field.name}' is deprecated`, + path: [input.name, field.name].join('.'), + }; +} + +export function inputFieldDeprecationRemoved(input: GraphQLInputObjectType, field: GraphQLInputField) { + return { + criticality: { + level: CriticalityLevel.NonBreaking, + }, + type: ChangeType.InputFieldDeprecationRemoved, + message: `Input field '${input.name}.${field.name}' is no longer deprecated`, + path: [input.name, field.name].join('.'), + }; +} \ No newline at end of file diff --git a/packages/core/src/diff/input.ts b/packages/core/src/diff/input.ts index f7b6d6bff5..e25981be89 100644 --- a/packages/core/src/diff/input.ts +++ b/packages/core/src/diff/input.ts @@ -2,6 +2,7 @@ import { GraphQLInputObjectType, GraphQLInputField } from 'graphql'; import { diffArrays, isNotEqual, isVoid } from '../utils/compare'; import { compareLists } from '../utils/compare'; +import { isDeprecated } from '../utils/isDeprecated'; import { inputFieldAdded, inputFieldRemoved, @@ -10,6 +11,8 @@ import { inputFieldDescriptionRemoved, inputFieldDefaultValueChanged, inputFieldTypeChanged, + inputFieldDeprecationAdded, + inputFieldDeprecationRemoved, } from './changes/input'; import { AddChange } from './schema'; @@ -63,4 +66,13 @@ function changesInInputField( if (isNotEqual(oldField.type.toString(), newField.type.toString())) { addChange(inputFieldTypeChanged(input, oldField, newField)); } + + if (isNotEqual(isDeprecated(oldField), isDeprecated(newField))) { + if (isDeprecated(newField)) { + addChange(inputFieldDeprecationAdded(input, newField)); + } + else { + addChange(inputFieldDeprecationRemoved(input, oldField)); + } + } }