Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new example for spring-boot-dgs #227

Merged
merged 1 commit into from
May 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 145 additions & 0 deletions examples/dgs-spring-boot/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
<?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.3.1-SNAPSHOT</version>
</parent>

<groupId>org.neo4j.graphql.examples</groupId>
<artifactId>dgs-spring-boot</artifactId>

<name>Example - dgs-spring-boot</name>
<description>Example for using neo4j-graphql-java with Spring Boot and Netflix Domain Graph Service (DGS)</description>

<properties>
<testcontainers.version>1.15.3</testcontainers.version>
<spring-boot.version>2.3.10.RELEASE</spring-boot.version>
</properties>

<dependencies>
<!-- spring dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- neo4j driver + the neo4j-graphql-java library -->
<dependency>
<groupId>org.neo4j.driver</groupId>
<artifactId>neo4j-java-driver-spring-boot-starter</artifactId>
<version>4.2.4.0</version>
</dependency>
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-graphql-java</artifactId>
<version>1.3.1-SNAPSHOT</version>
</dependency>

<dependency>
<groupId>com.netflix.graphql.dgs</groupId>
<artifactId>graphql-dgs-spring-boot-starter</artifactId>
<version>3.12.1</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>${spring-boot.version}</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>

<plugin>
<groupId>io.github.deweyjose</groupId>
<artifactId>graphqlcodegen-maven-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<configuration>
<schemaPaths>
<param>src/main/resources/schema/schema.graphqls</param>
</schemaPaths>
<generateClient>true</generateClient>
<generateInterfaces>true</generateInterfaces>
<generateDataTypes>true</generateDataTypes>
<packageName>org.neo4j.graphql.examples.dgsspringboot.types</packageName>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>${project.build.directory}/generated-sources</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
36 changes: 36 additions & 0 deletions examples/dgs-spring-boot/readme.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
= Example: Integration of Neo4j-GraphQL-Java into a Spring Boot application in combination with Netflix DGS

== Overview

This example uses the https://netflix.github.io/dgs/[Netflix DGS Framework (Domain Graph Service)]

In the link:src/main/kotlin/org/neo4j/graphql/examples/dgsspringboot/config/Neo4jConfiguration.kt[Neo4jConfiguration]
a DataFetchingInterceptor is created, which will be bound to all fields augmented 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/dgsspringboot/config/GraphQLConfiguration.kt[GraphQLConfiguration]
the type definitions of link:src/main/resources/neo4j.graphql[schema] are loaded and augmented.

In this example some fields of the enhanced type (neo4j) are extended with
link:src/main/kotlin/org/neo4j/graphql/examples/dgsspringboot/datafetcher/AdditionalDataFetcher.kt[custom data fetcher] whose link:src/main/resources/schema/schema.graphqls[schema is separately defined].

With This in place you can

== Run the example

1. link:src/main/resources/application.yaml[configure your neo4j db] or use a public one
2. run the link:src/main/kotlin/org/neo4j/graphql/examples/dgsspringboot/DgsSpringBootApplication.kt[spring boot application]
3. open http://localhost:8080/graphiql to run some graphql queries e.g. try:

