Skip to content

Commit fc4d087

Browse files
committed
subscription test
1 parent 3cb23d2 commit fc4d087

File tree

7 files changed

+244
-36
lines changed

7 files changed

+244
-36
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ GraphQL Kotlin consists of number of libraries that aim to simplify GraphQL inte
1010

1111
* [graphql-kotlin-schema-generator](/graphql-kotlin-schema-generator) - Code only GraphQL schema generation for Kotlin
1212
* [graphql-kotlin-federation](/graphql-kotlin-federation) - Schema generator extension to build federated GraphQL schemas
13-
* [graphql-kotlin-spring-server](/graphql-kotlin-spring-server) - Spring Boot autoconfiguration library to create GraphQL web app
13+
* [graphql-kotlin-spring-server](/graphql-kotlin-spring-server) - Spring Boot auto-configuration library to create GraphQL web app
1414
* [examples](/examples) - Example apps that use graphql-kotlin libraries to test and demonstrate usages
1515

1616
## ⌨️ Usage

graphql-kotlin-spring-server/src/main/kotlin/com/expediagroup/graphql/spring/GraphQLAutoConfiguration.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ import java.util.Optional
4040
@Import(
4141
RoutesConfiguration::class,
4242
SchemaAutoConfiguration::class,
43-
FederationAutoConfiguration::class
43+
FederationAutoConfiguration::class,
44+
SubscriptionAutoConfiguration::class
4445
)
4546
@EnableConfigurationProperties(GraphQLConfigurationProperties::class)
4647
class GraphQLAutoConfiguration {

graphql-kotlin-spring-server/src/main/kotlin/com/expediagroup/graphql/spring/SubscriptionAutoConfiguration.kt

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import com.expediagroup.graphql.spring.operations.Subscription
2020
import com.fasterxml.jackson.databind.ObjectMapper
2121
import graphql.GraphQL
2222
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean
23-
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
2423
import org.springframework.context.annotation.Bean
2524
import org.springframework.context.annotation.Configuration
2625
import org.springframework.core.Ordered
@@ -29,22 +28,19 @@ import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping
2928
import org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter
3029

3130
/**
32-
* SpringBoot autoconfiguration that creates default WebSocket handler for GraphQL subscriptions.
31+
* SpringBoot auto-configuration that creates default WebSocket handler for GraphQL subscriptions.
3332
*/
3433
@Configuration
3534
@ConditionalOnBean(Subscription::class)
3635
class SubscriptionAutoConfiguration {
3736

3837
@Bean
39-
@ConditionalOnMissingBean
4038
fun subscriptionHandler(graphQL: GraphQL, objectMapper: ObjectMapper): SubscriptionHandler = SubscriptionHandler(graphQL, objectMapper)
4139

4240
@Bean
43-
@ConditionalOnMissingBean
4441
fun websocketHandlerAdapter(): WebSocketHandlerAdapter = WebSocketHandlerAdapter()
4542

4643
@Bean
47-
@ConditionalOnMissingBean
4844
fun handlerMapping(config: GraphQLConfigurationProperties, subscriptionHandler: SubscriptionHandler): HandlerMapping =
4945
SimpleUrlHandlerMapping(mapOf(config.subscriptions.endpoint to subscriptionHandler), Ordered.HIGHEST_PRECEDENCE)
5046
}

graphql-kotlin-spring-server/src/main/kotlin/com/expediagroup/graphql/spring/SubscriptionHandler.kt

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,21 +41,21 @@ class SubscriptionHandler(
4141

4242
@Suppress("ForbiddenVoid")
4343
override fun handle(session: WebSocketSession): Mono<Void> = session.send(session.receive()
44-
.doOnSubscribe {
45-
logger.trace("session starting, ID=${session.id}")
46-
}
47-
.doOnCancel {
48-
logger.trace("closing session, ID=${session.id}")
49-
}
50-
.concatMap {
51-
val graphQLRequest = objectMapper.readValue<GraphQLRequest>(it.payloadAsText)
52-
val executionInput = graphQLRequest.toExecutionInput()
53-
val executionResult = graphQL.execute(executionInput)
54-
executionResult.getData<Publisher<ExecutionResult>>()
55-
}
56-
.map { objectMapper.writeValueAsString(it.toGraphQLResponse()) }
57-
.map { session.textMessage(it) }
58-
)
44+
.concatMap {
45+
val graphQLRequest = objectMapper.readValue<GraphQLRequest>(it.payloadAsText)
46+
val executionResult = graphQL.execute(graphQLRequest.toExecutionInput())
47+
executionResult.getData<Publisher<ExecutionResult>>()
48+
}
49+
.map { objectMapper.writeValueAsString(it.toGraphQLResponse()) }
50+
.map { session.textMessage(it) }
51+
.doOnSubscribe { logger.info("session starting, ID=${session.id}") }
52+
.doOnCancel { logger.info("cancelling session, ID=${session.id}") }
53+
.doOnComplete {
54+
logger.info("session complete, ID=${session.id}")
55+
session.close()
56+
}
57+
.log()
58+
)
5959

6060
override fun getSubProtocols(): List<String> = listOf("graphql-ws")
6161
}

