Skip to content

Commit

Permalink
Add spring boot integration example (neo4j-graphql#126)
Browse files Browse the repository at this point in the history
* add spring boot integration example

* adjustments after review
  • Loading branch information
Andy2003 authored Oct 22, 2020
1 parent 0c0b262 commit 86f53ba
Show file tree
Hide file tree
Showing 67 changed files with 638 additions and 86 deletions.
89 changes: 89 additions & 0 deletions core/pom.xml
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.
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,4 @@ input _RatedInput { rating:Int }
assertEquals(true, typeDefinitionRegistry.getType("_MovieFilter").isPresent)
}
}
}
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.
102 changes: 102 additions & 0 deletions examples/graphql-spring-boot/pom.xml
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>
21 changes: 21 additions & 0 deletions examples/graphql-spring-boot/readme.adoc
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
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)
}
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)
}
}
Loading

0 comments on commit 86f53ba

Please sign in to comment.