neo4j-graphql / neo4j-graphql-java

Neo4j Labs Project: Pure JVM translation for GraphQL queries and mutations to Neo4j's Cypher
Apache License 2.0
105 stars 49 forks source link

Integration with Spring data #66

Open arabbani opened 5 years ago

arabbani commented 5 years ago

How to integrate this library with Spring data

Andy2003 commented 5 years ago

Just configure spring data neo4j and reuse the neo4j-driver:

import org.neo4j.driver.v1.AuthTokens
import org.neo4j.driver.v1.Driver
import org.neo4j.driver.v1.GraphDatabase
import org.neo4j.ogm.config.UsernamePasswordCredentials
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration
class Neo4jGraphqlConfig {
    @Autowired
    private val configuration: org.neo4j.ogm.config.Configuration? = null

    @Bean(destroyMethod = "close")
    fun driver(): Driver {
        val credentials = configuration!!.credentials as UsernamePasswordCredentials
        return GraphDatabase.driver(configuration.uri,
            AuthTokens.basic(credentials.username, credentials.password))
    }
}

and

import graphql.language.VariableReference
import graphql.schema.*
import graphql.schema.idl.SchemaParser
import org.apache.commons.io.IOUtils
import org.neo4j.driver.v1.Driver
import org.neo4j.graphql.Cypher
import org.neo4j.graphql.DataFetchingInterceptor
import org.neo4j.graphql.SchemaBuilder
import org.neo4j.graphql.SchemaConfig
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.core.io.Resource
import org.springframework.security.core.Authentication
import java.io.IOException
import java.math.BigDecimal
import java.math.BigInteger
import java.nio.charset.StandardCharsets

@Configuration
class GraphQLConfig {

    @Bean
    @Throws(IOException::class)
    fun neo4jSchema(
            @Value("classpath:graphql/neo4j/*.graphql") graphQls: Array<Resource>,
            @Autowired(required = false) dataFetchingInterceptor: DataFetchingInterceptor): GraphQLSchema {
        val idl = StringBuilder()
        for (resource in graphQls) {
            resource.inputStream.use { inputStream -> idl.append(IOUtils.toString(inputStream, StandardCharsets.UTF_8)) }
        }
        val schemaParser = SchemaParser()
        val typeDefinitionRegistry = schemaParser.parse(idl.toString())
        return SchemaBuilder.buildSchema(typeDefinitionRegistry, SchemaConfig(), dataFetchingInterceptor)
    }

    @Bean
    fun dataFetchingInterceptor(
            driver: Driver,
            authentication: Authentication
    ): DataFetchingInterceptor {
        return object : DataFetchingInterceptor {

            override fun fetchData(env: DataFetchingEnvironment, delegate: DataFetcher<Cypher>): Any? {
                val cypher = delegate.get(env)
                val params = toBoltValue(cypher.params, env.variables)
                return driver.session().writeTransaction<Any> { tx ->
                    val boltParams = cypher.params.mapValues { toBoltValue(it.value, env.variables) }
                    val result = tx.run(cypher.query, boltParams)
                    val key = result.keys().stream().findFirst().orElse(null)
                    if (isListType(cypher.type)) {
                        result.list()
                                .map { record -> record.get(key).asObject() }
                    } else {
                        result.list()
                                .map { record -> record.get(key).asObject() }
                                .firstOrNull() ?: emptyMap<String, Any>()
                    }
                }
            }
        }
    }

    private fun isListType(type: GraphQLType?): Boolean {
        if (type is GraphQLList) {
            return true
        }
        return if (type is GraphQLNonNull) {
            isListType(type.wrappedType)
        } else false
    }

    fun toBoltValue(value: Any?, params: Map<String, Any?>) = when (value) {
        is VariableReference -> params[value.name]
        is BigInteger -> value.longValueExact()
        is BigDecimal -> value.toDouble()
        else -> value
    }
}

I'm using

<dependency>
    <groupId>io.leangen.graphql</groupId>
    <artifactId>graphql-spqr-spring-boot-starter</artifactId>
    <version>0.0.5-SNAPSHOT</version>
</dependency>
Andy2003 commented 4 years ago

relates to #119 and #97