-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDemoApplication.kt
161 lines (141 loc) · 5.85 KB
/
DemoApplication.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package nl.sourcelabs.graphql.demo
import graphql.kickstart.execution.context.DefaultGraphQLContext
import graphql.kickstart.execution.context.GraphQLContext
import graphql.kickstart.execution.context.GraphQLContextBuilder
import graphql.kickstart.tools.GraphQLQueryResolver
import graphql.kickstart.tools.GraphQLResolver
import graphql.kickstart.tools.SchemaParser
import graphql.schema.DataFetchingEnvironment
import graphql.schema.GraphQLSchema
import graphql.servlet.GraphQLConfiguration
import graphql.servlet.SimpleGraphQLHttpServlet
import graphql.servlet.context.DefaultGraphQLServletContext
import graphql.servlet.context.DefaultGraphQLWebSocketContext
import graphql.servlet.context.GraphQLServletContextBuilder
import org.dataloader.BatchLoader
import org.dataloader.DataLoader
import org.dataloader.DataLoaderRegistry
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.boot.web.servlet.ServletRegistrationBean
import org.springframework.context.support.beans
import java.util.concurrent.CompletableFuture
import java.util.concurrent.CompletionStage
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import javax.websocket.Session
import javax.websocket.server.HandshakeRequest
/**
* Example Spring Boot application with GraphQL Java Tools + GraphQL Java Servlet using DataLoader.
*/
@SpringBootApplication
class DemoApplication
/**
* Main for this Spring Boot application using Kotlin beans DSL.
*/
fun main(args: Array<String>) {
runApplication<DemoApplication>(*args) {
addInitializers(
beans {
bean { Query() }
bean { OrderItemResolver() }
bean {
// Create a DataLoaderRegistry and register the ProductBatchLoader.
DataLoaderRegistry().register(ProductBatchLoader())
}
bean {
// Create the GraphQLSchema
SchemaParser.newParser().file("schema.graphqls").resolvers(ref<Query>(), ref<OrderItemResolver>()).build().makeExecutableSchema()
}
bean {
// Register the GraphQLServlet
ServletRegistrationBean(ConfigurableGraphQLServlet(graphQLConfiguration(ref(), ref())), "/graphql")
}
}
)
}
}
/**
* Helper for the GraphQLConfiguration which registers the DataLoaderRegistry.
*
* GraphQLConfiguration allows for customizing how the runtime is behaving.
*/
fun graphQLConfiguration(schema: GraphQLSchema, registry: DataLoaderRegistry) = GraphQLConfiguration
.with(schema)
.with(DataLoaderAwareGraphQLContextBuilder(registry))
.build()
/**
* Data class for top level query field.
*/
data class Order(val items: List<OrderItem>)
/**
* Data class for parent->child : order->orderItem relation.
*/
data class OrderItem(val productId: String)
/**
* Data class for parent->child : orderItem->product relation.
*/
data class Product(val productId: String, val title: String)
/**
* GraphQL java tools Query resolver.
*/
class Query : GraphQLQueryResolver {
/**
* Retrieve order data (dummy implementation).
*/
fun order(): Order = Order(items = listOf(OrderItem("123"), OrderItem("234")))
}
/**
* GraphQL java tools OrderItem field resolver.
*/
class OrderItemResolver() : GraphQLResolver<OrderItem> {
/**
* Delegates data fetching to the DataLoader for Product.
*/
fun product(orderItem: OrderItem, env: DataFetchingEnvironment): CompletableFuture<Product> = env.getDataLoader<Product>().load(orderItem.productId)
}
/**
* Customer batch loader for products.
*/
class ProductBatchLoader : BatchLoader<String, Product> {
/**
* Retrieve products by id in batch (dummy implementation).
*/
fun getProducts(ids: List<String>) = ids.map { Product(productId = it, title = "title ${it}") }
/**
* Implementation of BatchLoader interface.
*/
override fun load(ids: List<String>): CompletionStage<List<Product>> = CompletableFuture.supplyAsync { getProducts(ids) }
}
/**
* DataLoaderAwareGraphQLContextBuilder allows for configuring the GraphQLContext with a given DataLoaderRegistry.
*/
class DataLoaderAwareGraphQLContextBuilder(private val registry: DataLoaderRegistry) : GraphQLContextBuilder, GraphQLServletContextBuilder {
override fun build(): GraphQLContext {
return DefaultGraphQLContext(registry, null)
}
override fun build(httpServletRequest: HttpServletRequest, httpServletResponse: HttpServletResponse): GraphQLContext {
return DefaultGraphQLServletContext.createServletContext().with(httpServletRequest).with(httpServletResponse).with(registry).build()
}
override fun build(session: Session, handshakeRequest: HandshakeRequest): GraphQLContext {
return DefaultGraphQLWebSocketContext.createWebSocketContext().with(session).with(handshakeRequest).with(registry).build()
}
}
/**
* GraphQLServlet that allows for configuring a DataLoaderRegistry.
*/
class ConfigurableGraphQLServlet(private val configuration: GraphQLConfiguration) : SimpleGraphQLHttpServlet() {
override fun getConfiguration(): GraphQLConfiguration = configuration
}
/**
* Kotlin extensions which allows for easy registration of BatchLoaders.
*/
private inline fun <reified T> DataLoaderRegistry.register(batchLoader: BatchLoader<*, T>): DataLoaderRegistry {
return DataLoaderRegistry().register(T::class.simpleName, DataLoader.newDataLoader(batchLoader))
}
/**
* Kotlin extensions for fetching a DataLoader from the DataFetchingEnvironment in a standard way.
*/
private inline fun <reified T> DataFetchingEnvironment.getDataLoader(): DataLoader<Any, T> {
return dataLoaderRegistry.getDataLoader<Any, T>(T::class.simpleName)
}