Skip to content

Commit 8614439

Browse files
d4rkendariuszkuc
authored andcommitted
Examples for method and argument based custom directives. (#73)
* Examples for method and argument based custom directives. * Refactoring, code shenanigans * Kotlin to v1.3.10 to fix a reflection issue. (Library kotlin dependency was raised for the name reason) * Refactoring
1 parent 05a1555 commit 8614439

File tree

10 files changed

+150
-7
lines changed

10 files changed

+150
-7
lines changed

example/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060

6161
<properties>
6262
<java.version>1.8</java.version>
63-
<kotlin.version>1.2.71</kotlin.version>
63+
<kotlin.version>1.3.10</kotlin.version>
6464
<junit.version>4.12</junit.version>
6565
<graphiql.version>5.0.2</graphiql.version>
6666
<graphql-kotlin.version>0.0.24</graphql-kotlin.version>

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.expedia.graphql.sample
33
import com.expedia.graphql.TopLevelObjectDef
44
import com.expedia.graphql.sample.context.MyGraphQLContextBuilder
55
import com.expedia.graphql.sample.dataFetchers.SpringDataFetcherFactory
6+
import com.expedia.graphql.sample.directives.DirectiveWiringFactory
67
import com.expedia.graphql.sample.exceptions.CustomDataFetcherExceptionHandler
78
import com.expedia.graphql.sample.extension.CustomSchemaGeneratorHooks
89
import com.expedia.graphql.sample.mutation.Mutation
@@ -36,9 +37,12 @@ class Application {
3637
private val logger = LoggerFactory.getLogger(Application::class.java)
3738

3839
@Bean
39-
fun schemaConfig(dataFetcherFactory: SpringDataFetcherFactory, validator: Validator): SchemaGeneratorConfig = SchemaGeneratorConfig(
40+
fun wiringFactory() = DirectiveWiringFactory()
41+
42+
@Bean
43+
fun schemaConfig(dataFetcherFactory: SpringDataFetcherFactory, validator: Validator, wiringFactory: DirectiveWiringFactory): SchemaGeneratorConfig = SchemaGeneratorConfig(
4044
supportedPackages = listOf("com.expedia"),
41-
hooks = CustomSchemaGeneratorHooks(validator),
45+
hooks = CustomSchemaGeneratorHooks(validator, wiringFactory),
4246
dataFetcherFactory = dataFetcherFactory
4347
)
4448

@@ -85,7 +89,7 @@ class Application {
8589
@Bean
8690
fun graphQLObjectMapper(): GraphQLObjectMapper = GraphQLObjectMapper.newBuilder()
8791
.withObjectMapperConfigurer(ObjectMapperConfigurer { it.registerModule(KotlinModule()) })
88-
.withGraphQLErrorHandler( GraphQLErrorHandler { it })
92+
.withGraphQLErrorHandler(GraphQLErrorHandler { it })
8993
.build()
9094

9195
@Bean
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.expedia.graphql.sample.directives
2+
3+
import com.expedia.graphql.annotations.GraphQLDirective
4+
5+
@GraphQLDirective(description = "This validates string input")
6+
annotation class CakeOnly
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.expedia.graphql.sample.directives
2+
3+
import graphql.schema.DataFetcher
4+
import graphql.schema.GraphQLFieldDefinition
5+
import graphql.schema.idl.SchemaDirectiveWiringEnvironment
6+
7+
class CakeOnlyDirectiveWiring : DirectiveWiring {
8+
9+
override fun isApplicable(environment: SchemaDirectiveWiringEnvironment<*>): Boolean =
10+
environment.directive.name == getDirectiveName(CakeOnly::class)
11+
12+
13+
override fun onField(wiringEnv: SchemaDirectiveWiringEnvironment<GraphQLFieldDefinition>): GraphQLFieldDefinition {
14+
val field = wiringEnv.element
15+
val originalDataFetcher: DataFetcher<Any> = field.dataFetcher
16+
val cakeOnlyFetcher = DataFetcher<Any> { dataEnv ->
17+
val strArg: String? = dataEnv.getArgument(wiringEnv.element.arguments[0].name) as String?
18+
if (strArg != "Cake") {
19+
throw RuntimeException("The cake is a lie!")
20+
}
21+
originalDataFetcher.get(dataEnv)
22+
}
23+
return field.transform { it.dataFetcher(cakeOnlyFetcher) }
24+
}
25+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.expedia.graphql.sample.directives
2+
3+
import com.google.common.base.CaseFormat
4+
import graphql.schema.idl.SchemaDirectiveWiring
5+
import graphql.schema.idl.SchemaDirectiveWiringEnvironment
6+
import kotlin.reflect.KClass
7+
8+
interface DirectiveWiring : SchemaDirectiveWiring {
9+
10+
fun isApplicable(environment: SchemaDirectiveWiringEnvironment<*>): Boolean
11+
12+
}
13+
14+
fun getDirectiveName(kClass: KClass<out Annotation>): String =
15+
CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, kClass.simpleName!!)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.expedia.graphql.sample.directives
2+
3+
import graphql.schema.idl.SchemaDirectiveWiring
4+
import graphql.schema.idl.SchemaDirectiveWiringEnvironment
5+
import graphql.schema.idl.WiringFactory
6+
7+
class DirectiveWiringFactory : WiringFactory {
8+
private val wiring = listOf(
9+
StringEvalDirectiveWiring(),
10+
CakeOnlyDirectiveWiring()
11+
)
12+
13+
override fun providesSchemaDirectiveWiring(environment: SchemaDirectiveWiringEnvironment<*>): Boolean = true
14+
15+
override fun getSchemaDirectiveWiring(environment: SchemaDirectiveWiringEnvironment<*>): SchemaDirectiveWiring? =
16+
wiring.asSequence()
17+
.filter { it.isApplicable(environment) }
18+
.singleOrNull()
19+
20+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.expedia.graphql.sample.directives
2+
3+
import com.expedia.graphql.annotations.GraphQLDirective
4+
5+
@GraphQLDirective(description = "This validates string input")
6+
annotation class StringEval(
7+
val default: String = "",
8+
val lowerCase: Boolean = false
9+
)
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.expedia.graphql.sample.directives
2+
3+
import graphql.schema.DataFetcher
4+
import graphql.schema.DataFetchingEnvironmentBuilder
5+
import graphql.schema.GraphQLFieldDefinition
6+
import graphql.schema.idl.SchemaDirectiveWiringEnvironment
7+
8+
class StringEvalDirectiveWiring : DirectiveWiring {
9+
private val dirName = getDirectiveName(StringEval::class)
10+
11+
override fun isApplicable(environment: SchemaDirectiveWiringEnvironment<*>): Boolean =
12+
(environment.element as? GraphQLFieldDefinition)?.arguments?.any { it.getDirective(dirName) != null }
13+
?: false
14+
15+
override fun onField(wiringEnv: SchemaDirectiveWiringEnvironment<GraphQLFieldDefinition>): GraphQLFieldDefinition {
16+
val field = wiringEnv.element
17+
val originalDataFetcher: DataFetcher<Any> = field.dataFetcher
18+
19+
val defaultValueFetcher = DataFetcher<Any> { dataEnv ->
20+
val newArguments = HashMap(dataEnv.arguments)
21+
wiringEnv.element.arguments.asSequence()
22+
.map { gqlArg ->
23+
val strArg: String? = dataEnv.getArgument(gqlArg.name) as String?
24+
Pair(gqlArg, strArg)
25+
}
26+
.forEach { (gqlArg, value) ->
27+
if (gqlArg.getDirective(dirName).getArgument(StringEval::lowerCase.name).value as Boolean) {
28+
newArguments[gqlArg.name] = value?.toLowerCase()
29+
}
30+
if (value.isNullOrEmpty()) {
31+
newArguments[gqlArg.name] = gqlArg.getDirective(dirName).getArgument(StringEval::default.name).value as String
32+
}
33+
}
34+
val newEnv = DataFetchingEnvironmentBuilder.newDataFetchingEnvironment(dataEnv)
35+
.arguments(newArguments)
36+
.build()
37+
originalDataFetcher.get(newEnv)
38+
}
39+
return field.transform { it.dataFetcher(defaultValueFetcher) }
40+
}
41+
}

example/src/main/kotlin/com.expedia.graphql.sample/extension/CustomSchemaGeneratorHooks.kt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,24 @@
11
package com.expedia.graphql.sample.extension
22

3+
import com.expedia.graphql.sample.directives.DirectiveWiringFactory
34
import com.expedia.graphql.sample.validation.DataFetcherExecutionValidator
5+
import com.expedia.graphql.schema.generator.directive.DirectiveWiringHelper
46
import com.expedia.graphql.schema.hooks.DataFetcherExecutionPredicate
57
import com.expedia.graphql.schema.hooks.SchemaGeneratorHooks
68
import graphql.language.StringValue
79
import graphql.schema.Coercing
810
import graphql.schema.GraphQLScalarType
911
import graphql.schema.GraphQLType
10-
import java.util.UUID
12+
import java.util.*
1113
import javax.validation.Validator
1214
import kotlin.reflect.KClass
1315
import kotlin.reflect.KType
1416

1517
/**
1618
* Schema generator hook that adds additional scalar types.
1719
*/
18-
class CustomSchemaGeneratorHooks(validator: Validator) : SchemaGeneratorHooks {
19-
20+
class CustomSchemaGeneratorHooks(validator: Validator, wiringFactory: DirectiveWiringFactory) : SchemaGeneratorHooks {
21+
private val directiveWiringHelper = DirectiveWiringHelper(wiringFactory)
2022
/**
2123
* Register additional GraphQL scalar types.
2224
*/
@@ -25,6 +27,10 @@ class CustomSchemaGeneratorHooks(validator: Validator) : SchemaGeneratorHooks {
2527
else -> null
2628
}
2729

30+
override fun onRewireGraphQLType(type: KType, generatedType: GraphQLType): GraphQLType {
31+
return directiveWiringHelper.onWire(generatedType)
32+
}
33+
2834
override val dataFetcherExecutionPredicate: DataFetcherExecutionPredicate? = DataFetcherExecutionValidator(validator)
2935
}
3036

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.expedia.graphql.sample.query
2+
3+
import com.expedia.graphql.annotations.GraphQLDescription
4+
import com.expedia.graphql.sample.directives.CakeOnly
5+
import com.expedia.graphql.sample.directives.StringEval
6+
import org.springframework.stereotype.Component
7+
8+
@Component
9+
class CustomDirectiveQuery : Query {
10+
11+
@GraphQLDescription("Returns a message modified by directives, lower case and non-empty")
12+
fun justWhisper(@StringEval(default = "Default String", lowerCase = true) msg: String): String = msg
13+
14+
@GraphQLDescription("This will only accept 'Cake' as input")
15+
@CakeOnly
16+
fun onlyCake(msg: String): String = "<3"
17+
}

0 commit comments

Comments
 (0)