forked from neo4j-graphql/neo4j-graphql-java
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add spring boot integration example (neo4j-graphql#126)
* add spring boot integration example * adjustments after review
- Loading branch information
Showing
67 changed files
with
638 additions
and
86 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<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"> | ||
<modelVersion>4.0.0</modelVersion> | ||
|
||
<parent> | ||
<groupId>org.neo4j</groupId> | ||
<artifactId>neo4j-graphql-java-parent</artifactId> | ||
<version>1.1-SNAPSHOT</version> | ||
</parent> | ||
|
||
<artifactId>neo4j-graphql-java</artifactId> | ||
<name>Neo4j GraphQL Java</name> | ||
<description>GraphQL to Cypher Mapping</description> | ||
|
||
<properties> | ||
<neo4j.version>3.5.6</neo4j.version> | ||
<driver.version>1.7.2</driver.version> | ||
</properties> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>org.neo4j.driver</groupId> | ||
<artifactId>neo4j-java-driver</artifactId> | ||
<version>${driver.version}</version> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.sparkjava</groupId> | ||
<artifactId>spark-core</artifactId> | ||
<version>2.7.2</version> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.google.code.gson</groupId> | ||
<artifactId>gson</artifactId> | ||
<version>2.8.5</version> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.neo4j.test</groupId> | ||
<artifactId>neo4j-harness</artifactId> | ||
<version>${neo4j.version}</version> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.neo4j</groupId> | ||
<artifactId>server-api</artifactId> | ||
<version>${neo4j.version}</version> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.codehaus.jackson</groupId> | ||
<artifactId>jackson-mapper-asl</artifactId> | ||
<version>1.9.13</version> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.graphql-java</groupId> | ||
<artifactId>graphql-java</artifactId> | ||
<version>15.0</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.junit.jupiter</groupId> | ||
<artifactId>junit-jupiter</artifactId> | ||
<version>5.5.1</version> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.assertj</groupId> | ||
<artifactId>assertj-core</artifactId> | ||
<version>3.12.2</version> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.neo4j</groupId> | ||
<artifactId>neo4j-cypher-dsl</artifactId> | ||
<version>2020.0.1</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>ch.qos.logback</groupId> | ||
<artifactId>logback-classic</artifactId> | ||
<version>1.2.3</version> | ||
<scope>test</scope> | ||
</dependency> | ||
</dependencies> | ||
|
||
</project> |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
<parent> | ||
<groupId>org.neo4j</groupId> | ||
<artifactId>neo4j-graphql-java-examples</artifactId> | ||
<version>1.1-SNAPSHOT</version> | ||
</parent> | ||
|
||
<groupId>org.neo4j.graphql.examples</groupId> | ||
<artifactId>graphql-spring-boot</artifactId> | ||
|
||
<name>Example - graphql-spring-boot</name> | ||
<description>Example for using neo4j-graphql-java with Spring Boot</description> | ||
|
||
<properties> | ||
<testcontainers.version>1.14.3</testcontainers.version> | ||
</properties> | ||
|
||
<dependencies> | ||
<!-- spring dependencies --> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-configuration-processor</artifactId> | ||
<optional>true</optional> | ||
</dependency> | ||
|
||
<!-- neo4j driver + the neo4j-graphql-java library --> | ||
<dependency> | ||
<groupId>org.neo4j.driver</groupId> | ||
<artifactId>neo4j-java-driver-spring-boot-starter</artifactId> | ||
<version>4.1.1.0</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.neo4j</groupId> | ||
<artifactId>neo4j-graphql-java</artifactId> | ||
<version>1.1-SNAPSHOT</version> | ||
</dependency> | ||
|
||
<!-- a spring graphql library --> | ||
<dependency> | ||
<groupId>com.expediagroup</groupId> | ||
<artifactId>graphql-kotlin-spring-server</artifactId> | ||
<version>3.6.2</version> | ||
</dependency> | ||
|
||
<!-- Kotlin dependencies --> | ||
<dependency> | ||
<groupId>com.fasterxml.jackson.module</groupId> | ||
<artifactId>jackson-module-kotlin</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.jetbrains.kotlin</groupId> | ||
<artifactId>kotlin-reflect</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.jetbrains.kotlin</groupId> | ||
<artifactId>kotlin-stdlib-jdk8</artifactId> | ||
</dependency> | ||
|
||
<!-- Test dependencies --> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-test</artifactId> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.testcontainers</groupId> | ||
<artifactId>neo4j</artifactId> | ||
<version>${testcontainers.version}</version> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.testcontainers</groupId> | ||
<artifactId>junit-jupiter</artifactId> | ||
<version>${testcontainers.version}</version> | ||
<scope>test</scope> | ||
</dependency> | ||
</dependencies> | ||
|
||
<dependencyManagement> | ||
<dependencies> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-dependencies</artifactId> | ||
<version>2.3.3.RELEASE</version> | ||
<type>pom</type> | ||
<scope>import</scope> | ||
</dependency> | ||
</dependencies> | ||
</dependencyManagement> | ||
|
||
<build> | ||
<plugins> | ||
<plugin> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-maven-plugin</artifactId> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
</project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
= Example: Integration of Neo4j-GraphQL-Java into a Spring Boot application | ||
|
||
== Overview | ||
This example uses the https://expediagroup.github.io/graphql-kotlin/[graphql-kotlin library] | ||
|
||
In the link:src/main/kotlin/org/neo4j/graphql/examples/graphqlspringboot/config/Neo4jConfiguration.kt[Neo4jConfiguration] | ||
a DataFetchingInterceptor is created, which will be bound to all the graphql fields generated by the neo4j-graphql-library. | ||
Its purpose is the execution of the cypher query and the transformation of the query result. | ||
|
||
In the link:src/main/kotlin/org/neo4j/graphql/examples/graphqlspringboot/config/GraphQLConfiguration.kt[GraphQLConfiguration] | ||
the link:src/main/resources/schema.graphqls[schema] to be enhanced by the library is loaded as `neo4jSchema`. | ||
Additionally a `springSchema` is generated which is generated from the methods defined in | ||
link:src/main/kotlin/org/neo4j/graphql/examples/graphqlspringboot/controller/AdditionalQueries.kt[AdditionalQueries]. | ||
|
||
The `springSchema` and the `neo4jSchema` are merged to a single graphql schema exposed by the app. | ||
|
||
== Run the example | ||
|
||
1. link:src/main/resources/application.yaml[configue your neo4j db] | ||
2. run the link:src/main/kotlin/org/neo4j/graphql/examples/graphqlspringboot/GraphqlSpringBootApplication.kt[spring boot application] | ||
3. open http://localhost:8080/playground to run some graphql queries |
11 changes: 11 additions & 0 deletions
11
.../main/kotlin/org/neo4j/graphql/examples/graphqlspringboot/GraphqlSpringBootApplication.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package org.neo4j.graphql.examples.graphqlspringboot | ||
|
||
import org.springframework.boot.autoconfigure.SpringBootApplication | ||
import org.springframework.boot.runApplication | ||
|
||
@SpringBootApplication | ||
open class GraphqlSpringBootApplication | ||
|
||
fun main(args: Array<String>) { | ||
runApplication<GraphqlSpringBootApplication>(*args) | ||
} |
118 changes: 118 additions & 0 deletions
118
...c/main/kotlin/org/neo4j/graphql/examples/graphqlspringboot/config/GraphQLConfiguration.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
package org.neo4j.graphql.examples.graphqlspringboot.config | ||
|
||
import com.expediagroup.graphql.SchemaGeneratorConfig | ||
import com.expediagroup.graphql.TopLevelObject | ||
import com.expediagroup.graphql.extensions.print | ||
import com.expediagroup.graphql.generator.SchemaGenerator | ||
import com.expediagroup.graphql.spring.operations.Mutation | ||
import com.expediagroup.graphql.spring.operations.Query | ||
import com.expediagroup.graphql.spring.operations.Subscription | ||
import graphql.schema.GraphQLObjectType | ||
import graphql.schema.GraphQLSchema | ||
import org.neo4j.graphql.DataFetchingInterceptor | ||
import org.neo4j.graphql.SchemaBuilder | ||
import org.neo4j.graphql.SchemaConfig | ||
import org.slf4j.LoggerFactory | ||
import org.springframework.aop.framework.Advised | ||
import org.springframework.aop.support.AopUtils | ||
import org.springframework.beans.factory.annotation.Autowired | ||
import org.springframework.beans.factory.annotation.Value | ||
import org.springframework.context.annotation.Bean | ||
import org.springframework.context.annotation.Configuration | ||
import org.springframework.context.annotation.Primary | ||
import org.springframework.core.io.Resource | ||
import java.util.* | ||
|
||
/** | ||
* Configuration of the GraphQL schemas | ||
*/ | ||
@Configuration | ||
open class GraphQLConfiguration { | ||
private val logger = LoggerFactory.getLogger(GraphQLConfiguration::class.java) | ||
|
||
/** | ||
* This generates the augmented neo4j schema | ||
*/ | ||
@Bean | ||
open fun neo4jSchema( | ||
@Value("classpath:schema.graphql") graphQl: Resource, | ||
@Autowired(required = false) dataFetchingInterceptor: DataFetchingInterceptor | ||
): GraphQLSchema { | ||
val schema = graphQl.inputStream.bufferedReader().use { it.readText() } | ||
return SchemaBuilder.buildSchema(schema, SchemaConfig(), dataFetchingInterceptor) | ||
} | ||
|
||
/** | ||
* This generates the spring schema provided generated by the `graphql-kotlin-spring-server` | ||
*/ | ||
@Bean | ||
open fun springSchema( | ||
queries: Optional<List<Query>>, | ||
mutations: Optional<List<Mutation>>, | ||
subscriptions: Optional<List<Subscription>>, | ||
schemaConfig: SchemaGeneratorConfig | ||
): GraphQLSchema { | ||
val generator = SchemaGenerator(schemaConfig) | ||
return generator.use { | ||
it.generateSchema( | ||
queries = queries.orElse(emptyList()).toTopLevelObjects(), | ||
mutations = mutations.orElse(emptyList()).toTopLevelObjects(), | ||
subscriptions = subscriptions.orElse(emptyList()).toTopLevelObjects() | ||
) | ||
} | ||
} | ||
|
||
/** | ||
* This merges the springSchema and the neo4jSchema | ||
*/ | ||
@Bean | ||
@Primary | ||
open fun mergedSchema(neo4jSchema: GraphQLSchema, springSchema: GraphQLSchema): GraphQLSchema { | ||
val builder: GraphQLSchema.Builder = GraphQLSchema.newSchema(neo4jSchema) | ||
|
||
val springCodeRegistry = springSchema.codeRegistry | ||
builder.codeRegistry(neo4jSchema.codeRegistry.transform { crBuilder -> | ||
crBuilder.dataFetchers(springCodeRegistry) | ||
crBuilder.typeResolvers(springCodeRegistry) | ||
}) | ||
|
||
val allTypes = (neo4jSchema.typeMap + springSchema.typeMap).toMutableMap() | ||
allTypes.replace("Query", mergeType(neo4jSchema.queryType, springSchema.queryType)) | ||
allTypes.replace("Mutation", mergeType(neo4jSchema.mutationType, springSchema.mutationType)) | ||
|
||
allTypes["Query"]?.let { builder.query(it as GraphQLObjectType) } | ||
allTypes["Mutation"]?.let { builder.mutation(it as GraphQLObjectType) } | ||
allTypes["Subscription"]?.let { builder.subscription(it as GraphQLObjectType) } | ||
|
||
builder.clearAdditionalTypes() | ||
allTypes.values.forEach { builder.additionalType(it) } | ||
|
||
val schema = builder.build() | ||
|
||
logger.info("\n${schema.print()}") | ||
|
||
return schema | ||
} | ||
|
||
private fun mergeType(t1: GraphQLObjectType?, t2: GraphQLObjectType?): GraphQLObjectType? { | ||
if (t1 == null) { | ||
return t2 | ||
} | ||
if (t2 == null) { | ||
return t1 | ||
} | ||
val builder = GraphQLObjectType.newObject(t1) | ||
t2.fieldDefinitions.forEach { builder.field(it) } | ||
return builder.build() | ||
} | ||
|
||
// This was copied over from {@link com.expediagroup.graphql.spring.extensions.generatorExtensions.kt } | ||
private fun List<Any>.toTopLevelObjects() = this.map { | ||
val klazz = if (AopUtils.isAopProxy(it) && it is Advised) { | ||
it.targetSource.target!!::class | ||
} else { | ||
it::class | ||
} | ||
TopLevelObject(it, klazz) | ||
} | ||
} |
Oops, something went wrong.