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

Add NoDeprecatedCustomRule and deprecate findDeprecatedUsages #2605

Merged
merged 1 commit into from Jun 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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])
* ```
*/
danielrearden marked this conversation as resolved.
Show resolved Hide resolved
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> {
IvanGoncharov marked this conversation as resolved.
Show resolved Hide resolved
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';