graphql-kotlin-spring-server/src/test/kotlin/com/expediagroup/graphql/spring/base/SchemaConfigurationTest.kt

Lines changed: 82 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,43 @@
11
package com.expediagroup.graphql.spring.base
22

33
import com.expediagroup.graphql.SchemaGeneratorConfig
4+
import com.expediagroup.graphql.TopLevelObject
45
import com.expediagroup.graphql.spring.GraphQLAutoConfiguration
56
import com.expediagroup.graphql.spring.QueryHandler
67
import com.expediagroup.graphql.spring.operations.Query
8+
import com.expediagroup.graphql.toSchema
79
import com.fasterxml.jackson.databind.ObjectMapper
810
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
911
import graphql.GraphQL
12+
import graphql.execution.instrumentation.Instrumentation
13+
import graphql.execution.instrumentation.tracing.TracingInstrumentation
1014
import graphql.schema.GraphQLSchema
1115
import graphql.schema.GraphQLTypeUtil
16+
import org.assertj.core.api.AssertionsForInterfaceTypes.assertThat
1217
import org.junit.jupiter.api.Test
1318
import org.springframework.boot.autoconfigure.AutoConfigurations
14-
import org.springframework.boot.test.context.runner.ApplicationContextRunner
19+
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner
20+
import org.springframework.context.annotation.Bean
21+
import org.springframework.context.annotation.Configuration
1522
import kotlin.test.assertEquals
1623
import kotlin.test.assertNotNull
24+
import kotlin.test.assertNull
1725

