Skip to content

Commit 873eab4

Browse files
smyrickbrennantaylor
authored andcommitted
feat: allow directives on other types (#174)
* feat: allow directives on other types * feat: add example directives and SDL controller
1 parent 0658380 commit 873eab4

File tree

19 files changed

+239
-48
lines changed

19 files changed

+239
-48
lines changed

example/src/main/kotlin/com/expedia/graphql/sample/Application.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ class Application {
6464
SchemaPrinter.Options.defaultOptions()
6565
.includeScalarTypes(true)
6666
.includeExtendedScalarTypes(true)
67+
.includeIntrospectionTypes(true)
6768
.includeSchemaDefintion(true)
6869
)
6970

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.expedia.graphql.sample.controllers
2+
3+
import graphql.schema.GraphQLSchema
4+
import graphql.schema.idl.SchemaPrinter
5+
import org.springframework.http.MediaType
6+
import org.springframework.web.bind.annotation.GetMapping
7+
import org.springframework.web.bind.annotation.RestController
8+
9+
@RestController
10+
class SDLController(
11+
val schema: GraphQLSchema,
12+
val schemaPrinter: SchemaPrinter
13+
) {
14+
15+
@GetMapping(
16+
produces = [MediaType.TEXT_PLAIN_VALUE],
17+
value = ["/sdl"]
18+
)
19+
fun sdl() = schemaPrinter.print(schema)
20+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.expedia.graphql.sample.directives
2+
3+
import com.expedia.graphql.annotations.GraphQLDirective
4+
import graphql.introspection.Introspection.DirectiveLocation.ARGUMENT_DEFINITION
5+
import graphql.introspection.Introspection.DirectiveLocation.ENUM
6+
import graphql.introspection.Introspection.DirectiveLocation.ENUM_VALUE
7+
import graphql.introspection.Introspection.DirectiveLocation.FIELD
8+
import graphql.introspection.Introspection.DirectiveLocation.FIELD_DEFINITION
9+
import graphql.introspection.Introspection.DirectiveLocation.FRAGMENT_DEFINITION
10+
import graphql.introspection.Introspection.DirectiveLocation.FRAGMENT_SPREAD
11+
import graphql.introspection.Introspection.DirectiveLocation.INLINE_FRAGMENT
12+
import graphql.introspection.Introspection.DirectiveLocation.INPUT_FIELD_DEFINITION
13+
import graphql.introspection.Introspection.DirectiveLocation.INPUT_OBJECT
14+
import graphql.introspection.Introspection.DirectiveLocation.INTERFACE
15+
import graphql.introspection.Introspection.DirectiveLocation.MUTATION
16+
import graphql.introspection.Introspection.DirectiveLocation.OBJECT
17+
import graphql.introspection.Introspection.DirectiveLocation.QUERY
18+
import graphql.introspection.Introspection.DirectiveLocation.SCALAR
19+
import graphql.introspection.Introspection.DirectiveLocation.SCHEMA
20+
import graphql.introspection.Introspection.DirectiveLocation.UNION
21+
22+
/**
23+
* Valid on all locations
24+
*/
25+
@GraphQLDirective(locations = [
26+
QUERY,
27+
MUTATION,
28+
FIELD,
29+
FRAGMENT_DEFINITION,
30+
FRAGMENT_SPREAD,
31+
INLINE_FRAGMENT,
32+
SCHEMA,
33+
SCALAR,
34+
OBJECT,
35+
FIELD_DEFINITION,
36+
ARGUMENT_DEFINITION,
37+
INTERFACE,
38+
UNION,
39+
ENUM,
40+
ENUM_VALUE,
41+
INPUT_OBJECT,
42+
INPUT_FIELD_DEFINITION
43+
])
44+
annotation class SimpleDirective
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.expedia.graphql.sample.query
2+
3+
import com.expedia.graphql.sample.directives.SimpleDirective
4+
import org.springframework.stereotype.Component
5+
6+
@Component
7+
class DirectiveQuery : Query {
8+
9+
@SimpleDirective
10+
fun directiveFunction(@SimpleDirective value: String) = SimpleDataClass(value)
11+
}
12+
13+
@SimpleDirective
14+
data class SimpleDataClass(
15+
16+
@SimpleDirective
17+
val value: String = "goodbye"
18+
)

src/main/kotlin/com/expedia/graphql/generator/types/DirectiveTypeBuilder.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,9 @@ import com.expedia.graphql.annotations.GraphQLDirective as GraphQLDirectiveAnnot
1414
internal class DirectiveTypeBuilder(generator: SchemaGenerator) : TypeBuilder(generator) {
1515

1616
internal fun directives(element: KAnnotatedElement): List<GraphQLDirective> =
17-
element.annotations.asSequence()
17+
element.annotations
1818
.mapNotNull { it.getDirectiveInfo() }
1919
.map(this::getDirective)
20-
.toList()
2120

2221
private fun getDirective(directiveInfo: DirectiveInfo): GraphQLDirective {
2322

src/main/kotlin/com/expedia/graphql/generator/types/EnumTypeBuilder.kt

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,27 @@ internal class EnumTypeBuilder(generator: SchemaGenerator) : TypeBuilder(generat
1616
enumBuilder.name(kClass.getSimpleName())
1717
enumBuilder.description(kClass.getGraphQLDescription())
1818

19+
generator.directives(kClass).forEach {
20+
enumBuilder.withDirective(it)
21+
}
22+
1923
kClass.java.enumConstants.forEach {
20-
val valueBuilder = GraphQLEnumValueDefinition.newEnumValueDefinition()
24+
enumBuilder.value(getEnumValueDefinition(it, kClass))
25+
}
2126

22-
valueBuilder.name(it.name)
23-
valueBuilder.value(it.name)
27+
return enumBuilder.build()
28+
}
2429

25-
val valueField = kClass.java.getField(it.name)
26-
valueBuilder.description(valueField.getGraphQLDescription())
27-
valueBuilder.deprecationReason(valueField.getDeprecationReason())
30+
private fun getEnumValueDefinition(enum: Enum<*>, kClass: KClass<out Enum<*>>): GraphQLEnumValueDefinition {
31+
val valueBuilder = GraphQLEnumValueDefinition.newEnumValueDefinition()
2832

29-
enumBuilder.value(valueBuilder.build())
30-
}
31-
return enumBuilder.build()
33+
valueBuilder.name(enum.name)
34+
valueBuilder.value(enum.name)
35+
36+
val valueField = kClass.java.getField(enum.name)
37+
valueBuilder.description(valueField.getGraphQLDescription())
38+
valueBuilder.deprecationReason(valueField.getDeprecationReason())
39+
40+
return valueBuilder.build()
3241
}
3342
}

src/main/kotlin/com/expedia/graphql/generator/types/InputObjectTypeBuilder.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ internal class InputObjectTypeBuilder(generator: SchemaGenerator) : TypeBuilder(
2121
builder.name(kClass.getSimpleName(isInputClass = true))
2222
builder.description(kClass.getGraphQLDescription())
2323

24+
generator.directives(kClass).forEach {
25+
builder.withDirective(it)
26+
}
27+
2428
// It does not make sense to run functions against the input types so we only process the properties
2529
kClass.getValidProperties(config.hooks)
2630
.forEach { builder.field(inputProperty(it, kClass)) }
@@ -35,6 +39,10 @@ internal class InputObjectTypeBuilder(generator: SchemaGenerator) : TypeBuilder(
3539
builder.name(prop.name)
3640
builder.type(graphQLTypeOf(prop.returnType, true, prop.isPropertyGraphQLID(parentClass)).safeCast<GraphQLInputType>())
3741

42+
generator.directives(prop).forEach {
43+
builder.withDirective(it)
44+
}
45+
3846
return builder.build()
3947
}
4048
}

src/main/kotlin/com/expedia/graphql/generator/types/InterfaceTypeBuilder.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ internal class InterfaceTypeBuilder(generator: SchemaGenerator) : TypeBuilder(ge
2020
builder.name(kClass.getSimpleName())
2121
builder.description(kClass.getGraphQLDescription())
2222

23+
generator.directives(kClass).forEach {
24+
builder.withDirective(it)
25+
}
26+
2327
kClass.getValidProperties(config.hooks)
2428
.forEach { builder.field(generator.property(it, kClass)) }
2529

src/main/kotlin/com/expedia/graphql/generator/types/MutationTypeBuilder.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ internal class MutationTypeBuilder(generator: SchemaGenerator) : TypeBuilder(gen
2424
throw InvalidMutationTypeException(mutation.kClass)
2525
}
2626

27+
generator.directives(mutation.kClass).forEach {
28+
mutationBuilder.withDirective(it)
29+
}
30+
2731
mutation.kClass.getValidFunctions(config.hooks)
2832
.forEach {
2933
val function = generator.function(it, mutation.obj)

src/main/kotlin/com/expedia/graphql/generator/types/QueryTypeBuilder.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ internal class QueryTypeBuilder(generator: SchemaGenerator) : TypeBuilder(genera
2626
throw InvalidQueryTypeException(query.kClass)
2727
}
2828

29+
generator.directives(query.kClass).forEach {
30+
queryBuilder.withDirective(it)
31+
}
32+
2933
query.kClass.getValidFunctions(config.hooks)
3034
.forEach {
3135
val function = generator.function(it, query.obj)

src/main/kotlin/com/expedia/graphql/generator/types/UnionTypeBuilder.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ internal class UnionTypeBuilder(generator: SchemaGenerator) : TypeBuilder(genera
2323
builder.description(kClass.getGraphQLDescription())
2424
builder.typeResolver { env: TypeResolutionEnvironment -> env.schema.getObjectType(env.getObject<Any>().javaClass.simpleName) }
2525

26+
generator.directives(kClass).forEach {
27+
builder.withDirective(it)
28+
}
29+
2630
val implementations = subTypeMapper.getSubTypesOf(kClass)
2731
implementations.forEach {
2832
val objectType = state.cache.get(TypesCacheKey(it.kotlin.createType(), false))

src/test/kotlin/com/expedia/graphql/generator/types/EnumTypeTest.kt renamed to src/test/kotlin/com/expedia/graphql/generator/types/EnumTypeBuilderTest.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
package com.expedia.graphql.generator.types
22

33
import com.expedia.graphql.annotations.GraphQLDescription
4+
import com.expedia.graphql.utils.SimpleDirective
45
import org.junit.jupiter.api.Test
56
import kotlin.test.assertEquals
67
import kotlin.test.assertFalse
78
import kotlin.test.assertNotNull
89
import kotlin.test.assertNull
910
import kotlin.test.assertTrue
1011

11-
internal class EnumTypeTest : TypeTestHelper() {
12+
internal class EnumTypeBuilderTest : TypeTestHelper() {
1213

1314
@Suppress("Detekt.UnusedPrivateClass")
1415
@GraphQLDescription("MyTestEnum description")
16+
@SimpleDirective
1517
private enum class MyTestEnum {
1618
@GraphQLDescription("enum 'ONE' description")
19+
@SimpleDirective
1720
ONE,
1821

1922
@GraphQLDescription("enum 'TWO' description")
@@ -67,4 +70,11 @@ internal class EnumTypeTest : TypeTestHelper() {
6770
assertTrue(three.isDeprecated)
6871
assertEquals("THREE is out, replace with TWO", three.deprecationReason)
6972
}
73+
74+
@Test
75+
fun `Enum classes can have directives`() {
76+
val gqlEnum = assertNotNull(builder.enumType(MyTestEnum::class))
77+
assertEquals(1, gqlEnum.directives.size)
78+
assertEquals("simpleDirective", gqlEnum.directives.first().name)
79+
}
7080
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.expedia.graphql.generator.types
2+
3+
import com.expedia.graphql.annotations.GraphQLDescription
4+
import com.expedia.graphql.utils.SimpleDirective
5+
import org.junit.jupiter.api.Test
6+
import kotlin.test.assertEquals
7+
8+
internal class InputObjectTypeBuilderTest : TypeTestHelper() {
9+
10+
private lateinit var builder: InputObjectTypeBuilder
11+
12+
override fun beforeTest() {
13+
builder = InputObjectTypeBuilder(generator)
14+
}
15+
16+
@Suppress("Detekt.UnusedPrivateClass")
17+
@GraphQLDescription("The truth")
18+
@SimpleDirective
19+
private class InputClass {
20+
21+
@SimpleDirective
22+
val myField: String = "car"
23+
}
24+
25+
@Test
26+
fun `Test naming`() {
27+
val result = builder.inputObjectType(InputClass::class)
28+
assertEquals("InputClassInput", result.name)
29+
}
30+
31+
@Test
32+
fun `Test description`() {
33+
val result = builder.inputObjectType(InputClass::class)
34+
assertEquals("The truth", result.description)
35+
}
36+
37+
@Test
38+
fun `directives should be on input objects`() {
39+
val result = builder.inputObjectType(InputClass::class)
40+
assertEquals(1, result.directives.size)
41+
assertEquals("simpleDirective", result.directives.first().name)
42+
}
43+
44+
@Test
45+
fun `directives should be on input object fields`() {
46+
val result = builder.inputObjectType(InputClass::class)
47+
assertEquals(1, result.fields.first().directives.size)
48+
assertEquals("simpleDirective", result.fields.first().directives.first().name)
49+
}
50+
}

src/test/kotlin/com/expedia/graphql/generator/types/InputObjectTypeTest.kt

Lines changed: 0 additions & 30 deletions
This file was deleted.

src/test/kotlin/com/expedia/graphql/generator/types/InterfaceTypeTest.kt renamed to src/test/kotlin/com/expedia/graphql/generator/types/InterfaceTypeBuilderTest.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
package com.expedia.graphql.generator.types
22

33
import com.expedia.graphql.annotations.GraphQLDescription
4+
import com.expedia.graphql.utils.SimpleDirective
45
import graphql.schema.GraphQLInterfaceType
56
import org.junit.jupiter.api.Test
67
import kotlin.test.assertEquals
78

8-
internal class InterfaceTypeTest : TypeTestHelper() {
9+
internal class InterfaceTypeBuilderTest : TypeTestHelper() {
910

1011
private lateinit var builder: InterfaceTypeBuilder
1112

@@ -15,6 +16,7 @@ internal class InterfaceTypeTest : TypeTestHelper() {
1516

1617
@Suppress("Detekt.UnusedPrivateClass")
1718
@GraphQLDescription("The truth")
19+
@SimpleDirective
1820
private interface HappyInterface
1921

2022
@Test
@@ -28,4 +30,11 @@ internal class InterfaceTypeTest : TypeTestHelper() {
2830
val result = builder.interfaceType(HappyInterface::class) as? GraphQLInterfaceType
2931
assertEquals("The truth", result?.description)
3032
}
33+
34+
@Test
35+
fun `Interfaces can have directives`() {
36+
val result = builder.interfaceType(HappyInterface::class) as? GraphQLInterfaceType
37+
assertEquals(1, result?.directives?.size)
38+
assertEquals("simpleDirective", result?.directives?.first()?.name)
39+
}
3140
}

0 commit comments

Comments
 (0)