Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: check deprecated directive added or removed from input field and field argument #2200

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions packages/core/__tests__/diff/argument.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 */ `
Expand Down
30 changes: 30 additions & 0 deletions packages/core/__tests__/diff/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down
12 changes: 12 additions & 0 deletions packages/core/src/diff/argument.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -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));
}
}
}
22 changes: 22 additions & 0 deletions packages/core/src/diff/changes/argument.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,25 @@ export function fieldArgumentTypeChanged(
path: [type.name, field.name, oldArg.name].join('.'),
};
}

export function fieldArgumentDeprecationAdded(type: GraphQLObjectType | GraphQLInterfaceType, field: GraphQLField<any, any, any>, 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<any, any, any>, 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('.'),
};
}
4 changes: 4 additions & 0 deletions packages/core/src/diff/changes/change.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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',
Expand Down
22 changes: 22 additions & 0 deletions packages/core/src/diff/changes/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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('.'),
};
}
12 changes: 12 additions & 0 deletions packages/core/src/diff/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -10,6 +11,8 @@ import {
inputFieldDescriptionRemoved,
inputFieldDefaultValueChanged,
inputFieldTypeChanged,
inputFieldDeprecationAdded,
inputFieldDeprecationRemoved,
} from './changes/input';
import { AddChange } from './schema';

Expand Down Expand Up @@ -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));
}
}
}