Skip to content

Commit

Permalink
Add ProhibitDeprecatedFieldsRule and deprecate findDeprecatedUsages
Browse files Browse the repository at this point in the history
  • Loading branch information
danielrearden committed Jun 3, 2020
1 parent d10cf6c commit b6925a0
Show file tree
Hide file tree
Showing 14 changed files with 224 additions and 6 deletions.
2 changes: 1 addition & 1 deletion src/index.d.ts
Expand Up @@ -414,7 +414,7 @@ export {
DangerousChangeType,
findBreakingChanges,
findDangerousChanges,
// Report all deprecated usage within a GraphQL document.
// @deprecated: Report all deprecated usage within a GraphQL document.
findDeprecatedUsages,
} from './utilities/index';

Expand Down
2 changes: 1 addition & 1 deletion src/index.js
Expand Up @@ -414,7 +414,7 @@ export {
DangerousChangeType,
findBreakingChanges,
findDangerousChanges,
// Report all deprecated usage within a GraphQL document.
// @deprecated: Report all deprecated usage within a GraphQL document.
findDeprecatedUsages,
} from './utilities/index';

Expand Down
2 changes: 2 additions & 0 deletions src/utilities/__tests__/findDeprecatedUsages-test.js
@@ -1,5 +1,7 @@
// @flow strict

/* eslint-disable import/no-deprecated */

import { expect } from 'chai';
import { describe, it } from 'mocha';

Expand Down
2 changes: 1 addition & 1 deletion src/utilities/findDeprecatedUsages.d.ts
Expand Up @@ -3,7 +3,7 @@ import { DocumentNode } from '../language/ast';
import { GraphQLSchema } from '../type/schema';

/**
* A validation rule which reports deprecated usages.
* @deprecated: A validation rule which reports deprecated usages.
*
* Returns a list of GraphQLError instances describing each deprecated use.
*/
Expand Down
2 changes: 1 addition & 1 deletion src/utilities/findDeprecatedUsages.js
Expand Up @@ -11,7 +11,7 @@ import { type GraphQLSchema } from '../type/schema';
import { TypeInfo, visitWithTypeInfo } from './TypeInfo';

/**
* A validation rule which reports deprecated usages.
* @deprecated: A validation rule which reports deprecated usages.
*
* Returns a list of GraphQLError instances describing each deprecated use.
*/
Expand Down
2 changes: 1 addition & 1 deletion src/utilities/index.d.ts
Expand Up @@ -112,5 +112,5 @@ export {
DangerousChange,
} from './findBreakingChanges';

// Report all deprecated usage within a GraphQL document.
// @deprecated: Report all deprecated usage within a GraphQL document.
export { findDeprecatedUsages } from './findDeprecatedUsages';
2 changes: 1 addition & 1 deletion src/utilities/index.js
Expand Up @@ -111,5 +111,5 @@ export {
} from './findBreakingChanges';
export type { BreakingChange, DangerousChange } from './findBreakingChanges';

// Report all deprecated usage within a GraphQL document.
// @deprecated: Report all deprecated usage within a GraphQL document.
export { findDeprecatedUsages } from './findDeprecatedUsages';
3 changes: 3 additions & 0 deletions src/validation/ValidationContext.d.ts
Expand Up @@ -18,6 +18,7 @@ import {
GraphQLCompositeType,
GraphQLField,
GraphQLArgument,
GraphQLEnumValue,
} from '../type/definition';
import { TypeInfo } from '../utilities/TypeInfo';

Expand Down Expand Up @@ -90,6 +91,8 @@ export class ValidationContext extends ASTValidationContext {
getDirective(): Maybe<GraphQLDirective>;

getArgument(): Maybe<GraphQLArgument>;

getEnumValue(): Maybe<GraphQLEnumValue>;
}

