Skip to content

Commit 6416453

Browse files
dariuszkucgscheibel
authored andcommitted
Add example app (#9)
* sample example app that exposes queries/mutations used in the README * fixes selection of data fetcher for classes implementing interfaces * add support for directives
1 parent f1b4e39 commit 6416453

28 files changed

+875
-95
lines changed

README.md

Lines changed: 272 additions & 60 deletions
Large diffs are not rendered by default.

example/pom.xml

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<groupId>com.expedia.www</groupId>
7+
<artifactId>graphql-kotlin-example</artifactId>
8+
<version>0.0.8-SNAPSHOT</version>
9+
<name>graphql-kotlin-example</name>
10+
<description>Example SpringBoot app using graphql-kotlin</description>
11+
<url>https://github.com/ExpediaDotCom/graphql-kotlin</url>
12+
13+
<packaging>jar</packaging>
14+
15+
<licenses>
16+
<license>
17+
<name>The Apache Software License, Version 2.0</name>
18+
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
19+
</license>
20+
</licenses>
21+
22+
<developers>
23+
<developer>
24+
<name>Rick Fast</name>
25+
<email>[email protected]</email>
26+
<organization>Expedia Group</organization>
27+
<organizationUrl>https://www.expediagroup.com</organizationUrl>
28+
</developer>
29+
<developer>
30+
<name>Brennan Taylor</name>
31+
<email>[email protected]</email>
32+
<organization>Expedia Group</organization>
33+
<organizationUrl>https://www.expediagroup.com</organizationUrl>
34+
</developer>
35+
<developer>
36+
<name>Dariusz Kuc</name>
37+
<email>[email protected]</email>
38+
<organization>Expedia Group</organization>
39+
<organizationUrl>https://www.expediagroup.com</organizationUrl>
40+
</developer>
41+
<developer>
42+
<name>Shane Myrick</name>
43+
<email>[email protected]</email>
44+
<organization>Expedia Group</organization>
45+
<organizationUrl>https://www.expediagroup.com</organizationUrl>
46+
</developer>
47+
<developer>
48+
<name>Amanda Ducrou</name>
49+
<email>[email protected]</email>
50+
<organization>Expedia Group</organization>
51+
<organizationUrl>https://www.expediagroup.com</organizationUrl>
52+
</developer>
53+
</developers>
54+
55+
<properties>
56+
<java.version>1.8</java.version>
57+
<kotlin.version>1.2.70</kotlin.version>
58+
<junit.version>4.12</junit.version>
59+
<graphiql.version>5.0.2</graphiql.version>
60+
<graphql-servlet.version>6.1.2</graphql-servlet.version>
61+
</properties>
62+
63+
<build>
64+
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
65+
<plugins>
66+
<plugin>
67+
<artifactId>kotlin-maven-plugin</artifactId>
68+
<groupId>org.jetbrains.kotlin</groupId>
69+
<version>${kotlin.version}</version>
70+
<executions>
71+
<execution>
72+
<id>compile</id>
73+
<phase>compile</phase>
74+
<goals>
75+
<goal>compile</goal>
76+
</goals>
77+
</execution>
78+
<execution>
79+
<id>test-compile</id>
80+
<phase>test-compile</phase>
81+
<goals>
82+
<goal>test-compile</goal>
83+
</goals>
84+
</execution>
85+
</executions>
86+
<configuration>
87+
<compilerPlugins>
88+
<plugin>spring</plugin>
89+
</compilerPlugins>
90+
<jvmTarget>${java.version}</jvmTarget>
91+
</configuration>
92+
<dependencies>
93+
<dependency>
94+
<groupId>org.jetbrains.kotlin</groupId>
95+
<artifactId>kotlin-maven-allopen</artifactId>
96+
<version>${kotlin.version}</version>
97+
</dependency>
98+
</dependencies>
99+
</plugin>
100+
</plugins>
101+
</build>
102+
103+
<dependencies>
104+
<dependency>
105+
<groupId>com.expedia.www</groupId>
106+
<artifactId>graphql-kotlin</artifactId>
107+
<version>0.0.10-SNAPSHOT</version>
108+
</dependency>
109+
110+
<dependency>
111+
<groupId>org.springframework.boot</groupId>
112+
<artifactId>spring-boot-starter-web</artifactId>
113+
<version>2.0.5.RELEASE</version>
114+
</dependency>
115+
116+
<dependency>
117+
<groupId>com.graphql-java</groupId>
118+
<artifactId>graphiql-spring-boot-starter</artifactId>
119+
<version>${graphiql.version}</version>
120+
</dependency>
121+
<dependency>
122+
<groupId>com.graphql-java</groupId>
123+
<artifactId>graphql-java-servlet</artifactId>
124+
<version>${graphql-servlet.version}</version>
125+
<exclusions>
126+
<exclusion>
127+
<groupId>com.fasterxml.jackson.core</groupId>
128+
<artifactId>*</artifactId>
129+
</exclusion>
130+
<exclusion>
131+
<groupId>com.fasterxml.jackson.datatype</groupId>
132+
<artifactId>*</artifactId>
133+
</exclusion>
134+
</exclusions>
135+
</dependency>
136+
</dependencies>
137+
</project>
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package com.expedia.graphql.sample
2+
3+
import com.expedia.graphql.TopLevelObjectDef
4+
import com.expedia.graphql.sample.context.MyGraphQLContextBuilder
5+
import com.expedia.graphql.sample.extension.CustomSchemaGeneratorHooks
6+
import com.expedia.graphql.sample.mutation.Mutation
7+
import com.expedia.graphql.sample.query.Query
8+
import com.expedia.graphql.schema.SchemaGeneratorConfig
9+
import com.expedia.graphql.toSchema
10+
import com.fasterxml.jackson.module.kotlin.KotlinModule
11+
import graphql.schema.GraphQLSchema
12+
import graphql.schema.idl.SchemaPrinter
13+
import graphql.servlet.GraphQLInvocationInputFactory
14+
import graphql.servlet.GraphQLObjectMapper
15+
import graphql.servlet.GraphQLQueryInvoker
16+
import graphql.servlet.ObjectMapperConfigurer
17+
import graphql.servlet.SimpleGraphQLHttpServlet
18+
import org.slf4j.LoggerFactory
19+
import org.springframework.boot.autoconfigure.SpringBootApplication
20+
import org.springframework.boot.runApplication
21+
import org.springframework.boot.web.servlet.ServletRegistrationBean
22+
import org.springframework.context.annotation.Bean
23+
import org.springframework.context.annotation.ComponentScan
24+
import javax.servlet.http.HttpServlet
25+
26+
@SpringBootApplication
27+
@ComponentScan("com.expedia.graphql")
28+
class Application {
29+
30+
private val logger = LoggerFactory.getLogger(Application::class.java)
31+
32+
@Bean
33+
fun schemaConfig(): SchemaGeneratorConfig = SchemaGeneratorConfig(supportedPackages = "com.expedia", hooks = CustomSchemaGeneratorHooks())
34+
35+
@Bean
36+
fun schema(
37+
queries: List<Query>,
38+
mutations: List<Mutation>,
39+
schemaConfig: SchemaGeneratorConfig
40+
): GraphQLSchema {
41+
fun List<Any>.toTopLevelObjectDefs() = this.map {
42+
TopLevelObjectDef(it)
43+
}
44+
45+
val schema = toSchema(
46+
queries = queries.toTopLevelObjectDefs(),
47+
mutations = mutations.toTopLevelObjectDefs(),
48+
config = schemaConfig
49+
)
50+
logger.info(SchemaPrinter().print(schema))
51+
return schema
52+
}
53+
54+
@Bean
55+
fun contextBuilder() = MyGraphQLContextBuilder()
56+
57+
@Bean
58+
fun graphQLInvocationInputFactory(
59+
schema: GraphQLSchema,
60+
contextBuilder: MyGraphQLContextBuilder
61+
): GraphQLInvocationInputFactory = GraphQLInvocationInputFactory.newBuilder(schema)
62+
.withGraphQLContextBuilder(contextBuilder)
63+
.build()
64+
65+
@Bean
66+
fun graphQLQueryInvoker(): GraphQLQueryInvoker = GraphQLQueryInvoker.newBuilder()
67+
.build()
68+
69+
@Bean
70+
fun graphQLObjectMapper(): GraphQLObjectMapper = GraphQLObjectMapper.newBuilder()
71+
.withObjectMapperConfigurer(ObjectMapperConfigurer { it.registerModule(KotlinModule()) })
72+
.build()
73+
74+
@Bean
75+
fun graphQLServlet(
76+
invocationInputFactory: GraphQLInvocationInputFactory,
77+
queryInvoker: GraphQLQueryInvoker,
78+
objectMapper: GraphQLObjectMapper
79+
): SimpleGraphQLHttpServlet = SimpleGraphQLHttpServlet.newBuilder(invocationInputFactory)
80+
.withQueryInvoker(queryInvoker)
81+
.withObjectMapper(objectMapper)
82+
.build()
83+
84+
@Bean
85+
fun graphQLServletRegistration(graphQLServlet: HttpServlet) = ServletRegistrationBean(graphQLServlet, "/graphql")
86+
}
87+
88+
fun main(args: Array<String>) {
89+
runApplication<Application>(*args)
90+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.expedia.graphql.sample.context
2+
3+
import graphql.servlet.GraphQLContext
4+
import javax.security.auth.Subject
5+
import javax.servlet.http.HttpServletRequest
6+
import javax.websocket.server.HandshakeRequest
7+
8+
/**
9+
* Simple [GraphQLContext] that holds extra value.
10+
*/
11+
class MyGraphQLContext(
12+
val myCustomValue: String,
13+
val httpServletRequest: HttpServletRequest? = null,
14+
handshakeRequest: HandshakeRequest? = null,
15+
subject: Subject? = null): GraphQLContext(httpServletRequest, handshakeRequest, subject)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.expedia.graphql.sample.context
2+
3+
import graphql.servlet.GraphQLContext
4+
import graphql.servlet.GraphQLContextBuilder
5+
import javax.servlet.http.HttpServletRequest
6+
import javax.websocket.server.HandshakeRequest
7+
8+
/**
9+
* Basic [GraphQLContextBuilder] that creates custom [MyGraphQLContext].
10+
*/
11+
class MyGraphQLContextBuilder: GraphQLContextBuilder {
12+
13+
override fun build(httpServletRequest: HttpServletRequest): GraphQLContext {
14+
val myValue = httpServletRequest.getHeader("MyHeader") ?: "context_from_servlet"
15+
return MyGraphQLContext(myValue, httpServletRequest = httpServletRequest)
16+
}
17+
18+
override fun build(handshakeRequest: HandshakeRequest): GraphQLContext =
19+
MyGraphQLContext("context_from_handshake", handshakeRequest = handshakeRequest)
20+
21+
override fun build(): GraphQLContext = MyGraphQLContext("default_context")
22+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.expedia.graphql.sample.extension
2+
3+
import com.expedia.graphql.schema.hooks.NoopSchemaGeneratorHooks
4+
import graphql.language.StringValue
5+
import graphql.schema.Coercing
6+
import graphql.schema.GraphQLScalarType
7+
import graphql.schema.GraphQLType
8+
import java.util.UUID
9+
import kotlin.reflect.KClass
10+
import kotlin.reflect.KType
11+
12+
/**
13+
* Schema generator hook that adds additional scalar types.
14+
*/
15+
class CustomSchemaGeneratorHooks: NoopSchemaGeneratorHooks() {
16+
17+
/**
18+
* Register additional GraphQL scalar types.
19+
*/
20+
override fun willGenerateGraphQLType(type: KType): GraphQLType? = when (type.classifier as? KClass<*>) {
21+
UUID::class -> graphqlUUIDType
22+
else -> null
23+
}
24+
25+
}
26+
27+
internal val graphqlUUIDType = GraphQLScalarType("UUID",
28+
"A type representing a formatted java.util.UUID",
29+
UUIDCoercing
30+
)
31+
32+
private object UUIDCoercing : Coercing<UUID, String> {
33+
override fun parseValue(input: Any?): UUID = UUID.fromString(
34+
serialize(
35+
input
36+
)
37+
)
38+
39+
override fun parseLiteral(input: Any?): UUID? {
40+
val uuidString = (input as? StringValue)?.value
41+
return UUID.fromString(uuidString)
42+
}
43+
44+
override fun serialize(dataFetcherResult: Any?): String = dataFetcherResult.toString()
45+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.expedia.graphql.sample.model
2+
3+
import com.expedia.graphql.annotations.GraphQLDescription
4+
5+
@GraphQLDescription("animal interface type")
6+
interface Animal {
7+
@GraphQLDescription("common field of animals")
8+
val type: AnimalType
9+
10+
@GraphQLDescription("common function of animals")
11+
fun sound(): String
12+
}
13+
14+
@GraphQLDescription("enum holding all supported animal types")
15+
enum class AnimalType {
16+
CAT,
17+
DOG
18+
}
19+
20+
@GraphQLDescription("dog is one of the implementations of animal")
21+
class Dog: Animal {
22+
23+
override val type: AnimalType
24+
get() = AnimalType.DOG
25+
26+
override fun sound() = "bark"
27+
28+
@GraphQLDescription("this is specific to dogs")
29+
fun doSomethingUseful(): String = "something useful"
30+
}
31+
32+
@GraphQLDescription("cat is another implementation of animal")
33+
class Cat: Animal {
34+
override val type: AnimalType
35+
get() = AnimalType.CAT
36+
37+
override fun sound() = "meow"
38+
39+
@GraphQLDescription("this is specific to cats")
40+
fun ignoreEveryone(): String = "ignore everyone"
41+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.expedia.graphql.sample.model
2+
3+
import com.expedia.graphql.annotations.GraphQLDescription
4+
5+
@GraphQLDescription("simple response that contains value read from context")
6+
data class ContextualResponse(val passedInValue: Int, val contextValue: String)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.expedia.graphql.sample.model
2+
3+
import com.expedia.graphql.annotations.GraphQLDescription
4+
import com.expedia.graphql.annotations.GraphQLIgnore
5+
6+
@GraphQLDescription("A useful widget")
7+
data class Widget(
8+
@property:GraphQLDescription("The widget's value that can be null")
9+
val value: Int?,
10+
@property:Deprecated(message = "This field is deprecated", replaceWith = ReplaceWith("value"))
11+
@property:GraphQLDescription("The widget's deprecated value that shouldn't be used")
12+
val deprecatedValue: Int? = value,
13+
@property:GraphQLIgnore
14+
val ignoredField: String? = "ignored",
15+
private val hiddenField: String? = "hidden"
16+
) {
17+
18+
@GraphQLDescription("returns original value multiplied by target OR null if original value was null")
19+
fun multiplyValueBy(multiplier: Int) = value?.times(multiplier)
20+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.expedia.graphql.sample.mutation
2+
3+
/**
4+
* Marker interface to easily autowire all GraphQL mutations.
5+
*/
6+
interface Mutation

0 commit comments

Comments
 (0)