Skip to content

Commit

Permalink
Add NoDeprecatedCustomRule and deprecate findDeprecatedUsages
Browse files Browse the repository at this point in the history
  • Loading branch information
danielrearden committed Jun 20, 2020
1 parent 00077f1 commit 2a1ee4a
Show file tree
Hide file tree
Showing 15 changed files with 222 additions and 123 deletions.
1 change: 1 addition & 0 deletions .nycrc.yml
Expand Up @@ -14,6 +14,7 @@ exclude:
- 'src/validation/rules/UniqueFieldDefinitionNames.js'
- 'src/validation/rules/UniqueTypeNames.js'
- 'src/validation/rules/UniqueOperationTypes.js'
- 'src/utilities/findDeprecatedUsages.js'
clean: true
temp-directory: 'coverage/tests'
report-dir: 'coverage/tests'
Expand Down
3 changes: 2 additions & 1 deletion src/index.d.ts
Expand Up @@ -336,6 +336,7 @@ export {
UniqueDirectiveNamesRule,
PossibleTypeExtensionsRule,
// Custom validation rules
NoDeprecatedCustomRule,
NoSchemaIntrospectionCustomRule,
ValidationRule,
} from './validation/index';
Expand Down Expand Up @@ -416,7 +417,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
3 changes: 2 additions & 1 deletion src/index.js
Expand Up @@ -334,6 +334,7 @@ export {
UniqueDirectiveNamesRule,
PossibleTypeExtensionsRule,
// Custom validation rules
NoDeprecatedCustomRule,
NoSchemaIntrospectionCustomRule,
} from './validation/index';

Expand Down Expand Up @@ -416,7 +417,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
76 changes: 0 additions & 76 deletions src/utilities/__tests__/findDeprecatedUsages-test.js

This file was deleted.

10 changes: 9 additions & 1 deletion src/utilities/findDeprecatedUsages.d.ts
Expand Up @@ -6,8 +6,16 @@ import { GraphQLSchema } from '../type/schema';
* A validation rule which reports deprecated usages.
*
* Returns a list of GraphQLError instances describing each deprecated use.
*
* @deprecated Please use `validate` with `NoDeprecatedCustomRule` instead:
*
* ```
* import { validate, NoDeprecatedCustomRule } from 'graphql'
*
* const errors = validate(schema, document, [NoDeprecatedCustomRule])
* ```
*/
export function findDeprecatedUsages(
schema: GraphQLSchema,
ast: DocumentNode,
): Array<GraphQLError>;
): ReadonlyArray<GraphQLError>;
54 changes: 12 additions & 42 deletions src/utilities/findDeprecatedUsages.js
Expand Up @@ -4,57 +4,27 @@ import { GraphQLError } from '../error/GraphQLError';

import type { DocumentNode } from '../language/ast';

import { visit } from '../language/visitor';

import type { GraphQLSchema } from '../type/schema';

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

import { TypeInfo, visitWithTypeInfo } from './TypeInfo';
import { validate } from '../validation/validate';
import { NoDeprecatedCustomRule } from '../validation/rules/custom/NoDeprecatedCustomRule';

/**
* A validation rule which reports deprecated usages.
*
* Returns a list of GraphQLError instances describing each deprecated use.
*
* @deprecated Please use `validate` with `NoDeprecatedCustomRule` instead:
*
* ```
* import { validate, NoDeprecatedCustomRule } from 'graphql'
*
* const errors = validate(schema, document, [NoDeprecatedCustomRule])
* ```
*/
export function findDeprecatedUsages(
schema: GraphQLSchema,
ast: DocumentNode,
): Array<GraphQLError> {
const errors = [];
const typeInfo = new TypeInfo(schema);

visit(
ast,
visitWithTypeInfo(typeInfo, {
Field(node) {
const parentType = typeInfo.getParentType();
const fieldDef = typeInfo.getFieldDef();
if (parentType && fieldDef?.deprecationReason != null) {
errors.push(
new GraphQLError(
`The field "${parentType.name}.${fieldDef.name}" is deprecated. ` +
fieldDef.deprecationReason,
node,
),
);
}
},
EnumValue(node) {
const type = getNamedType(typeInfo.getInputType());
const enumVal = typeInfo.getEnumValue();
if (type && enumVal?.deprecationReason != null) {
errors.push(
new GraphQLError(
`The enum value "${type.name}.${enumVal.name}" is deprecated. ` +
enumVal.deprecationReason,
node,
),
);
}
},
}),
);

return errors;
): $ReadOnlyArray<GraphQLError> {
return validate(schema, ast, [NoDeprecatedCustomRule]);
}
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 @@ -25,6 +25,7 @@ import type {
GraphQLCompositeType,
GraphQLField,
GraphQLArgument,
GraphQLEnumValue,
} from '../type/definition';

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

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

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

import { describe, it } from 'mocha';

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

import { NoDeprecatedCustomRule } from '../rules/custom/NoDeprecatedCustomRule';

import { expectValidationErrorsWithSchema } from './harness';

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

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

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
}
`);

describe('Validate: no deprecated', () => {
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)
}
fragment QueryFragment on Query {
unknownField(unknownArg: UNKNOWN_VALUE)
normalField(enumArg: UNKNOWN_VALUE)
}
`);
});

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

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

it('reports error when a deprecated field is selected or an enum value is used inside a fragment', () => {
expectErrors(`
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: 3, column: 9 }],
},
{
message:
'The enum value "EnumType.DEPRECATED_VALUE" is deprecated. Some enum reason.',
locations: [{ line: 4, column: 45 }],
},
]);
});
});
1 change: 1 addition & 0 deletions src/validation/index.d.ts
Expand Up @@ -92,4 +92,5 @@ export { UniqueDirectiveNamesRule } from './rules/UniqueDirectiveNamesRule';
export { PossibleTypeExtensionsRule } from './rules/PossibleTypeExtensionsRule';

// Optional rules not defined by the GraphQL Specification
export { NoDeprecatedCustomRule } from './rules/custom/NoDeprecatedCustomRule';
export { NoSchemaIntrospectionCustomRule } from './rules/custom/NoSchemaIntrospectionCustomRule';
1 change: 1 addition & 0 deletions src/validation/index.js
Expand Up @@ -96,4 +96,5 @@ export { UniqueDirectiveNamesRule } from './rules/UniqueDirectiveNamesRule';
export { PossibleTypeExtensionsRule } from './rules/PossibleTypeExtensionsRule';

// Optional rules not defined by the GraphQL Specification
export { NoDeprecatedCustomRule } from './rules/custom/NoDeprecatedCustomRule';
export { NoSchemaIntrospectionCustomRule } from './rules/custom/NoSchemaIntrospectionCustomRule';

0 comments on commit 2a1ee4a

Please sign in to comment.