export type ValidationRule = (context: ValidationContext) => ASTVisitor;
5 changes: 5 additions & 0 deletions src/validation/ValidationContext.js
Expand Up @@ -23,6 +23,7 @@ import {
type GraphQLCompositeType,
type GraphQLField,
type GraphQLArgument,
type GraphQLEnumValue,
} from '../type/definition';

import { TypeInfo, visitWithTypeInfo } from '../utilities/TypeInfo';
Expand Down Expand Up @@ -243,6 +244,10 @@ export class ValidationContext extends ASTValidationContext {
getArgument(): ?GraphQLArgument {
return this._typeInfo.getArgument();
}

getEnumValue(): ?GraphQLEnumValue {
return this._typeInfo.getEnumValue();
}
}

export type ValidationRule = (ValidationContext) => ASTVisitor;
135 changes: 135 additions & 0 deletions src/validation/__tests__/ProhibitDeprecatedFieldsRule-test.js
@@ -0,0 +1,135 @@
// @flow strict

import { describe, it } from 'mocha';

import { buildSchema } from '../../utilities/buildASTSchema';

import { ProhibitDeprecatedFieldsRule } from '../rules/optional/ProhibitDeprecatedFieldsRule';

import { expectValidationErrorsWithSchema } from './harness';

const schema = buildSchema(`
enum EnumType {
NORMAL_VALUE
DEPRECATED_VALUE @deprecated(reason: "Some enum reason.")
DEPRECATED_VALUE_WITH_NO_REASON @deprecated
}
type Query {
normalField(enumArg: [EnumType]): String
deprecatedField: String @deprecated(reason: "Some field reason.")
deprecatedFieldWithNoReason: String @deprecated
}
`);

function expectErrors(queryStr) {
return expectValidationErrorsWithSchema(
schema,
ProhibitDeprecatedFieldsRule,
queryStr,
);
}

function expectValid(queryStr) {
expectErrors(queryStr).to.deep.equal([]);
}

describe('Validate: prohibit deprecated fields', () => {
it('ignores fields and enum values that are not deprecated', () => {
expectValid(`
{
normalField(enumArg: [NORMAL_VALUE])
}
`);
});

it('ignores unknown fields and enum values', () => {
expectValid(`
fragment UnknownFragment on UnknownType {
unknownField(unknownArg: UNKNOWN_VALUE)
}
`);
});

it('reports error when a deprecated field is selected', () => {
expectErrors(`
{
normalField
deprecatedField
}
`).to.deep.equal([
{
message:
'The field Query.deprecatedField is deprecated. Some field reason.',
locations: [{ line: 4, column: 9 }],
},
]);
});

it('reports error when a deprecated field with default reason is selected', () => {
expectErrors(`
{
normalField
deprecatedFieldWithNoReason
}
`).to.deep.equal([
{
message:
'The field Query.deprecatedFieldWithNoReason is deprecated. No longer supported',
locations: [{ line: 4, column: 9 }],
},
]);
});

it('reports error when a deprecated enum value is used', () => {
expectErrors(`
{
normalField(enumArg: [NORMAL_VALUE, DEPRECATED_VALUE])
}
`).to.deep.equal([
{
message:
'The enum value "EnumType.DEPRECATED_VALUE" is deprecated. Some enum reason.',
locations: [{ line: 3, column: 45 }],
},
]);
});

it('reports error when a deprecated enum value with default reason is used', () => {
expectErrors(`
{
normalField(enumArg: [DEPRECATED_VALUE_WITH_NO_REASON])
}
`).to.deep.equal([
{
message:
'The enum value "EnumType.DEPRECATED_VALUE_WITH_NO_REASON" is deprecated. No longer supported',
locations: [{ line: 3, column: 31 }],
},
]);
});

it('reports error when a deprecated field is selected or an enum value is used inside a fragment', () => {
expectErrors(`
{
...QueryFragment
}
fragment QueryFragment on Query {
deprecatedField
normalField(enumArg: [NORMAL_VALUE, DEPRECATED_VALUE])
}
`).to.deep.equal([
{
message:
'The field Query.deprecatedField is deprecated. Some field reason.',
locations: [{ line: 7, column: 9 }],
},
{
message:
'The enum value "EnumType.DEPRECATED_VALUE" is deprecated. Some enum reason.',
locations: [{ line: 8, column: 45 }],
},
]);
});
});
3 changes: 3 additions & 0 deletions src/validation/index.d.ts
Expand Up @@ -90,3 +90,6 @@ export { UniqueEnumValueNamesRule } from './rules/UniqueEnumValueNamesRule';
export { UniqueFieldDefinitionNamesRule } from './rules/UniqueFieldDefinitionNamesRule';
export { UniqueDirectiveNamesRule } from './rules/UniqueDirectiveNamesRule';
export { PossibleTypeExtensionsRule } from './rules/PossibleTypeExtensionsRule';

