Skip to content

Commit 92a5618

Browse files
authored
update to neo4j 4.2 (#200)
this requires also an update to java 11 additionally I cleaned up the dependencies
1 parent 0f89393 commit 92a5618

File tree

10 files changed

+143
-155
lines changed

10 files changed

+143
-155
lines changed

core/pom.xml

Lines changed: 16 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -15,45 +15,23 @@
1515
<description>GraphQL to Cypher Mapping</description>
1616

1717
<dependencies>
18-
<dependency>
19-
<groupId>org.neo4j.driver</groupId>
20-
<artifactId>neo4j-java-driver</artifactId>
21-
<scope>test</scope>
22-
</dependency>
23-
<dependency>
24-
<groupId>com.sparkjava</groupId>
25-
<artifactId>spark-core</artifactId>
26-
<version>2.7.2</version>
27-
<scope>test</scope>
28-
</dependency>
29-
<dependency>
30-
<groupId>com.google.code.gson</groupId>
31-
<artifactId>gson</artifactId>
32-
<version>2.8.5</version>
33-
<scope>test</scope>
34-
</dependency>
3518
<dependency>
3619
<groupId>org.neo4j.test</groupId>
3720
<artifactId>neo4j-harness</artifactId>
3821
<version>${neo4j.version}</version>
3922
<scope>test</scope>
23+
<exclusions>
24+
<exclusion>
25+
<artifactId>slf4j-nop</artifactId>
26+
<groupId>org.slf4j</groupId>
27+
</exclusion>
28+
</exclusions>
4029
</dependency>
4130
<dependency>
4231
<groupId>org.neo4j.procedure</groupId>
4332
<artifactId>apoc</artifactId>
4433
<version>${neo4j-apoc.version}</version>
45-
<scope>test</scope>
46-
</dependency>
47-
<dependency>
48-
<groupId>org.neo4j</groupId>
49-
<artifactId>server-api</artifactId>
50-
<version>${neo4j.version}</version>
51-
<scope>test</scope>
52-
</dependency>
53-
<dependency>
54-
<groupId>org.codehaus.jackson</groupId>
55-
<artifactId>jackson-mapper-asl</artifactId>
56-
<version>1.9.13</version>
34+
<classifier>all</classifier>
5735
<scope>test</scope>
5836
</dependency>
5937
<dependency>
@@ -66,16 +44,9 @@
6644
<artifactId>junit-jupiter</artifactId>
6745
<scope>test</scope>
6846
</dependency>
69-
<dependency>
70-
<groupId>junit</groupId>
71-
<artifactId>junit</artifactId>
72-
<version>4.13.1</version>
73-
<scope>test</scope>
74-
</dependency>
7547
<dependency>
7648
<groupId>org.assertj</groupId>
7749
<artifactId>assertj-core</artifactId>
78-
<version>3.12.2</version>
7950
<scope>test</scope>
8051
</dependency>
8152
<dependency>
@@ -94,19 +65,19 @@
9465
<dependencyManagement>
9566
<dependencies>
9667
<dependency>
97-
<groupId>org.neo4j.driver</groupId>
98-
<artifactId>neo4j-java-driver</artifactId>
99-
<version>${driver.version}</version>
68+
<groupId>org.junit.jupiter</groupId>
69+
<artifactId>junit-jupiter</artifactId>
70+
<version>${junit-jupiter.version}</version>
10071
</dependency>
10172
<dependency>
102-
<groupId>org.neo4j</groupId>
103-
<artifactId>neo4j-kernel</artifactId>
104-
<version>${neo4j.version}</version>
73+
<groupId>org.junit.jupiter</groupId>
74+
<artifactId>junit-jupiter-api</artifactId>
75+
<version>${junit-jupiter.version}</version>
10576
</dependency>
10677
<dependency>
107-
<groupId>org.junit.jupiter</groupId>
108-
<artifactId>junit-jupiter</artifactId>
109-
<version>5.5.1</version>
78+
<groupId>org.assertj</groupId>
79+
<artifactId>assertj-core</artifactId>
80+
<version>3.19.0</version>
11081
</dependency>
11182
<dependency>
11283
<groupId>org.slf4j</groupId>

core/src/main/kotlin/org/neo4j/graphql/handler/BaseDataFetcher.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ abstract class BaseDataFetcher(val fieldDefinition: GraphQLFieldDefinition) : Pr
2929
.withPrettyPrint(true)
3030
.build()
3131
).render(statement)
32-
return Cypher(query, statement.parameters, fieldDefinition.type, variable = variable)
32+
return Cypher(query, statement.parameters, fieldDefinition.type, variable = field.aliasOrName())
3333
}
3434

