Repository: graphql-java-kickstart/graphql-java-tools
Pull request #696 (2022-09-13 14:48:25)

Support @deprecated on arguments and input fields
This pull request adds support for the `@deprecated` directive on arguments and input fields.

Support for this was added in version 17.0 of `graphql-java`.

## Checklist
<!-- Change [ ] to [x] to indicate you acknowledge the check. -->
- [x] Pull requests follows the [contribution guide](https://github.com/graphql-java-kickstart/graphql-java-tools/wiki/Contribution-guide)
- [x] New or modified functionality is covered by tests

## Description

Fairly simple changes - I added support for this to the main `SchemaParser` class, and added tests for the new and previously-existing functionality.


Patch:
diff --git a/src/main/kotlin/graphql/kickstart/tools/SchemaParser.kt b/src/main/kotlin/graphql/kickstart/tools/SchemaParser.kt
index 44926f9..24544ad 100644
--- a/src/main/kotlin/graphql/kickstart/tools/SchemaParser.kt
+++ b/src/main/kotlin/graphql/kickstart/tools/SchemaParser.kt
@@ -168,6 +168,7 @@ class SchemaParser internal constructor(
                     .definition(inputDefinition)
                     .description(getDocumentation(inputDefinition, options))
                     .apply { inputDefinition.defaultValue?.let { v -> defaultValueLiteral(v) } }
+                    .apply { getDeprecated(inputDefinition.directives)?.let { deprecate(it) } }
                     .type(determineInputType(inputDefinition.type, inputObjects, referencingInputObjects))
                     .withAppliedDirectives(*buildAppliedDirectives(inputDefinition.directives))
                 builder.field(fieldBuilder.build())
@@ -279,6 +280,7 @@ class SchemaParser internal constructor(
                 .definition(argumentDefinition)
                 .description(getDocumentation(argumentDefinition, options))
                 .type(determineInputType(argumentDefinition.type, inputObjects, setOf()))
+                .apply { getDeprecated(argumentDefinition.directives)?.let { deprecate(it) } }
                 .apply { argumentDefinition.defaultValue?.let { defaultValueLiteral(it) } }
                 .withAppliedDirectives(*buildAppliedDirectives(argumentDefinition.directives))
 
@@ -305,6 +307,7 @@ class SchemaParser internal constructor(
                         .definition(arg)
                         .description(getDocumentation(arg, options))
                         .type(determineInputType(arg.type, inputObjects, setOf()))
+                        .apply { getDeprecated(arg.directives)?.let { deprecate(it) } }
                         .apply { arg.defaultValue?.let { defaultValueLiteral(it) } }
                         .withAppliedDirectives(*buildAppliedDirectives(arg.directives))
                         .build())


Test patch:
diff --git a/src/test/kotlin/graphql/kickstart/tools/DeprecatedDirectiveTest.kt b/src/test/kotlin/graphql/kickstart/tools/DeprecatedDirectiveTest.kt
new file mode 100644
index 0000000..b611a06
--- /dev/null
+++ b/src/test/kotlin/graphql/kickstart/tools/DeprecatedDirectiveTest.kt
@@ -0,0 +1,387 @@
+package graphql.kickstart.tools
+
+import graphql.relay.Connection
+import graphql.relay.SimpleListConnection
+import graphql.schema.DataFetchingEnvironment
+import graphql.schema.GraphQLEnumType
+import graphql.schema.GraphQLInputObjectType
+import graphql.schema.GraphQLObjectType
+import org.junit.Test
+
+class DeprecatedDirectiveTest {
+    @Test
+    fun `should apply @deprecated directive on output field with default reason`() {
+        val schema = SchemaParser.newParser()
+            .schemaString(
+                """
+                type Query {
+                    users: UserConnection
+                }
+                
+                type UserConnection {
+                    edges: [UserEdge!]!
+                }
+                
+                type UserEdge {
+                    node: User!
+                } 
+                
+                type User {
+                    id: ID!
+                    name: String @deprecated
+                }
+                """)
+            .resolvers(UsersQueryResolver())
+            .build()
+            .makeExecutableSchema()
+
+        val userType = schema.getType("User") as GraphQLObjectType
+        val nameDefinition = userType.getField("name")
+
+        assert(nameDefinition.isDeprecated)
+        assertEquals(nameDefinition.deprecationReason, "No longer supported")
+    }
+
+    @Test
+    fun `should apply @deprecated directive on output field with custom reason`() {
+        val schema = SchemaParser.newParser()
+            .schemaString(
+                """
+                type Query {
+                    users: UserConnection
+                }
+                
+                type UserConnection {
+                    edges: [UserEdge!]!
+                }
+                
+                type UserEdge {
+                    node: User!
+                } 
+                
+                type User {
+                    id: ID!
+                    name: String @deprecated(reason: "Use firstName and lastName instead")
+                }
+                """)
+            .resolvers(UsersQueryResolver())
+            .build()
+            .makeExecutableSchema()
+
+        val userType = schema.getType("User") as GraphQLObjectType
+        val nameDefinition = userType.getField("name")
+
+        assert(nameDefinition.isDeprecated)
+        assertEquals(nameDefinition.deprecationReason, "Use firstName and lastName instead")
+    }
+
+    @Test
+    fun `should apply @deprecated directive on enum value with default reason`() {
+        val schema = SchemaParser.newParser()
+            .schemaString(
+                """
+                type Query {
+                    users: UserConnection
+                }
+                
+                type UserConnection {
+                    edges: [UserEdge!]!
+                }
+                
+                type UserEdge {
+                    node: User!
+                } 
+
+                enum UserType {
+                    JEDI
+                    BASIC
+                    DROID @deprecated
+                }
+                
+                type User {
+                    id: ID!
+                    name: String
+                    type: UserType
+                }
+                """)
+            .resolvers(UsersQueryResolver())
+            .build()
+            .makeExecutableSchema()
+
+        val userTypeEnum = schema.getType("UserType") as GraphQLEnumType
+        val droidValue = userTypeEnum.getValue("DROID")
+
+        assert(droidValue.isDeprecated)
+        assertEquals(droidValue.deprecationReason, "No longer supported")
+    }
+
+    @Test
+    fun `should apply @deprecated directive on enum value with custom reason`() {
+        val schema = SchemaParser.newParser()
+            .schemaString(
+                """
+                type Query {
+                    users: UserConnection
+                }
+                
+                type UserConnection {
+                    edges: [UserEdge!]!
+                }
+                
+                type UserEdge {
+                    node: User!
+                } 
+
+                enum UserType {
+                    JEDI
+                    BASIC
+                    DROID @deprecated(reason: "This value is no longer used")
+                }
+                
+                type User {
+                    id: ID!
+                    name: String
+                    type: UserType
+                }
+                """)
+            .resolvers(UsersQueryResolver())
+            .build()
+            .makeExecutableSchema()
+
+        val userTypeEnum = schema.getType("UserType") as GraphQLEnumType
+        val droidValue = userTypeEnum.getValue("DROID")
+
+        assert(droidValue.isDeprecated)
+        assertEquals(droidValue.deprecationReason, "This value is no longer used")
+    }
+
+    @Test
+    fun `should apply @deprecated directive on argument with default reason`() {
+        val schema = SchemaParser.newParser()
+            .schemaString(
+                """
+                type Query {
+                    users(first: Int @deprecated): UserConnection
+                }
+                
+                type UserConnection {
+                    edges: [UserEdge!]!
+                }
+                
+                type UserEdge {
+                    node: User!
+                } 
+                
+                type User {
+                    id: ID!
+                    name: String
+                }
+                """)
+            .resolvers(UsersQueryResolver())
+            .build()
+            .makeExecutableSchema()
+
+        val usersQuery = schema.queryType.getField("users")
+        val firstArgument = usersQuery.getArgument("first")
+
+        assert(firstArgument.isDeprecated)
+        assertEquals(firstArgument.deprecationReason, "No longer supported")
+    }
+
+    @Test
+    fun `should apply @deprecated directive on argument with custom reason`() {
+        val schema = SchemaParser.newParser()
+            .schemaString(
+                """
+                type Query {
+                    users(first: Int @deprecated(reason: "Please do not use this argument")): UserConnection
+                }
+                
+                type UserConnection {
+                    edges: [UserEdge!]!
+                }
+                
+                type UserEdge {
+                    node: User!
+                } 
+                
+                type User {
+                    id: ID!
+                    name: String
+                }
+                """)
+            .resolvers(UsersQueryResolver())
+            .build()
+            .makeExecutableSchema()
+
+        val usersQuery = schema.queryType.getField("users")
+        val firstArgument = usersQuery.getArgument("first")
+
+        assert(firstArgument.isDeprecated)
+        assertEquals(firstArgument.deprecationReason, "Please do not use this argument")
+    }
+
+    @Test
+    fun `should apply @deprecated directive on directive argument with default reason`() {
+        val schema = SchemaParser.newParser()
+            .schemaString(
+                """
+                directive @uppercase(firstCharacterOnly: Boolean @deprecated) on FIELD_DEFINITION
+
+                type Query {
+                    users: UserConnection
+                }
+
+                type UserConnection {
+                    edges: [UserEdge!]!
+                }
+
+                type UserEdge {
+                    node: User!
+                }
+
+                type User {
+                    id: ID!
+                    name: String
+                }
+                """)
+            .resolvers(UsersQueryResolver())
+            .build()
+            .makeExecutableSchema()
+
+        val directive = schema.getDirective("uppercase")
+        val argument = directive.getArgument("firstCharacterOnly")
+
+        assert(argument.isDeprecated)
+        assertEquals(argument.deprecationReason, "No longer supported")
+    }
+
+    @Test
+    fun `should apply @deprecated directive on directive argument with custom reason`() {
+        val schema = SchemaParser.newParser()
+            .schemaString(
+                """
+                directive @uppercase(firstCharacterOnly: Boolean @deprecated(reason: "Do not use this thing")) on FIELD_DEFINITION
+
+                type Query {
+                    users: UserConnection
+                }
+
+                type UserConnection {
+                    edges: [UserEdge!]!
+                }
+
+                type UserEdge {
+                    node: User!
+                }
+
+                type User {
+                    id: ID!
+                    name: String
+                }
+                """)
+            .resolvers(UsersQueryResolver())
+            .build()
+            .makeExecutableSchema()
+
+        val directive = schema.getDirective("uppercase")
+        val argument = directive.getArgument("firstCharacterOnly")
+
+        assert(argument.isDeprecated)
+        assertEquals(argument.deprecationReason, "Do not use this thing")
+    }
+
+    @Test
+    fun `should apply @deprecated directive on input field with default reason`() {
+        val schema = SchemaParser.newParser()
+            .schemaString(
+                """
+                type Query {
+                    users(connectionInput: ConnectionInput): UserConnection
+                }
+                
+                input ConnectionInput {
+                    first: Int @deprecated
+                }
+                
+                type UserConnection {
+                    edges: [UserEdge!]!
+                }
+                
+                type UserEdge {
+                    node: User!
+                } 
+                
+                type User {
+                    id: ID!
+                    name: String
+                }
+                """)
+            .resolvers(UsersQueryResolver())
+            .build()
+            .makeExecutableSchema()
+
+        val connectionInputType = schema.getType("ConnectionInput") as GraphQLInputObjectType
+        val firstField = connectionInputType.getField("first")
+
+        assert(firstField.isDeprecated)
+        assertEquals(firstField.deprecationReason, "No longer supported")
+    }
+
+    @Test
+    fun `should apply @deprecated directive on input field with custom reason`() {
+        val schema = SchemaParser.newParser()
+            .schemaString(
+                """
+                type Query {
+                    users(connectionInput: ConnectionInput): UserConnection
+                }
+                
+                input ConnectionInput {
+                    first: Int @deprecated(reason: "Please do not use this field")
+                }
+                
+                type UserConnection {
+                    edges: [UserEdge!]!
+                }
+                
+                type UserEdge {
+                    node: User!
+                } 
+                
+                type User {
+                    id: ID!
+                    name: String
+                }
+                """)
+            .resolvers(UsersQueryResolver())
+            .build()
+            .makeExecutableSchema()
+
+        val connectionInputType = schema.getType("ConnectionInput") as GraphQLInputObjectType
+        val firstField = connectionInputType.getField("first")
+
+        assert(firstField.isDeprecated)
+        assertEquals(firstField.deprecationReason, "Please do not use this field")
+    }
+
+    private enum class UserType {
+        JEDI,
+        BASIC,
+        DROID
+    }
+
+    private class UsersQueryResolver : GraphQLQueryResolver {
+        fun users(env: DataFetchingEnvironment): Connection<User> {
+            return SimpleListConnection(listOf(User(1L, "Luke Skywalker", "Luke", "Skywalker", UserType.JEDI))).get(env)
+        }
+
+        private data class User(
+            val id: Long,
+            val name: String,
+            val firstName: String,
+            val lastName: String,
+            val type: UserType
+        )
+    }
+}