@@ -4,17 +4,21 @@ package demo
44// curl -H content-type:application/json -d'{"query": "{ movie { title, released }}"}' http://localhost:4567/graphql
55// GraphiQL: https://neo4j-graphql.github.io/graphiql4all/index.html?graphqlEndpoint=http%3A%2F%2Flocalhost%3A4567%2Fgraphql&query=query%20%7B%0A%20%20movie%20%7B%20title%7D%0A%7D
66
7- import com.google.gson.Gson
7+ import com.fasterxml.jackson.databind.ObjectMapper
8+ import com.sun.net.httpserver.HttpExchange
9+ import com.sun.net.httpserver.HttpServer
10+ import graphql.ExecutionInput
811import graphql.GraphQL
9- import org.neo4j.driver.v1.AuthTokens
10- import org.neo4j.driver.v1.Config
11- import org.neo4j.driver.v1.GraphDatabase
12- import org.neo4j.driver.v1.Values
12+ import graphql.schema.DataFetcher
13+ import graphql.schema.DataFetchingEnvironment
14+ import org.neo4j.driver.AuthTokens
15+ import org.neo4j.driver.Config
16+ import org.neo4j.driver.GraphDatabase
17+ import org.neo4j.driver.Values
1318import org.neo4j.graphql.*
14- import spark.Request
15- import spark.Response
16- import spark.Spark
17- import java.util.*
19+ import java.net.InetSocketAddress
20+ import kotlin.streams.toList
21+
1822
1923const val schema = """
2024type Person {
@@ -29,69 +33,85 @@ type Movie {
2933}
3034"""
3135
36+ val mapper = ObjectMapper ()
37+
3238fun main () {
33- val gson = Gson ()
34- fun render (value : Any ) = gson.toJson(value)
35- fun parseBody (value : String ) = gson.fromJson(value, Map ::class .java)
3639
3740 fun query (payload : Map <* , * >) = (payload[" query" ]!! as String ).also { println (it) }
3841 fun params (payload : Map <* , * >): Map <String , Any ?> = payload[" variables" ]
3942 .let {
4043 @Suppress(" UNCHECKED_CAST" )
4144 when (it) {
42- is String -> if (it.isBlank()) emptyMap<String , Any ?>() else gson.fromJson (it, Map ::class .java)
45+ is String -> if (it.isBlank()) emptyMap<String , Any ?>() else mapper.readValue (it, Map ::class .java)
4346 is Map <* , * > -> it
4447 else -> emptyMap<String , Any ?>()
4548 } as Map <String , Any ?>
4649 }.also { println (it) }
4750
51+ val driver = GraphDatabase .driver(" bolt://localhost" , AuthTokens .basic(" neo4j" , " test" ), Config .builder().withoutEncryption().build())
52+
53+ val graphQLSchema = SchemaBuilder .buildSchema(schema, dataFetchingInterceptor = object : DataFetchingInterceptor {
54+ override fun fetchData (env : DataFetchingEnvironment , delegate : DataFetcher <Cypher >): Any? {
55+ val (cypher, params, type, variable) = delegate.get(env)
56+ println (cypher)
57+ println (params)
58+ return driver.session().use { session ->
59+ try {
60+ val result = session.run (cypher, Values .value(params))
61+ when {
62+ type?.isList() == true -> result.stream().map { it[variable].asObject() }.toList()
63+ else -> result.stream().map { it[variable].asObject() }.findFirst().orElse(emptyMap<String , Any >())
64+ }
65+ } catch (e: Exception ) {
66+ e.printStackTrace()
67+ }
68+ }
69+ }
70+ })
4871
49- val graphQLSchema = SchemaBuilder .buildSchema(schema)
50- println (graphQLSchema)
5172 val schema = GraphQL .newGraphQL(graphQLSchema).build()
52- val translator = Translator (graphQLSchema)
53- fun translate (query : String , params : Map <String , Any ?>) = try {
54- val ctx = QueryContext (optimizedQuery = setOf (QueryContext .OptimizationStrategy .FILTER_AS_MATCH ))
55- translator.translate(query, params, ctx)
56- } catch (e: OptimizedQueryException ) {
57- translator.translate(query, params)
58- }
5973
60- val driver = GraphDatabase .driver(" bolt://localhost" , AuthTokens .basic(" neo4j" , " test" ), Config .build().withoutEncryption().build())
61- fun run (cypher : Cypher ) = driver.session().use {
62- println (cypher.query)
63- println (cypher.params)
64- try {
65- // todo fix parameter mapping in translator
66- val result = it.run (cypher.query, Values .value(cypher.params))
67- val value = if (cypher.type?.isList() == true ) {
68- result.list().map { row -> row.get(cypher.variable).asObject() }
69- } else {
70- result.list().map { record -> record.get(cypher.variable).asObject() }
71- .firstOrNull() ? : emptyMap<String , Any >()
74+ val server: HttpServer = HttpServer .create(InetSocketAddress (4567 ), 0 )
75+
76+ server.createContext(" /graphql" ) { req ->
77+ when {
78+ req.requestMethod == " OPTIONS" -> req.sendResponse(null )
79+ req.requestMethod == " POST" && req.requestHeaders[" Content-Type" ]?.contains(" application/json" ) == true -> {
80+ val payload = mapper.readValue(req.requestBody, Map ::class .java)
81+ val query = query(payload)
82+ val response = if (query.contains(" __schema" )) {
83+ schema.execute(query).let { println (mapper.writeValueAsString(it));it }
84+ } else {
85+ try {
86+ schema.execute(ExecutionInput
87+ .newExecutionInput()
88+ .query(query)
89+ .context(QueryContext (optimizedQuery = setOf (QueryContext .OptimizationStrategy .FILTER_AS_MATCH )))
90+ .variables(params(payload))
91+ .build())
92+ } catch (e: OptimizedQueryException ) {
93+ schema.execute(ExecutionInput
94+ .newExecutionInput()
95+ .query(query)
96+ .variables(params(payload))
97+ .build())
98+ }
99+ }
100+ req.sendResponse(response)
72101 }
73- Collections .singletonMap(cypher.variable, value)
74- } catch (e: Exception ) {
75- e.printStackTrace()
76102 }
77103 }
104+ server.start()
78105
79- // CORS
80- Spark .before(" /*" ) { req, res ->
81- res.header(" Access-Control-Allow-Origin" , " *" )
82- res.header(" Access-Control-Allow-Headers" , " *" )
83- res.type(" application/json" )
84- }
85-
86- fun handler (req : Request , @Suppress(" UNUSED_PARAMETER" ) res : Response ) = req.body().let { body ->
87- val payload = parseBody(body)
88- val query = query(payload)
89- if (query.contains(" __schema" ))
90- schema.execute(query).let { println (render(it));it }
91- else run (translate(query, params(payload)).first())
92- }
106+ }
93107
94- Spark .options(" /graphql" ) { _, _ -> " OK" }
95- Spark .post(" /graphql" , " application/json" , ::handler, ::render)
108+ private fun HttpExchange.sendResponse (data : Any? ) {
109+ val responseString = data?.let { mapper.writeValueAsString(it) }
110+ // CORS
111+ this .responseHeaders.add(" Access-Control-Allow-Origin" , " *" )
112+ this .responseHeaders.add(" Access-Control-Allow-Headers" , " *" )
113+ this .responseHeaders.add(" Content-Type" , " application/json" )
114+ this .sendResponseHeaders(200 , responseString?.length?.toLong() ? : 0 )
115+ if (responseString != null ) this .responseBody.use { it.write(responseString.toByteArray()) }
96116}
97117
0 commit comments