3535
protected abstract fun generateCypher(variable: String, field: Field, env: DataFetchingEnvironment): Statement

core/src/test/kotlin/DataFetcherInterceptorDemo.kt

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
package demo
22

33
import graphql.GraphQL
4-
import graphql.language.VariableReference
54
import graphql.schema.*
65
import org.intellij.lang.annotations.Language
7-
import org.neo4j.driver.v1.AuthTokens
8-
import org.neo4j.driver.v1.GraphDatabase
6+
import org.neo4j.driver.AuthTokens
7+
import org.neo4j.driver.Driver
8+
import org.neo4j.driver.GraphDatabase
99
import org.neo4j.graphql.Cypher
1010
import org.neo4j.graphql.DataFetchingInterceptor
1111
import org.neo4j.graphql.SchemaBuilder
@@ -14,19 +14,19 @@ import java.math.BigInteger
1414

1515

1616
fun initBoundSchema(schema: String): GraphQLSchema {
17-
val driver = GraphDatabase.driver("bolt://localhost", AuthTokens.basic("neo4j", "test"))
17+
val driver: Driver = GraphDatabase.driver("bolt://localhost", AuthTokens.basic("neo4j", "test"))
1818

1919
val dataFetchingInterceptor = object : DataFetchingInterceptor {
2020
override fun fetchData(env: DataFetchingEnvironment, delegate: DataFetcher<Cypher>): Any {
21-
val cypher = delegate.get(env)
21+
val (cypher, params, type, variable) = delegate.get(env)
2222
return driver.session().use { session ->
23-
val result = session.run(cypher.query, cypher.params.mapValues { toBoltValue(it.value, env.variables) })
24-
if (isListType(cypher.type)) {
25-
result.list().map { record -> record.get(cypher.variable).asObject() }
23+
val result = session.run(cypher, params.mapValues { toBoltValue(it.value) })
24+
if (isListType(type)) {
25+
result.list().map { record -> record.get(variable).asObject() }
2626

2727
} else {
28-
result.list().map { record -> record.get(cypher.variable).asObject() }
29-
.firstOrNull() ?: emptyMap<String, Any>()
28+
result.list().map { record -> record.get(variable).asObject() }.firstOrNull()
29+
?: emptyMap<String, Any>()
3030
}
3131
}
3232
}
@@ -45,8 +45,7 @@ fun main() {
4545
val movies = graphql.execute("{ movie { title }}")
4646
}
4747

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

core/src/test/kotlin/GraphQLServer.kt

Lines changed: 73 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -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
811
import 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
1318
import 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

1923
const val schema = """
2024
type Person {
@@ -29,69 +33,85 @@ type Movie {
2933
}
3034
"""
3135

36+
val mapper = ObjectMapper()
37+
3238
fun 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

core/src/test/kotlin/org/neo4j/graphql/utils/AsciiDocTestSuite.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package org.neo4j.graphql.utils
22

3+
import com.fasterxml.jackson.databind.ObjectMapper
34
import com.intellij.rt.execution.junit.FileComparisonFailure
4-
import org.codehaus.jackson.map.ObjectMapper
55
import org.junit.jupiter.api.DynamicContainer
66
import org.junit.jupiter.api.DynamicNode
77
import org.junit.jupiter.api.DynamicTest
@@ -269,7 +269,7 @@ open class AsciiDocTestSuite(
269269
}
270270
}
271271

272-
private fun fixNumber(v: Any?): Any? = when (v) {
272+
fun fixNumber(v: Any?): Any? = when (v) {
273273
is Float -> v.toDouble()
274274
is Int -> v.toLong()
275275
is Iterable<*> -> v.map { fixNumber(it) }

0 commit comments

Comments
 (0)