Skip to content

Commit

Permalink
fix: add deprecationReason property to args
Browse files Browse the repository at this point in the history
closes #317
  • Loading branch information
nodkz committed May 30, 2021
1 parent da94463 commit ca91949
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 51 deletions.
2 changes: 2 additions & 0 deletions src/ObjectTypeComposer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ export type ObjectTypeComposerArgumentConfigAsObjectDefinition = {
type: ThunkWithSchemaComposer<ComposeInputTypeDefinition, SchemaComposer<any>>;
defaultValue?: any;
description?: string | null;
deprecationReason?: string | null;
extensions?: Extensions;
directives?: Directive[];
[key: string]: any;
Expand All @@ -161,6 +162,7 @@ export type ObjectTypeComposerArgumentConfig = {
type: ComposeInputType;
defaultValue?: any;
description?: string | null;
deprecationReason?: string | null;
astNode?: InputValueDefinitionNode | null;
extensions?: Extensions;
directives?: Directive[];
Expand Down
68 changes: 53 additions & 15 deletions src/TypeMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,11 +390,26 @@ export class TypeMapper<TContext = any> {

const tc = this.convertOutputTypeDefinition(type, fieldName, typeName);
if (tc) {
return {
const fc = {
type: tc,
args: this.convertArgConfigMap(args || {}, fieldName, typeName),
...rest,
};
} as ObjectTypeComposerFieldConfig<any, any, any>;

const deprecatedDirectiveIdx =
fc?.directives?.findIndex((d) => d.name === 'deprecated') ?? -1;
if (fc.deprecationReason) {
if (!fc.directives) fc.directives = [];
if (deprecatedDirectiveIdx >= 0) {
fc.directives[deprecatedDirectiveIdx].args = { reason: fc.deprecationReason };
} else {
fc.directives.push({ name: 'deprecated', args: { reason: fc.deprecationReason } });
}
} else if (deprecatedDirectiveIdx >= 0) {
fc.deprecationReason = fc?.directives?.[deprecatedDirectiveIdx]?.args?.reason;
}

return fc;
}
}

Expand Down Expand Up @@ -445,10 +460,25 @@ export class TypeMapper<TContext = any> {

const tc = this.convertInputTypeDefinition(type);
if (tc) {
return {
const ac = {
type: tc,
...rest,
};
} as ObjectTypeComposerArgumentConfig;

const deprecatedDirectiveIdx =
ac?.directives?.findIndex((d) => d.name === 'deprecated') ?? -1;
if (ac.deprecationReason) {
if (!ac.directives) ac.directives = [];
if (deprecatedDirectiveIdx >= 0) {
ac.directives[deprecatedDirectiveIdx].args = { reason: ac.deprecationReason };
} else {
ac.directives.push({ name: 'deprecated', args: { reason: ac.deprecationReason } });
}
} else if (deprecatedDirectiveIdx >= 0) {
ac.deprecationReason = ac?.directives?.[deprecatedDirectiveIdx]?.args?.reason;
}

return ac;
}
}

Expand Down Expand Up @@ -579,10 +609,25 @@ export class TypeMapper<TContext = any> {

const tc = this.convertInputTypeDefinition(type, fieldName, typeName);
if (tc) {
return {
const fc = {
type: tc,
...rest,
} as any;
} as InputTypeComposerFieldConfig;

const deprecatedDirectiveIdx =
fc?.directives?.findIndex((d) => d.name === 'deprecated') ?? -1;
if (fc.deprecationReason) {
if (!fc.directives) fc.directives = [];
if (deprecatedDirectiveIdx >= 0) {
fc.directives[deprecatedDirectiveIdx].args = { reason: fc.deprecationReason };
} else {
fc.directives.push({ name: 'deprecated', args: { reason: fc.deprecationReason } });
}
} else if (deprecatedDirectiveIdx >= 0) {
fc.deprecationReason = fc?.directives?.[deprecatedDirectiveIdx]?.args?.reason;
}

return fc;
}
}

Expand Down Expand Up @@ -764,6 +809,8 @@ export class TypeMapper<TContext = any> {
type,
description: getDescription(value),
directives: this.parseDirectives(value.directives),
deprecationReason: this.getDeprecationReason(value.directives),
astNode: value,
} as ObjectTypeComposerArgumentConfig;

if (value.defaultValue) {
Expand Down Expand Up @@ -1079,15 +1126,6 @@ export class TypeMapper<TContext = any> {
directives.forEach((directive) => {
const name = directive.name.value;

if (name === GraphQLDeprecatedDirective.name) {
// @deprecated directive should be parsed via getDeprecationReason() method
// It's due to fact that deprecated is stored as separate type instance's field.
return;
// TODO: rewrite this logic in TypeComposers which updates directive via:
// - setFieldArgDirectiveByName
// - setFieldDirectiveByName
}

const directiveDef = this.schemaComposer._getDirective(name);
const args = directiveDef
? getArgumentValues(directiveDef, directive)
Expand Down
76 changes: 64 additions & 12 deletions src/__tests__/github_issues/317-test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
// TODO: enable after migration to TypeScript

import { printType } from 'graphql';
import { schemaComposer, dedent, ObjectTypeComposer } from '../..';

describe.skip('github issue #317: Directive @deprecated for arguments in SDL is ignored ', () => {
it('should build schema successfully', async () => {
describe('github issue #317: Directive @deprecated for arguments in SDL is ignored', () => {
it('should print correct SDL 1', async () => {
const typeComposer = schemaComposer.typeMapper.convertSDLTypeDefinition(`
type Foo {
foo(
Expand All @@ -13,21 +11,75 @@ describe.skip('github issue #317: Directive @deprecated for arguments in SDL is
}
`) as ObjectTypeComposer;

const type = typeComposer.getType();

expect(typeComposer.toSDL()).toEqual(dedent`
type Foo {
foo(
arg: String @deprecated(reason: "Tired")
): String
foo(arg: String @deprecated(reason: "Tired")): String
}
`);

const type = typeComposer.getType();

expect(printType(type)).toEqual(dedent`
type Foo {
foo(
arg: String @deprecated(reason: "Tired")
): String
foo(arg: String @deprecated(reason: "Tired")): String
}
`);
});

it('should print correct SDL 2', async () => {
const typeComposer = schemaComposer.createObjectTC({
name: 'Foo2',
fields: {
foo: {
type: 'String',
args: {
arg: {
type: 'String',
deprecationReason: 'ArgTired',
},
},
deprecationReason: 'FieldTired',
},
},
});

expect(typeComposer.toSDL()).toEqual(dedent`
type Foo2 {
foo(arg: String @deprecated(reason: "ArgTired")): String @deprecated(reason: "FieldTired")
}
`);

const type = typeComposer.getType();

expect(printType(type)).toEqual(dedent`
type Foo2 {
foo(arg: String @deprecated(reason: "ArgTired")): String @deprecated(reason: "FieldTired")
}
`);
});

it('should print correct SDL 3', async () => {
const typeComposer = schemaComposer.createInputTC({
name: 'Foo3',
fields: {
foo: {
type: 'String',
deprecationReason: 'FieldTired',
},
},
});

expect(typeComposer.toSDL()).toEqual(dedent`
input Foo3 {
foo: String @deprecated(reason: "FieldTired")
}
`);

const type = typeComposer.getType();

expect(printType(type)).toEqual(dedent`
input Foo3 {
foo: String @deprecated(reason: "FieldTired")
}
`);
});
Expand Down
2 changes: 2 additions & 0 deletions src/utils/configToDefine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ export function defineFieldMap(
name: argName,
description: arg.description === undefined ? null : arg.description,
type: arg.type,
isDeprecated: Boolean(fieldConfig.deprecationReason),
deprecationReason: arg?.deprecationReason,
defaultValue: arg.defaultValue,
astNode: fieldArgNodeMap[argName],
};
Expand Down
28 changes: 4 additions & 24 deletions src/utils/schemaPrinter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,8 @@ import { printBlockString } from 'graphql/language/blockString';
import type { DirectiveNode } from 'graphql/language/ast';
import type { GraphQLSchema } from 'graphql/type/schema';
import { isIntrospectionType } from 'graphql/type/introspection';
import { GraphQLString, isSpecifiedScalarType } from 'graphql/type/scalars';
import {
GraphQLDirective,
DEFAULT_DEPRECATION_REASON,
isSpecifiedDirective,
} from 'graphql/type/directives';
import { isSpecifiedScalarType } from 'graphql/type/scalars';
import { GraphQLDirective, isSpecifiedDirective } from 'graphql/type/directives';
import {
GraphQLArgument,
GraphQLNamedType,
Expand All @@ -39,7 +35,6 @@ import { astFromValue } from 'graphql/utilities/astFromValue';

import { SchemaComposer } from '../SchemaComposer';
import { SchemaFilterTypes, getTypesFromSchema, getDirectivesFromSchema } from './getFromSchema';
import { Maybe } from 'graphql/jsutils/Maybe';

import { CompareTypeComposersOption, getSortMethodFromOption } from './schemaPrinterSortTypes';

Expand Down Expand Up @@ -301,7 +296,7 @@ export function printEnum(type: GraphQLEnumType, options?: Options): string {
(value, i) =>
`${printDescription(value, options, ' ', !i)} ${value.name}${printNodeDirectives(
value.astNode
)}${printDeprecated(value)}`
)}`
);

return `${printDescription(type, options)}enum ${type.name}${printNodeDirectives(
Expand Down Expand Up @@ -338,7 +333,7 @@ export function printFields(
f.args,
options,
' '
)}: ${String(f.type)}${printNodeDirectives(f.astNode)}${printDeprecated(f)}`
)}: ${String(f.type)}${printNodeDirectives(f.astNode)}`
);
return printBlock(fieldsList);
}
Expand Down Expand Up @@ -412,21 +407,6 @@ export function printNodeDirectives(
.join(' ')}`;
}

export function printDeprecated(fieldOrEnumVal: {
isDeprecated?: boolean;
deprecationReason?: Maybe<string>;
}): string {
if (!fieldOrEnumVal.isDeprecated) {
return '';
}
const reason = fieldOrEnumVal.deprecationReason;
const reasonAST = astFromValue(reason, GraphQLString);
if (reasonAST && reason !== DEFAULT_DEPRECATION_REASON) {
return ` @deprecated(reason: ${print(reasonAST)})`;
}
return ' @deprecated';
}

export function printDescription(
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
def: any,
Expand Down

0 comments on commit ca91949

Please sign in to comment.