Skip to content

Commit 0c6fa79

Browse files
smyrickShane Myrickdariuszkuc
authored
Support renaming of enum values (#805)
* Support renaming of enum values: * Update docs/schema-generator/customizing-schemas/renaming-fields.md Co-authored-by: Dariusz Kuc <[email protected]> * Update renaming-fields.md * Add extension unit tests * Flip order for branch coverage Co-authored-by: Shane Myrick <[email protected]> Co-authored-by: Dariusz Kuc <[email protected]>
1 parent 331a161 commit 0c6fa79

File tree

9 files changed

+109
-14
lines changed

9 files changed

+109
-14
lines changed

docs/schema-generator/customizing-schemas/renaming-fields.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ type MyCustomName {
2222
```
2323

2424
## Known Issues
25-
> NOTE: Due to how we deserialize input classes, if you rename a field of an input class you must also annotate the field with the Jackson annotation @JsonProperty. See [issue 493](https://github.com/ExpediaGroup/graphql-kotlin/issues/493) for more info.
25+
> NOTE: Due to how we deserialize input classes, if you rename a field of an input class or an enum value you must also annotate it with the Jackson annotation @JsonProperty. See [issue 493](https://github.com/ExpediaGroup/graphql-kotlin/issues/493) for more info.
2626

2727
```kotlin
2828
data class MyInputClass(
@@ -31,8 +31,25 @@ data class MyInputClass(
3131
val field1: String
3232
)
3333

34+
// GraphQL enums should use UPPER_CASE naming if possible, but any case is supported
35+
enum class Selection {
36+
37+
@JsonProperty("first")
38+
@GraphQLName("first")
39+
ONE,
40+
41+
@JsonProperty("second")
42+
@GraphQLName("second")
43+
TWO
44+
}
45+
3446
class QueryClass {
3547
fun parseData(arg: MyInputClass) = "You sent ${arg.field1}"
48+
49+
fun chooseValue(selection: Selection): String = when (selection) {
50+
Selection.ONE -> "You chose the first value"
51+
Selection.TWO -> "You chose the second value"
52+
}
3653
}
3754
```
3855

@@ -42,7 +59,13 @@ input MyInputClassInput {
4259
renamedField: String!
4360
}
4461

62+
enum Selection {
63+
first,
64+
second
65+
}
66+
4567
type Query {
4668
parseData(arg: MyInputClass!): String!
69+
chooseValue(selection: Selection!): String!
4770
}
4871
```
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2020 Expedia, Inc
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.expediagroup.graphql.examples.model
18+
19+
import com.expediagroup.graphql.annotations.GraphQLDescription
20+
import com.expediagroup.graphql.annotations.GraphQLName
21+
import com.fasterxml.jackson.annotation.JsonProperty
22+
23+
@GraphQLDescription("Use to represent a selection when choosing a value")
24+
enum class Selection {
25+
26+
@GraphQLDescription("Use this when you want the first one")
27+
ONE,
28+
29+
// If we change the name, we need to update Jackson as well
30+
@JsonProperty("second")
31+
@GraphQLName("second")
32+
@GraphQLDescription("Use this when you want the second one")
33+
TWO
34+
}

examples/spring/src/main/kotlin/com/expediagroup/graphql/examples/model/Widget.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,17 @@ import com.expediagroup.graphql.annotations.GraphQLIgnore
2121

2222
@GraphQLDescription("A useful widget")
2323
data class Widget(
24+
2425
@GraphQLDescription("The widget's value that can be null")
2526
var value: Int? = null,
27+
2628
@Deprecated(message = "This field is deprecated", replaceWith = ReplaceWith("value"))
2729
@GraphQLDescription("The widget's deprecated value that shouldn't be used")
2830
val deprecatedValue: Int? = value,
31+
2932
@GraphQLIgnore
3033
val ignoredField: String? = "ignored",
34+
3135
private val hiddenField: String? = "hidden"
3236
) {
3337

examples/spring/src/main/kotlin/com/expediagroup/graphql/examples/query/SimpleQuery.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package com.expediagroup.graphql.examples.query
1818

1919
import com.expediagroup.graphql.annotations.GraphQLDescription
2020
import com.expediagroup.graphql.annotations.GraphQLIgnore
21+
import com.expediagroup.graphql.examples.model.Selection
2122
import com.expediagroup.graphql.spring.operations.Query
2223
import org.springframework.stereotype.Component
2324
import java.util.Random
@@ -76,4 +77,10 @@ class SimpleQuery : Query {
7677
@GraphQLDescription("this field is optional") optionalValue: Int? = null
7778
) =
7879
"required value=$requiredValue, optional value=$optionalValue"
80+
81+
@GraphQLDescription("Demonstrate the usage of custom enum input")
82+
fun getAValue(selection: Selection): String = when (selection) {
83+
Selection.ONE -> "You chose the first one"
84+
Selection.TWO -> "You chose the second one"
85+
}
7986
}

graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/extensions/fieldExtenstions.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717
package com.expediagroup.graphql.generator.extensions
1818

1919
import com.expediagroup.graphql.annotations.GraphQLDescription
20+
import com.expediagroup.graphql.annotations.GraphQLName
2021
import java.lang.reflect.Field
2122

2223
internal fun Field.getGraphQLDescription(): String? = this.getAnnotation(GraphQLDescription::class.java)?.value
2324

2425
internal fun Field.getDeprecationReason(): String? = this.getDeclaredAnnotation(Deprecated::class.java)?.getReason()
26+
27+
internal fun Field.getGraphQLName(): String = this.getAnnotation(GraphQLName::class.java)?.value ?: this.name

graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateArgument.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ internal fun generateArgument(generator: SchemaGenerator, parameter: KParameter)
3737
// This is not currently supported by the GraphQL spec
3838
// https://github.com/graphql/graphql-spec/blob/master/rfcs/InputUnion.md
3939
val unwrappedClass = getUnwrappedClass(parameter)
40-
if (unwrappedClass.isInterface() || unwrappedClass.isUnion()) {
40+
if (unwrappedClass.isUnion() || unwrappedClass.isInterface()) {
4141
throw InvalidInputFieldTypeException(parameter)
4242
}
4343

graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateEnum.kt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import com.expediagroup.graphql.directives.deprecatedDirectiveWithReason
2020
import com.expediagroup.graphql.generator.SchemaGenerator
2121
import com.expediagroup.graphql.generator.extensions.getDeprecationReason
2222
import com.expediagroup.graphql.generator.extensions.getGraphQLDescription
23+
import com.expediagroup.graphql.generator.extensions.getGraphQLName
2324
import com.expediagroup.graphql.generator.extensions.getSimpleName
2425
import com.expediagroup.graphql.generator.extensions.safeCast
2526
import graphql.schema.GraphQLEnumType
@@ -44,12 +45,12 @@ internal fun generateEnum(generator: SchemaGenerator, kClass: KClass<out Enum<*>
4445

4546
private fun getEnumValueDefinition(generator: SchemaGenerator, enum: Enum<*>, kClass: KClass<out Enum<*>>): GraphQLEnumValueDefinition {
4647
val valueBuilder = GraphQLEnumValueDefinition.newEnumValueDefinition()
47-
48-
valueBuilder.name(enum.name)
49-
valueBuilder.value(enum.name)
50-
5148
val valueField = kClass.java.getField(enum.name)
5249

50+
val name = valueField.getGraphQLName()
51+
valueBuilder.name(name)
52+
valueBuilder.value(name)
53+
5354
generateFieldDirectives(generator, valueField).forEach {
5455
valueBuilder.withDirective(it)
5556
}

graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/extensions/FieldExtenstionsKtTest.kt

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,17 @@
1717
package com.expediagroup.graphql.generator.extensions
1818

1919
import com.expediagroup.graphql.annotations.GraphQLDescription
20+
import com.expediagroup.graphql.annotations.GraphQLName
2021
import org.junit.jupiter.api.Test
2122
import kotlin.test.assertEquals
2223
import kotlin.test.assertNull
2324

24-
internal class FieldExtenstionsKtTest {
25+
class FieldExtenstionsKtTest {
2526

26-
internal enum class AnnotatedEnum {
27+
enum class AnnotatedEnum {
2728
@GraphQLDescription("field description")
2829
@Deprecated("do not use", ReplaceWith("TWO"))
30+
@GraphQLName("customOne")
2931
ONE,
3032
TWO
3133
}
@@ -43,4 +45,13 @@ internal class FieldExtenstionsKtTest {
4345
val propertyDeprecation = AnnotatedEnum::class.java.getField("ONE").getDeprecationReason()
4446
assertEquals(expected = "do not use, replace with TWO", actual = propertyDeprecation)
4547
}
48+
49+
@Test
50+
fun `verify @GraphQLName on fields`() {
51+
val customNameField = AnnotatedEnum::class.java.getField("ONE")
52+
assertEquals(expected = "customOne", actual = customNameField.getGraphQLName())
53+
54+
val basicNameField = AnnotatedEnum::class.java.getField("TWO")
55+
assertEquals(expected = "TWO", actual = basicNameField.getGraphQLName())
56+
}
4657
}

graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/types/GenerateEnumTest.kt

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ import kotlin.test.assertNotNull
2727
import kotlin.test.assertNull
2828
import kotlin.test.assertTrue
2929

30-
internal class GenerateEnumTest : TypeTestHelper() {
30+
class GenerateEnumTest : TypeTestHelper() {
3131

3232
@Suppress("Detekt.UnusedPrivateClass")
3333
@GraphQLDescription("MyTestEnum description")
3434
@SimpleDirective
35-
private enum class MyTestEnum {
35+
enum class MyTestEnum {
3636
@GraphQLDescription("enum 'ONE' description")
3737
@SimpleDirective
3838
ONE,
@@ -44,21 +44,25 @@ internal class GenerateEnumTest : TypeTestHelper() {
4444
@SimpleDirective
4545
@CustomDirective("foo bar")
4646
@Deprecated("THREE is out", replaceWith = ReplaceWith("TWO"))
47-
THREE
47+
THREE,
48+
49+
@GraphQLName("customFour")
50+
FOUR
4851
}
4952

5053
@Suppress("Detekt.UnusedPrivateClass")
5154
@GraphQLName("MyTestEnumRenamed")
52-
private enum class MyTestEnumCustomName
55+
enum class MyTestEnumCustomName
5356

5457
@Test
5558
fun enumType() {
5659
val actual = generateEnum(generator, MyTestEnum::class)
57-
assertEquals(expected = 3, actual = actual.values.size)
60+
assertEquals(expected = 4, actual = actual.values.size)
5861
assertEquals(expected = "MyTestEnum", actual = actual.name)
5962
assertEquals(expected = "ONE", actual = actual.values[0].value)
6063
assertEquals(expected = "TWO", actual = actual.values[1].value)
6164
assertEquals(expected = "THREE", actual = actual.values[2].value)
65+
assertEquals(expected = "customFour", actual = actual.values[3].value)
6266
}
6367

6468
@Test
@@ -105,7 +109,8 @@ internal class GenerateEnumTest : TypeTestHelper() {
105109
fun `Enum values can have directives`() {
106110
val gqlEnum = assertNotNull(generateEnum(generator, MyTestEnum::class))
107111

108-
val enumValuesDirectives = gqlEnum.values.last().directives
112+
val enumValuesDirectives = gqlEnum.values.find { it.name == "THREE" }?.directives
113+
assertNotNull(enumValuesDirectives)
109114
assertEquals(3, enumValuesDirectives.size)
110115
assertEquals("simpleDirective", enumValuesDirectives[0].name)
111116
assertEquals("customName", enumValuesDirectives[1].name)
@@ -118,4 +123,11 @@ internal class GenerateEnumTest : TypeTestHelper() {
118123
assertEquals(1, gqlEnum.values.first().directives.size)
119124
assertEquals("simpleDirective", gqlEnum.values.first().directives.first().name)
120125
}
126+
127+
@Test
128+
fun `Enum values can have a custom name`() {
129+
val gqlEnum = assertNotNull(generateEnum(generator, MyTestEnum::class))
130+
131+
assertNotNull(gqlEnum.getValue("customFour"))
132+
}
121133
}

0 commit comments

Comments
 (0)