```graphql
query{
other
movie (first: 3){
title
bar
javaData {
name
}
}
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.neo4j.graphql.examples.dgsspringboot

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
open class DgsSpringBootApplication

fun main(args: Array<String>) {
runApplication<DgsSpringBootApplication>(*args)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package org.neo4j.graphql.examples.dgsspringboot.config

import com.netflix.graphql.dgs.DgsCodeRegistry
import com.netflix.graphql.dgs.DgsComponent
import com.netflix.graphql.dgs.DgsRuntimeWiring
import com.netflix.graphql.dgs.DgsTypeDefinitionRegistry
import graphql.schema.GraphQLCodeRegistry
import graphql.schema.idl.RuntimeWiring
import graphql.schema.idl.SchemaParser
import graphql.schema.idl.TypeDefinitionRegistry
import org.neo4j.graphql.DataFetchingInterceptor
import org.neo4j.graphql.SchemaBuilder
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Value
import org.springframework.core.io.Resource
import javax.annotation.PostConstruct


/**
* Configuration of the GraphQL schemas
*/
@DgsComponent
open class GraphQLConfiguration {

@Value("classpath:neo4j.graphql")
lateinit var graphQl: Resource

@Autowired(required = false)
lateinit var dataFetchingInterceptor: DataFetchingInterceptor

lateinit var schemaBuilder: SchemaBuilder

@PostConstruct
fun postConstruct() {
val schema = graphQl.inputStream.bufferedReader().use { it.readText() }
val typeDefinitionRegistry = SchemaParser().parse(schema)
schemaBuilder = SchemaBuilder(typeDefinitionRegistry)
schemaBuilder.augmentTypes()
}

@DgsTypeDefinitionRegistry
fun registry(): TypeDefinitionRegistry {
return schemaBuilder.typeDefinitionRegistry
}

@DgsCodeRegistry
fun codeRegistry(codeRegistryBuilder: GraphQLCodeRegistry.Builder, registry: TypeDefinitionRegistry): GraphQLCodeRegistry.Builder {
schemaBuilder.registerDataFetcher(codeRegistryBuilder, dataFetchingInterceptor, registry)
return codeRegistryBuilder
}

@DgsRuntimeWiring
fun runtimeWiring(builder: RuntimeWiring.Builder): RuntimeWiring.Builder {
schemaBuilder.registerTypeNameResolver(builder)
schemaBuilder.registerScalars(builder)
return builder
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package org.neo4j.graphql.examples.dgsspringboot.config

import graphql.schema.*
import org.neo4j.driver.Driver
import org.neo4j.driver.SessionConfig
import org.neo4j.graphql.Cypher
import org.neo4j.graphql.DataFetchingInterceptor
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.math.BigDecimal
import java.math.BigInteger

/**
* Configuration of the DataFetchingInterceptor
*/
@Configuration
open class Neo4jConfiguration {

/**
* This interceptor is 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.
*/
@Bean
open fun dataFetchingInterceptor(driver: Driver, @Value("\${database}") database: String): DataFetchingInterceptor {
return object : DataFetchingInterceptor {
override fun fetchData(env: DataFetchingEnvironment, delegate: DataFetcher<Cypher>): Any? {
val (cypher, params, type, variable) = delegate.get(env)

return driver.session(SessionConfig.forDatabase(database)).writeTransaction { tx ->
val boltParams = params.mapValues { toBoltValue(it.value) }
val result = tx.run(cypher, boltParams)
if (isListType(type)) {
result.list()
.map { record -> record.get(variable).asObject() }
} else {
result.list()
.map { record -> record.get(variable).asObject() }
.firstOrNull() ?: emptyMap<String, Any>()
}
}
}
}
}

companion object {
private fun toBoltValue(value: Any?) = when (value) {
is BigInteger -> value.longValueExact()
is BigDecimal -> value.toDouble()
else -> value
}

private fun isListType(type: GraphQLType?): Boolean = when (type) {
is GraphQLList -> true
is GraphQLNonNull -> isListType(type.wrappedType)
else -> false
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.neo4j.graphql.examples.dgsspringboot.datafetcher

import com.netflix.graphql.dgs.DgsComponent
import com.netflix.graphql.dgs.DgsData
import graphql.schema.DataFetchingEnvironment
import org.neo4j.graphql.examples.dgsspringboot.types.types.JavaData
import java.util.*


@DgsComponent
class AdditionalDataFetcher {
@DgsData(parentType = "Movie", field = "bar")
fun bar(): String {
return "foo"
}

@DgsData(parentType = "Movie", field = "javaData")
fun javaData(env: DataFetchingEnvironment): List<JavaData> {
val title = env.getSource<Map<String, *>>()["title"]
return Collections.singletonList(JavaData("test $title"))
}

@DgsData(parentType = "Query", field = "other")
fun other(): String {
return "other"
}
}
10 changes: 10 additions & 0 deletions examples/dgs-spring-boot/src/main/resources/application.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
org:
neo4j:
driver:
uri: bolt://demo.neo4jlabs.com:7687
authentication:
username: movies
password: movies
config :
encrypted : true
database: movies
3 changes: 3 additions & 0 deletions examples/dgs-spring-boot/src/main/resources/neo4j.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
type Movie {
title: String
}
12 changes: 12 additions & 0 deletions examples/dgs-spring-boot/src/main/resources/schema/schema.graphqls
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
extend type Movie {
bar: String @ignore
javaData: [JavaData!] @ignore
}

type JavaData {
name: String
}

extend type Query {
other: String
}
Loading