Skip to content

Commit be4664b

Browse files
smyricksmyrick
authored and
smyrick
committed
Federation example (ExpediaGroup#315)
* Start a federation example Requires an update from graphql-java first graphql-java/graphql-java#1644 * fix federation example * exclude federation example from code coverage * Move to top-level examples folder
1 parent 6ae7188 commit be4664b

File tree

106 files changed

+5405
-11
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

106 files changed

+5405
-11
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ GraphQL Kotlin consists of number of libraries that aim to simplify GraphQL inte
1111

1212
* [graphql-kotlin-federation](/graphql-kotlin-federation) - Schema generator extension to build federated GraphQL schemas
1313
* [graphql-kotlin-schema-generator](/graphql-kotlin-schema-generator) - Code only GraphQL schema generation for Kotlin
14-
* [graphql-kotlin-spring-example](/graphql-kotlin-spring-example) - Example SpringBoot app that uses GraphQL Kotlin schema generator
14+
* [examples/spring](/examples/spring) - Example SpringBoot app that uses GraphQL Kotlin schema generator
15+
* [examples/federation](/examples/federation) - Example Federation module with multiple SpringBoot apps that use GraphQL Kotlin federation generator
1516

1617
## Documentation
1718

codecov.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55

66
# Ingore the example app from coverage reports
77
ignore:
8-
- "graphql-kotlin-spring-example/**/*"
8+
- "examples/**/*"

examples/federation/README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Federation Example
2+
3+
This example is two Spring applications `base-app` and `extend-app` that use `graphql-kotlin-federation` to generate the schema.
4+
These apps run on different ports (`8080`, `8081`) so they can run simultaniously
5+
6+
Then the `gateway` is a Node.js app running Apollo Gateway on port `4000` and connects to the two spring apps.
7+
You can make queries against the spring apps directly or combined queries from the gateway.
8+
9+
## Running Locally
10+
11+
12+
### Spring Apps
13+
Build the spring applications by running the following commands in the `/federation` directory
14+
15+
```shell script
16+
mvn clean install
17+
```
18+
19+
Start the servers:
20+
21+
* Run each `Application.kt` directly from your IDE
22+
* Alternatively you can also use the spring boot maven plugin by running `mvn spring-boot:run` from the command line.
23+
24+
25+
Once the app has started you can explore the example schema by opening Playground endpoint
26+
* `base-app` http://localhost:8080/playground
27+
* `extend-app` http://localhost:8081/playground
28+
29+
### Gateway
30+
31+
See the instructions in the [README](/gateway/README.md) for this folder

examples/federation/base-app/pom.xml

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
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" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
<parent>
5+
<groupId>com.expedia</groupId>
6+
<artifactId>graphql-kotlin</artifactId>
7+
<version>1.0.0-RC6-SNAPSHOT</version>
8+
<relativePath>..</relativePath>
9+
</parent>
10+
11+
<artifactId>base-app</artifactId>
12+
<description>Example SpringBoot app using graphql-kotlin</description>
13+
<url>https://github.com/ExpediaDotCom/graphql-kotlin</url>
14+
<packaging>jar</packaging>
15+
16+
<properties>
17+
<spring-boot.version>2.1.2.RELEASE</spring-boot.version>
18+
19+
<!-- skip release plugins -->
20+
<maven.source.skip>true</maven.source.skip>
21+
</properties>
22+
23+
<dependencies>
24+
<dependency>
25+
<groupId>com.expedia</groupId>
26+
<artifactId>graphql-kotlin-schema-generator</artifactId>
27+
<version>${project.parent.version}</version>
28+
</dependency>
29+
<dependency>
30+
<groupId>com.expedia</groupId>
31+
<artifactId>graphql-kotlin-federation</artifactId>
32+
<version>${project.parent.version}</version>
33+
</dependency>
34+
<dependency>
35+
<groupId>org.springframework.boot</groupId>
36+
<artifactId>spring-boot-starter-webflux</artifactId>
37+
<version>${spring-boot.version}</version>
38+
</dependency>
39+
</dependencies>
40+
41+
<build>
42+
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
43+
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
44+
<plugins>
45+
<plugin>
46+
<groupId>org.springframework.boot</groupId>
47+
<artifactId>spring-boot-maven-plugin</artifactId>
48+
<version>${spring-boot.version}</version>
49+
</plugin>
50+
51+
<plugin>
52+
<artifactId>kotlin-maven-plugin</artifactId>
53+
<groupId>org.jetbrains.kotlin</groupId>
54+
<executions>
55+
<execution>
56+
<id>compile</id>
57+
<phase>compile</phase>
58+
<goals>
59+
<goal>compile</goal>
60+
</goals>
61+
</execution>
62+
<execution>
63+
<id>test-compile</id>
64+
<phase>test-compile</phase>
65+
<goals>
66+
<goal>test-compile</goal>
67+
</goals>
68+
</execution>
69+
</executions>
70+
<configuration>
71+
<compilerPlugins>
72+
<plugin>spring</plugin>
73+
</compilerPlugins>
74+
<jvmTarget>${java.version}</jvmTarget>
75+
</configuration>
76+
<dependencies>
77+
<dependency>
78+
<groupId>org.jetbrains.kotlin</groupId>
79+
<artifactId>kotlin-maven-allopen</artifactId>
80+
<version>${kotlin.version}</version>
81+
</dependency>
82+
</dependencies>
83+
</plugin>
84+
<!-- TODO enable detekt/ktlint -->
85+
<!-- <plugin>-->
86+
<!-- <groupId>org.apache.maven.plugins</groupId>-->
87+
<!-- <artifactId>maven-antrun-plugin</artifactId>-->
88+
<!-- </plugin>-->
89+
<plugin>
90+
<groupId>org.jetbrains.dokka</groupId>
91+
<artifactId>dokka-maven-plugin</artifactId>
92+
<version>${dokka-maven-plugin.version}</version>
93+
<configuration>
94+
<!-- don't generate javadocs as there is no artifact-->
95+
<skip>true</skip>
96+
</configuration>
97+
</plugin>
98+
<plugin>
99+
<groupId>org.sonatype.plugins</groupId>
100+
<artifactId>nexus-staging-maven-plugin</artifactId>
101+
<version>1.6.8</version>
102+
<extensions>true</extensions>
103+
<executions>
104+
<execution>
105+
<id>default-deploy</id>
106+
<phase>none</phase>
107+
</execution>
108+
</executions>
109+
</plugin>
110+
</plugins>
111+
</build>
112+
</project>
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package com.expedia.graphql.sample
2+
3+
import com.expedia.graphql.TopLevelObject
4+
import com.expedia.graphql.execution.KotlinDataFetcherFactoryProvider
5+
import com.expedia.graphql.extensions.print
6+
import com.expedia.graphql.federation.FederatedSchemaGeneratorConfig
7+
import com.expedia.graphql.federation.FederatedSchemaGeneratorHooks
8+
import com.expedia.graphql.federation.execution.FederatedTypeRegistry
9+
import com.expedia.graphql.federation.toFederatedSchema
10+
import com.expedia.graphql.hooks.SchemaGeneratorHooks
11+
import com.expedia.graphql.sample.datafetchers.CustomDataFetcherFactoryProvider
12+
import com.expedia.graphql.sample.datafetchers.SpringDataFetcherFactory
13+
import com.expedia.graphql.sample.exceptions.CustomDataFetcherExceptionHandler
14+
import com.expedia.graphql.sample.query.Query
15+
import graphql.GraphQL
16+
import graphql.execution.AsyncExecutionStrategy
17+
import graphql.execution.AsyncSerialExecutionStrategy
18+
import graphql.execution.DataFetcherExceptionHandler
19+
import graphql.execution.SubscriptionExecutionStrategy
20+
import graphql.schema.GraphQLSchema
21+
import org.slf4j.LoggerFactory
22+
import org.springframework.boot.autoconfigure.SpringBootApplication
23+
import org.springframework.boot.runApplication
24+
import org.springframework.context.annotation.Bean
25+
26+
@SpringBootApplication
27+
class Application {
28+
29+
private val logger = LoggerFactory.getLogger(Application::class.java)
30+
31+
@Bean
32+
fun hooks() = FederatedSchemaGeneratorHooks(FederatedTypeRegistry(emptyMap()))
33+
34+
@Bean
35+
fun dataFetcherFactoryProvider(springDataFetcherFactory: SpringDataFetcherFactory, hooks: SchemaGeneratorHooks) =
36+
CustomDataFetcherFactoryProvider(springDataFetcherFactory, hooks)
37+
38+
@Bean
39+
fun schemaConfig(hooks: FederatedSchemaGeneratorHooks, dataFetcherFactoryProvider: KotlinDataFetcherFactoryProvider) = FederatedSchemaGeneratorConfig(
40+
supportedPackages = listOf("com.expedia"),
41+
hooks = hooks,
42+
dataFetcherFactoryProvider = dataFetcherFactoryProvider
43+
)
44+
45+
@Bean
46+
fun schema(
47+
queries: List<Query>,
48+
schemaConfig: FederatedSchemaGeneratorConfig
49+
): GraphQLSchema {
50+
fun List<Any>.toTopLevelObjects() = this.map {
51+
TopLevelObject(it)
52+
}
53+
54+
val schema = toFederatedSchema(
55+
config = schemaConfig,
56+
queries = queries.toTopLevelObjects()
57+
)
58+
59+
logger.info(schema.print())
60+
61+
return schema
62+
}
63+
64+
@Bean
65+
fun dataFetcherExceptionHandler(): DataFetcherExceptionHandler = CustomDataFetcherExceptionHandler()
66+
67+
@Bean
68+
fun graphQL(
69+
schema: GraphQLSchema,
70+
dataFetcherExceptionHandler: DataFetcherExceptionHandler
71+
): GraphQL = GraphQL.newGraphQL(schema)
72+
.queryExecutionStrategy(AsyncExecutionStrategy(dataFetcherExceptionHandler))
73+
.mutationExecutionStrategy(AsyncSerialExecutionStrategy(dataFetcherExceptionHandler))
74+
.subscriptionExecutionStrategy(SubscriptionExecutionStrategy(dataFetcherExceptionHandler))
75+
.build()
76+
}
77+
78+
fun main(args: Array<String>) {
79+
runApplication<Application>(*args)
80+
}

graphql-kotlin-spring-example/src/main/kotlin/com/expedia/graphql/sample/GraphQLRequest.kt renamed to examples/federation/base-app/src/main/kotlin/com/expedia/graphql/sample/GraphQLRequest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ fun GraphQLRequest.toExecutionInput(graphQLContext: MyGraphQLContext? = null): E
1313
ExecutionInput.newExecutionInput()
1414
.query(this.query)
1515
.operationName(this.operationName)
16-
.variables(this.variables)
16+
.variables(this.variables ?: emptyMap())
1717
.context(graphQLContext)
1818
.build()
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.expedia.graphql.sample
2+
3+
import com.expedia.graphql.sample.context.MyGraphQLContext
4+
import com.expedia.graphql.sample.exceptions.SimpleKotlinGraphQLError
5+
import graphql.ErrorType
6+
import graphql.GraphQL
7+
import org.springframework.stereotype.Component
8+
import reactor.core.publisher.Mono
9+
10+
@Component
11+
class QueryHandler(private val graphql: GraphQL) {
12+
13+
fun executeQuery(request: GraphQLRequest): Mono<GraphQLResponse> = Mono.subscriberContext()
14+
.flatMap { ctx ->
15+
val graphQLContext: MyGraphQLContext = ctx.get("graphQLContext")
16+
val input = request.toExecutionInput(graphQLContext)
17+
18+
Mono.fromFuture(graphql.executeAsync(input))
19+
.map { executionResult -> executionResult.toGraphQLResponse() }
20+
}.onErrorResume {
21+
val graphQLError = SimpleKotlinGraphQLError(it, ErrorType.DataFetchingException)
22+
23+
Mono.just(GraphQLResponse(errors = listOf(graphQLError)))
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.expedia.graphql.sample.exceptions
2+
3+
import graphql.ErrorClassification
4+
import graphql.ErrorType
5+
import graphql.GraphQLError
6+
import graphql.language.SourceLocation
7+
8+
open class SimpleKotlinGraphQLError(
9+
private val exception: Throwable,
10+
private val errorType: ErrorType
11+
) : GraphQLError {
12+
override fun getErrorType(): ErrorClassification = errorType
13+
14+
override fun getLocations(): List<SourceLocation> = emptyList()
15+
16+
override fun getMessage(): String = "Exception while running code outside of data handler: ${exception.message}"
17+
18+
override fun getExtensions(): Map<String, Any> {
19+
val newExtensions = mutableMapOf<String, Any>()
20+
if (exception is GraphQLError && exception.extensions != null) {
21+
newExtensions.putAll(exception.extensions)
22+
}
23+
return newExtensions
24+
}
25+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.expedia.graphql.sample.model
2+
3+
import com.expedia.graphql.annotations.GraphQLDescription
4+
import com.expedia.graphql.federation.directives.FieldSet
5+
import com.expedia.graphql.federation.directives.KeyDirective
6+
7+
@KeyDirective(fields = FieldSet("id"))
8+
@GraphQLDescription("A useful widget")
9+
data class Widget(
10+
val id: Int,
11+
@GraphQLDescription("The widget's value that can be null")
12+
val value: Int? = null
13+
) {
14+
15+
@GraphQLDescription("returns original value multiplied by target OR null if original value was null")
16+
fun multiplyValueBy(multiplier: Int) = value?.times(multiplier)
17+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.expedia.graphql.sample.query
2+
3+
import org.springframework.stereotype.Component
4+
5+
@Component
6+
class SimpleQuery : Query {
7+
fun dataFromBaseApp() = "hello from base app"
8+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
spring:
2+
application:
3+
name: base-app
4+
server:
5+
port: 8080
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# GraphQL Kotlin Spring Example
2+
3+
One way to run a GraphQL server is with [Spring Boot](https://github.com/spring-projects/spring-boot). This example app uses [Spring Webflux](https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html) together with `graphql-kotlin` and [graphql-playground](https://github.com/prisma/graphql-playground).
4+
5+
### Running locally
6+
Build the application
7+
8+
```bash
9+
mvn clean install
10+
```
11+
12+
Start the server:
13+
14+
* Run `Application.kt` directly from your IDE
15+
* Alternatively you can also use the spring boot maven plugin by running `mvn spring-boot:run` from the command line.
16+
17+
18+
Once the app has started you can explore the example schema by opening Playground endpoint at http://localhost:8080/playground.

0 commit comments

Comments
 (0)