Repository: MichalLytek/type-graphql (ts)

Issue:
Allow deprecating input fields and arguments
It's now supported in `graphql-js` v15.4 🚀 

https://github.com/graphql/graphql-js/pull/2733

Pull request:
Allow deprecating input fields and arguments
Closes #794 :lock:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index db743f9a..bc08970c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@ ### Features
 - **Breaking Change**: `AuthChecker` type is now "function or class" - update to `AuthCheckerFn` if the function form is needed in the code
 - support class-based auth checker, which allows for dependency injection
 - allow defining directives for interface types and theirs fields, with inheritance for object types fields (#744)
+- allow deprecating input fields and args (#794)
 ### Fixes
 - allow defining extension on field resolver level for fields also defined as a property of the class (#776)
 - fix throwing error when schema with dynamic default value was built again (#787)
diff --git a/src/decorators/Arg.ts b/src/decorators/Arg.ts
index 1716f03c..e7c4f9e7 100644
--- a/src/decorators/Arg.ts
+++ b/src/decorators/Arg.ts
@@ -1,9 +1,18 @@
-import { ReturnTypeFunc, DecoratorTypeOptions, DescriptionOptions, ValidateOptions } from "./types";
+import {
+  ReturnTypeFunc,
+  DecoratorTypeOptions,
+  DescriptionOptions,
+  ValidateOptions,
+  DeprecationOptions,
+} from "./types";
 import { getMetadataStorage } from "../metadata/getMetadataStorage";
 import { getParamInfo } from "../helpers/params";
 import { getTypeDecoratorParams } from "../helpers/decorators";
 
-export type ArgOptions = DecoratorTypeOptions & DescriptionOptions & ValidateOptions;
+export type ArgOptions = DecoratorTypeOptions &
+  DescriptionOptions &
+  ValidateOptions &
+  DeprecationOptions;
 
 export function Arg(name: string, options?: ArgOptions): ParameterDecorator;
 export function Arg(
@@ -25,6 +34,7 @@ export function Arg(
       kind: "arg",
       name,
       description: options.description,
+      deprecationReason: options.deprecationReason,
       ...getParamInfo({
         prototype,
         propertyKey,
diff --git a/src/metadata/definitions/param-metadata.ts b/src/metadata/definitions/param-metadata.ts
index f8432c52..ffa03542 100644
--- a/src/metadata/definitions/param-metadata.ts
+++ b/src/metadata/definitions/param-metadata.ts
@@ -32,6 +32,7 @@ export interface ArgParamMetadata extends CommonArgMetadata {
   kind: "arg";
   name: string;
   description: string | undefined;
+  deprecationReason: string | undefined;
 }
 export interface ArgsParamMetadata extends CommonArgMetadata {
   kind: "args";
diff --git a/src/schema/schema-generator.ts b/src/schema/schema-generator.ts
index 3864dc6b..365d36ef 100644
--- a/src/schema/schema-generator.ts
+++ b/src/schema/schema-generator.ts
@@ -525,6 +525,7 @@ export abstract class SchemaGenerator {
                   defaultValue,
                   astNode: getInputValueDefinitionNode(field.name, type, field.directives),
                   extensions: field.extensions,
+                  deprecationReason: field.deprecationReason,
                 };
                 return fieldsMap;
               },
@@ -716,6 +717,7 @@ export abstract class SchemaGenerator {
             param.name,
           ),
           defaultValue: param.typeOptions.defaultValue,
+          deprecationReason: param.deprecationReason,
         };
       } else if (param.kind === "args") {
         const argumentType = getMetadataStorage().argumentTypes.find(
@@ -762,6 +764,7 @@ export abstract class SchemaGenerator {
           defaultValue,
         }),
         defaultValue,
+        deprecationReason: field.deprecationReason,
       };
     });
   }


Test patch:
diff --git a/tests/functional/deprecation.ts b/tests/functional/deprecation.ts
index 1cbe9322..ea56dab6 100644
--- a/tests/functional/deprecation.ts
+++ b/tests/functional/deprecation.ts
@@ -1,11 +1,22 @@
 import "reflect-metadata";
-import { IntrospectionSchema, IntrospectionObjectType } from "graphql";
-
-import { ObjectType, Resolver, Field, Query, Mutation } from "../../src";
+import { IntrospectionSchema, IntrospectionObjectType, GraphQLSchema, printType } from "graphql";
+
+import {
+  ObjectType,
+  Resolver,
+  Field,
+  Query,
+  Mutation,
+  InputType,
+  Arg,
+  ArgsType,
+  Args,
+} from "../../src";
 import { getSchemaInfo } from "../helpers/getSchemaInfo";
 
 describe("Deprecation", () => {
   describe("Schema", () => {
+    let schema: GraphQLSchema;
     let schemaIntrospection: IntrospectionSchema;
     let mutationType: IntrospectionObjectType;
     let queryType: IntrospectionObjectType;
@@ -19,11 +30,11 @@ describe("Deprecation", () => {
         normalField: string;
 
         @Field({ deprecationReason: "sample object field deprecation reason" })
-        describedField: string;
+        deprecatedField: string;
 
         @Field({ deprecationReason: "sample object getter field deprecation reason" })
-        get describedGetterField(): string {
-          return "describedGetterField";
+        get deprecatedGetterField(): string {
+          return "deprecatedGetterField";
         }
 
         @Field({ deprecationReason: "sample object method field deprecation reason" })
@@ -32,6 +43,30 @@ describe("Deprecation", () => {
         }
       }
 
+      @InputType()
+      class SampleInput {
+        @Field()
+        normalField: string;
+
+        @Field({
+          deprecationReason: "sample input field deprecation reason",
+          nullable: true,
+        })
+        deprecatedField: string;
+      }
+
+      @ArgsType()
+      class SampleArgs {
+        @Field()
+        normalArg: string;
+
+        @Field({
+          deprecationReason: "sample args field deprecation reason",
+          nullable: true,
+        })
+        deprecatedArg: string;
+      }
+
       @Resolver(of => SampleObject)
       class SampleResolver {
         @Query()
@@ -39,6 +74,27 @@ describe("Deprecation", () => {
           return {} as SampleObject;
         }
 
+        @Query()
+        inputQuery(@Arg("input") input: SampleInput): SampleObject {
+          return {} as SampleObject;
+        }
+
+        @Query()
+        argsQuery(@Args() args: SampleArgs): SampleObject {
+          return {} as SampleObject;
+        }
+
+        @Query()
+        deprecatedArgQuery(
+          @Arg("arg", {
+            deprecationReason: "sample query arg deprecation reason",
+            nullable: true,
+          })
+          arg?: string,
+        ): SampleObject {
+          return {} as SampleObject;
+        }
+
         @Query({ deprecationReason: "sample query deprecation reason" })
         describedQuery(): string {
           return "describedQuery";
@@ -59,6 +115,7 @@ describe("Deprecation", () => {
       const schemaInfo = await getSchemaInfo({
         resolvers: [SampleResolver],
       });
+      schema = schemaInfo.schema;
       schemaIntrospection = schemaInfo.schemaIntrospection;
       queryType = schemaInfo.queryType;
       mutationType = schemaInfo.mutationType!;
@@ -69,20 +126,20 @@ describe("Deprecation", () => {
         type => type.name === "SampleObject",
       ) as IntrospectionObjectType;
       const normalField = sampleObjectType.fields.find(field => field.name === "normalField")!;
-      const describedField = sampleObjectType.fields.find(
-        field => field.name === "describedField",
+      const deprecatedField = sampleObjectType.fields.find(
+        field => field.name === "deprecatedField",
       )!;
-      const describedGetterField = sampleObjectType.fields.find(
-        field => field.name === "describedGetterField",
+      const deprecatedGetterField = sampleObjectType.fields.find(
+        field => field.name === "deprecatedGetterField",
       )!;
       const methodField = sampleObjectType.fields.find(field => field.name === "methodField")!;
 
       expect(normalField.isDeprecated).toBeFalsy();
       expect(normalField.deprecationReason).toBeNull();
-      expect(describedField.isDeprecated).toBeTruthy();
-      expect(describedField.deprecationReason).toEqual("sample object field deprecation reason");
-      expect(describedGetterField.isDeprecated).toBeTruthy();
-      expect(describedGetterField.deprecationReason).toEqual(
+      expect(deprecatedField.isDeprecated).toBeTruthy();
+      expect(deprecatedField.deprecationReason).toEqual("sample object field deprecation reason");
+      expect(deprecatedGetterField.isDeprecated).toBeTruthy();
+      expect(deprecatedGetterField.deprecationReason).toEqual(
         "sample object getter field deprecation reason",
       );
       expect(methodField.isDeprecated).toBeTruthy();
@@ -91,6 +148,18 @@ describe("Deprecation", () => {
       );
     });
 
+    it("should generate proper input type fields deprecation reason", async () => {
+      const sampleInputType = schema.getType("SampleInput")!;
+      const sampleInputTypeSDL = printType(sampleInputType);
+
+      expect(sampleInputTypeSDL).toMatchInlineSnapshot(`
+        "input SampleInput {
+          normalField: String!
+          deprecatedField: String @deprecated(reason: \\"sample input field deprecation reason\\")
+        }"
+      `);
+    });
+
     it("should generate proper query type deprecation reason", async () => {
       const normalQuery = queryType.fields.find(field => field.name === "normalQuery")!;
       const describedQuery = queryType.fields.find(field => field.name === "describedQuery")!;
@@ -112,5 +181,23 @@ describe("Deprecation", () => {
       expect(describedMutation.isDeprecated).toBeTruthy();
       expect(describedMutation.deprecationReason).toEqual("sample mutation deprecation reason");
     });
+
+    it("should generate proper single arg deprecation reason", async () => {
+      const queryObjectType = schema.getQueryType()!;
+      const queryObjectTypeSDL = printType(queryObjectType);
+
+      expect(queryObjectTypeSDL).toContain(
+        'deprecatedArgQuery(arg: String @deprecated(reason: "sample query arg deprecation reason"))',
+      );
+    });
+
+    it("should generate proper args type fields deprecation reason", async () => {
+      const queryObjectType = schema.getQueryType()!;
+      const queryObjectTypeSDL = printType(queryObjectType);
+
+      expect(queryObjectTypeSDL).toContain(
+        'argsQuery(normalArg: String!, deprecatedArg: String @deprecated(reason: "sample args field deprecation reason")): SampleObject!',
+      );
+    });
   });
 });