// Optional rules not defined by the GraphQL Specification
export { ProhibitDeprecatedFieldsRule } from './rules/optional/ProhibitDeprecatedFieldsRule';
3 changes: 3 additions & 0 deletions src/validation/index.js
Expand Up @@ -94,3 +94,6 @@ export { UniqueEnumValueNamesRule } from './rules/UniqueEnumValueNamesRule';
export { UniqueFieldDefinitionNamesRule } from './rules/UniqueFieldDefinitionNamesRule';
export { UniqueDirectiveNamesRule } from './rules/UniqueDirectiveNamesRule';
export { PossibleTypeExtensionsRule } from './rules/PossibleTypeExtensionsRule';

// Optional rules not defined by the GraphQL Specification
export { ProhibitDeprecatedFieldsRule } from './rules/optional/ProhibitDeprecatedFieldsRule';
15 changes: 15 additions & 0 deletions src/validation/rules/optional/ProhibitDeprecatedFieldsRule.d.ts
@@ -0,0 +1,15 @@
import { ASTVisitor } from '../../../language/visitor';
import { ValidationContext } from '../../ValidationContext';

/**
* Prohibit deprecated fields
*
* A GraphQL document is only valid if all selected fields and all used enum
* values have not been deprecated.
*
* Note: This rule is optional and is not part of the Validation section of the
* GraphQL Specification.
*/
export function ProhibitDeprecatedFieldsRule(
context: ValidationContext,
): ASTVisitor;
52 changes: 52 additions & 0 deletions src/validation/rules/optional/ProhibitDeprecatedFieldsRule.js
@@ -0,0 +1,52 @@
// @flow strict

import { GraphQLError } from '../../../error/GraphQLError';

import { type EnumValueNode, type FieldNode } from '../../../language/ast';
import { type ASTVisitor } from '../../../language/visitor';

import { getNamedType } from '../../../type/definition';

import { type ValidationContext } from '../../ValidationContext';

/**
* Prohibit deprecated fields
*
* A GraphQL document is only valid if all selected fields and all used enum
* values have not been deprecated.
*
* Note: This rule is optional and is not part of the Validation section of the
* GraphQL Specification.
*/
export function ProhibitDeprecatedFieldsRule(
context: ValidationContext,
): ASTVisitor {
return {
Field(node: FieldNode) {
const fieldDef = context.getFieldDef();
const parentType = context.getParentType();
if (parentType && fieldDef && fieldDef.deprecationReason != null) {
context.reportError(
new GraphQLError(
`The field ${parentType.name}.${fieldDef.name} is deprecated. ` +
fieldDef.deprecationReason,
[node],
),
);
}
},
EnumValue(node: EnumValueNode) {
const type = getNamedType(context.getInputType());
const enumValue = context.getEnumValue();
if (type && enumValue?.deprecationReason != null) {
context.reportError(
new GraphQLError(
`The enum value "${type.name}.${enumValue.name}" is deprecated. ` +
enumValue.deprecationReason,
node,
),
);
}
},
};
}

0 comments on commit b6925a0

Please sign in to comment.