1826
class SchemaConfigurationTest {
1927

20-
private val contextRunner: ApplicationContextRunner = ApplicationContextRunner()
28+
private val contextRunner: ReactiveWebApplicationContextRunner = ReactiveWebApplicationContextRunner()
2129
.withConfiguration(AutoConfigurations.of(GraphQLAutoConfiguration::class.java))
22-
.withBean(ObjectMapper::class.java, jacksonObjectMapper())
23-
.withBean(BasicQuery()::class.java)
2430

2531
@Test
2632
fun `verify schema auto configuration`() {
27-
contextRunner.withPropertyValues("graphql.packages=com.expediagroup.graphql.spring.base")
33+
contextRunner.withUserConfiguration(BasicConfiguration::class.java)
34+
.withPropertyValues("graphql.packages=com.expediagroup.graphql.spring.base")
2835
.run { ctx ->
36+
assertThat(ctx).hasSingleBean(SchemaGeneratorConfig::class.java)
2937
val schemaGeneratorConfig = ctx.getBean(SchemaGeneratorConfig::class.java)
3038
assertEquals(listOf("com.expediagroup.graphql.spring.base"), schemaGeneratorConfig.supportedPackages)
3139

40+
assertThat(ctx).hasSingleBean(GraphQLSchema::class.java)
3241
val schema = ctx.getBean(GraphQLSchema::class.java)
3342
val query = schema.queryType
3443
val fields = query.fieldDefinitions
@@ -37,11 +46,77 @@ class SchemaConfigurationTest {
3746
assertNotNull(helloWorldQuery)
3847
assertEquals("String", GraphQLTypeUtil.unwrapAll(helloWorldQuery.type).name)
3948

40-
ctx.getBean(GraphQL::class.java)
41-
ctx.getBean(QueryHandler::class.java)
49+
assertThat(ctx).hasSingleBean(GraphQL::class.java)
50+
val graphQL = ctx.getBean(GraphQL::class.java)
51+
val result = graphQL.execute("query { hello }").toSpecification()
52+
assertNotNull(result["data"] as? Map<*, *>) { data ->
53+
assertEquals("Hello", data["hello"])
54+
}
55+
assertNull(result["errors"])
56+
assertNotNull(result["extensions"])
57+
58+
assertThat(ctx).hasSingleBean(QueryHandler::class.java)
4259
}
4360
}
4461

62+
@Test
63+
fun `verify schema auto configuration backs off in beans are defined by user`() {
64+
contextRunner.withUserConfiguration(CustomConfiguration::class.java)
65+
.run { ctx ->
66+
val customConfiguration = ctx.getBean(CustomConfiguration::class.java)
67+
68+
assertThat(ctx).hasSingleBean(SchemaGeneratorConfig::class.java)
69+
assertThat(ctx).getBean(SchemaGeneratorConfig::class.java)
70+
.isSameAs(customConfiguration.customSchemaConfig())
71+
72+
assertThat(ctx).hasSingleBean(GraphQLSchema::class.java)
73+
assertThat(ctx).getBean(GraphQLSchema::class.java)
74+
.isSameAs(customConfiguration.mySchema())
75+
76+
assertThat(ctx).hasSingleBean(GraphQL::class.java)
77+
assertThat(ctx).getBean(GraphQL::class.java)
78+
.isSameAs(customConfiguration.myGraphQL())
79+
80+
assertThat(ctx).hasSingleBean(QueryHandler::class.java)
81+
}
82+
}
83+
84+
@Configuration
85+
class BasicConfiguration {
86+
87+
@Bean
88+
fun objectMapper(): ObjectMapper = jacksonObjectMapper()
89+
90+
@Bean
91+
fun helloWorldQuery(): Query = BasicQuery()
92+
93+
@Bean
94+
fun instrumentation(): Instrumentation = TracingInstrumentation()
95+
}
96+
97+
@Configuration
98+
class CustomConfiguration {
99+
100+
@Bean
101+
fun objectMapper(): ObjectMapper = jacksonObjectMapper()
102+
103+
@Bean
104+
fun customSchemaConfig(): SchemaGeneratorConfig = SchemaGeneratorConfig(
105+
supportedPackages = listOf("com.expediagroup")
106+
)
107+
108+
@Bean
109+
fun mySchema(): GraphQLSchema = toSchema(
110+
config = customSchemaConfig(),
111+
queries = listOf(TopLevelObject(BasicQuery()))
112+
)
113+
114+
@Bean
115+
fun myGraphQL(): GraphQL = GraphQL.newGraphQL(mySchema())
116+
.instrumentation(TracingInstrumentation())
117+
.build()
118+
}
119+
45120
class BasicQuery : Query {
46121
@Suppress("FunctionOnlyReturningConstant")
47122
fun hello(): String = "Hello"

graphql-kotlin-spring-server/src/test/kotlin/com/expediagroup/graphql/spring/federation/FederationConfigurationTest.kt

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,47 @@
11
package com.expediagroup.graphql.spring.federation
22

33
import com.expediagroup.graphql.SchemaGeneratorConfig
4+
import com.expediagroup.graphql.TopLevelObject
5+
import com.expediagroup.graphql.federation.FederatedSchemaGeneratorConfig
6+
import com.expediagroup.graphql.federation.FederatedSchemaGeneratorHooks
47
import com.expediagroup.graphql.federation.directives.ExtendsDirective
58
import com.expediagroup.graphql.federation.directives.ExternalDirective
69
import com.expediagroup.graphql.federation.directives.FieldSet
710
import com.expediagroup.graphql.federation.directives.KeyDirective
11+
import com.expediagroup.graphql.federation.execution.FederatedTypeRegistry
812
import com.expediagroup.graphql.spring.GraphQLAutoConfiguration
913
import com.expediagroup.graphql.spring.QueryHandler
1014
import com.expediagroup.graphql.spring.operations.Query
15+
import com.expediagroup.graphql.toSchema
1116
import com.fasterxml.jackson.databind.ObjectMapper
1217
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
1318
import graphql.GraphQL
1419
import graphql.schema.GraphQLObjectType
1520
import graphql.schema.GraphQLSchema
21+
import org.assertj.core.api.Assertions.assertThat
1622
import org.junit.jupiter.api.Test
1723
import org.springframework.boot.autoconfigure.AutoConfigurations
18-
import org.springframework.boot.test.context.runner.ApplicationContextRunner
24+
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner
25+
import org.springframework.context.annotation.Bean
26+
import org.springframework.context.annotation.Configuration
1927
import kotlin.test.assertEquals
2028
import kotlin.test.assertNotNull
2129

2230
class FederationConfigurationTest {
2331

24-
private val contextRunner: ApplicationContextRunner = ApplicationContextRunner()
32+
private val contextRunner: ReactiveWebApplicationContextRunner = ReactiveWebApplicationContextRunner()
2533
.withConfiguration(AutoConfigurations.of(GraphQLAutoConfiguration::class.java))
26-
.withBean(ObjectMapper::class.java, jacksonObjectMapper())
27-
.withBean(FederatedQuery()::class.java)
2834

2935
@Test
3036
fun `verify federated schema auto configuration`() {
31-
contextRunner.withPropertyValues("graphql.packages=com.expediagroup.graphql.spring.federation", "graphql.federation.enabled=true")
37+
contextRunner.withUserConfiguration(FederatedConfiguration::class.java)
38+
.withPropertyValues("graphql.packages=com.expediagroup.graphql.spring.federation", "graphql.federation.enabled=true")
3239
.run { ctx ->
40+
assertThat(ctx).hasSingleBean(SchemaGeneratorConfig::class.java)
3341
val schemaGeneratorConfig = ctx.getBean(SchemaGeneratorConfig::class.java)
3442
assertEquals(listOf("com.expediagroup.graphql.spring.federation"), schemaGeneratorConfig.supportedPackages)
3543

44+
assertThat(ctx).hasSingleBean(GraphQLSchema::class.java)
3645
val schema = ctx.getBean(GraphQLSchema::class.java)
3746
val query = schema.queryType
3847
val fields = query.fieldDefinitions
@@ -49,11 +58,59 @@ class FederationConfigurationTest {
4958
assertNotNull(widgetType.directives.firstOrNull { it.name == "key" })
5059
assertNotNull(widgetType.directives.firstOrNull { it.name == "extends" })
5160

52-
ctx.getBean(GraphQL::class.java)
53-
ctx.getBean(QueryHandler::class.java)
61+
assertThat(ctx).hasSingleBean(GraphQL::class.java)
62+
assertThat(ctx).hasSingleBean(QueryHandler::class.java)
5463
}
5564
}
5665

66+
@Test
67+
fun `verify federated schema auto configuration backs off in beans are defined by user`() {
68+
contextRunner.withUserConfiguration(CustomFederatedConfiguration::class.java)
69+
.run { ctx ->
70+
val customConfiguration = ctx.getBean(CustomFederatedConfiguration::class.java)
71+
72+
assertThat(ctx).hasSingleBean(SchemaGeneratorConfig::class.java)
73+
assertThat(ctx).getBean(SchemaGeneratorConfig::class.java)
74+
.isSameAs(customConfiguration.customSchemaConfig())
75+
76+
assertThat(ctx).hasSingleBean(GraphQLSchema::class.java)
77+
assertThat(ctx).getBean(GraphQLSchema::class.java)
78+
.isSameAs(customConfiguration.mySchema())
79+
80+
assertThat(ctx).hasSingleBean(GraphQL::class.java)
81+
assertThat(ctx).hasSingleBean(QueryHandler::class.java)
82+
}
83+
}
84+
85+
@Configuration
86+
class FederatedConfiguration {
87+
88+
@Bean
89+
fun objectMapper(): ObjectMapper = jacksonObjectMapper()
90+
91+
@Bean
92+
fun federatedQuery(): Query = FederatedQuery()
93+
}
94+
95+
@Configuration
96+
class CustomFederatedConfiguration {
97+
98+
@Bean
99+
fun objectMapper(): ObjectMapper = jacksonObjectMapper()
100+
101+
@Bean
102+
fun customSchemaConfig(): SchemaGeneratorConfig = FederatedSchemaGeneratorConfig(
103+
supportedPackages = listOf("com.expediagroup"),
104+
hooks = FederatedSchemaGeneratorHooks(FederatedTypeRegistry())
105+
)
106+
107+
@Bean
108+
fun mySchema(): GraphQLSchema = toSchema(
109+
config = customSchemaConfig(),
110+
queries = listOf(TopLevelObject(FederatedQuery()))
111+
)
112+
}
113+
57114
class FederatedQuery : Query {
58115
fun widget(): Widget = Widget(1, "hello")
59116
}

0 commit comments

Comments
 (0)