Skip to content

Commit

Permalink
Add new example for spring-boot-dgs.
Browse files Browse the repository at this point in the history
This example shows, how a type can be extended with a custom data fetcher. It utilises Netflix Domain Graph Service (DGS).
  • Loading branch information
Andy2003 committed May 18, 2021
1 parent 9e83210 commit 29e2bde
Show file tree
Hide file tree
Showing 12 changed files with 487 additions and 2 deletions.
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

0 comments on commit 29e2bde

Please sign in to comment.