diff --git a/pom.xml b/pom.xml
index c7667f3c..c4977e4f 100755
--- a/pom.xml
+++ b/pom.xml
@@ -118,6 +118,12 @@
neo4j-cypher-dsl
2020.0.1
+
+ ch.qos.logback
+ logback-classic
+ 1.2.3
+ test
+
diff --git a/readme.adoc b/readme.adoc
index 170d4a5f..274b92bf 100644
--- a/readme.adoc
+++ b/readme.adoc
@@ -83,6 +83,7 @@ You find more usage examples in the:
* link:src/test/resources/translator-tests1.adoc[Translator 1 TCK]
* link:src/test/resources/translator-tests2.adoc[Translator 2 TCK]
* link:src/test/resources/translator-tests3.adoc[Translator 3 TCK]
+* link:src/test/resources/translator-tests-custom-scalars.adoc[Translator custom scalars TCK]
* link:src/test/resources/optimized-query-for-filter.adoc[Alternative Filter TCK]
== Demo
@@ -246,6 +247,7 @@ This example doesn't handle introspection queries, but the one in the test direc
* date(time)
* interfaces
* complex filter parameters, with optional query optimization strategy
+* scalars
* spatial
=== Next
@@ -254,7 +256,6 @@ This example doesn't handle introspection queries, but the one in the test direc
* sorting (nested)
* input types
* unions
-* scalars
== Documentation
diff --git a/src/main/kotlin/org/neo4j/graphql/NoOpCoercing.kt b/src/main/kotlin/org/neo4j/graphql/NoOpCoercing.kt
new file mode 100644
index 00000000..742ce17f
--- /dev/null
+++ b/src/main/kotlin/org/neo4j/graphql/NoOpCoercing.kt
@@ -0,0 +1,11 @@
+package org.neo4j.graphql
+
+import graphql.schema.Coercing
+
+object NoOpCoercing : Coercing {
+ override fun parseLiteral(input: Any?) = input
+
+ override fun serialize(dataFetcherResult: Any?) = dataFetcherResult
+
+ override fun parseValue(input: Any?) = input
+}
diff --git a/src/main/kotlin/org/neo4j/graphql/SchemaBuilder.kt b/src/main/kotlin/org/neo4j/graphql/SchemaBuilder.kt
index 5db55533..73d6d54f 100644
--- a/src/main/kotlin/org/neo4j/graphql/SchemaBuilder.kt
+++ b/src/main/kotlin/org/neo4j/graphql/SchemaBuilder.kt
@@ -4,6 +4,7 @@ import graphql.Scalars
import graphql.language.*
import graphql.schema.*
import graphql.schema.idl.RuntimeWiring
+import graphql.schema.idl.ScalarInfo.STANDARD_SCALAR_DEFINITIONS
import graphql.schema.idl.SchemaGenerator
import graphql.schema.idl.SchemaParser
import graphql.schema.idl.TypeDefinitionRegistry
@@ -40,7 +41,23 @@ object SchemaBuilder {
enhancedRegistry.add(ObjectTypeDefinition.newObjectTypeDefinition().name(QUERY).build())
}
- val builder = RuntimeWiring.newRuntimeWiring().scalar(DynamicProperties.INSTANCE)
+ val builder = RuntimeWiring.newRuntimeWiring()
+ typeDefinitionRegistry.scalars()
+ .filterNot { entry -> STANDARD_SCALAR_DEFINITIONS.containsKey(entry.key) }
+ .forEach { (name, definition) ->
+ val scalar = when (name) {
+ "DynamicProperties" -> DynamicProperties.INSTANCE
+ else -> GraphQLScalarType.newScalar()
+ .name(name)
+ .description(definition.description?.getContent() ?: "Scalar $name")
+ .withDirectives(*definition.directives.filterIsInstance().toTypedArray())
+ .definition(definition)
+ .coercing(NoOpCoercing)
+ .build()
+ }
+ builder.scalar(scalar)
+ }
+
enhancedRegistry
.getTypes(InterfaceTypeDefinition::class.java)
@@ -205,4 +222,4 @@ object SchemaBuilder {
typeDefinitionRegistry.add(inputType)
return inputName
}
-}
\ No newline at end of file
+}
diff --git a/src/test/kotlin/org/neo4j/graphql/CypherTests.kt b/src/test/kotlin/org/neo4j/graphql/CypherTests.kt
index e8b20e50..6b9bfabe 100644
--- a/src/test/kotlin/org/neo4j/graphql/CypherTests.kt
+++ b/src/test/kotlin/org/neo4j/graphql/CypherTests.kt
@@ -29,6 +29,9 @@ class CypherTests {
@TestFactory
fun `translator-tests3`() = CypherTestSuite("translator-tests3.adoc").run()
+ @TestFactory
+ fun `translator-tests-custom-scalars`() = CypherTestSuite("translator-tests-custom-scalars.adoc").run()
+
@TestFactory
fun `optimized-query-for-filter`() = CypherTestSuite("optimized-query-for-filter.adoc").run()
-}
\ No newline at end of file
+}
diff --git a/src/test/resources/translator-tests-custom-scalars.adoc b/src/test/resources/translator-tests-custom-scalars.adoc
new file mode 100644
index 00000000..1095ceed
--- /dev/null
+++ b/src/test/resources/translator-tests-custom-scalars.adoc
@@ -0,0 +1,202 @@
+:toc:
+
+= Translator Tests
+
+== Schema
+
+[source,graphql,schema=true]
+----
+scalar Date
+type Movie {
+ _id: ID!
+ title: String!
+ released: Date
+}
+----
+
+== Tests
+
+=== Create
+
+.GraphQL-Query
+[source,graphql]
+----
+mutation {
+ createMovie(title:"Forrest Gump", released: 1994) {
+ title
+ released
+ }
+}
+----
+
+.Cypher params
+[source,json]
+----
+{
+ "createMovieTitle": "Forrest Gump",
+ "createMovieReleased": 1994
+}
+----
+
+.Cypher
+[source,cypher]
+----
+CREATE (createMovie:Movie { title: $createMovieTitle, released: $createMovieReleased })
+WITH createMovie
+RETURN createMovie { .title, .released } AS createMovie
+----
+
+=== Update
+
+.GraphQL-Query
+[source,graphql]
+----
+mutation {
+ updateMovie(_id: 1, released: 1995) {
+ title
+ released
+ }
+}
+----
+
+.Cypher params
+[source,json]
+----
+{
+ "updateMovie_id": 1,
+ "updateMovieReleased": 1995
+}
+----
+
+.Cypher
+[source,cypher]
+----
+MATCH (updateMovie: Movie)
+WHERE ID(updateMovie) = toInteger($updateMovie_id)
+SET updateMovie = { released: $updateMovieReleased }
+WITH updateMovie
+RETURN updateMovie { .title, .released } AS updateMovie
+----
+
+=== Merge
+
+.GraphQL-Query
+[source,graphql]
+----
+mutation {
+ mergeMovie(_id: 1, released: 1995) {
+ title
+ released
+ }
+}
+----
+
+.Cypher params
+[source,json]
+----
+{
+ "mergeMovie_id": 1,
+ "mergeMovieReleased": 1995
+}
+----
+
+.Cypher
+[source,cypher]
+----
+MATCH (mergeMovie: Movie)
+WHERE ID(mergeMovie) = toInteger($mergeMovie_id)
+SET mergeMovie += { released: $mergeMovieReleased }
+WITH mergeMovie
+RETURN mergeMovie { .title, .released } AS mergeMovie
+----
+
+=== Merge null
+
+.GraphQL-Query
+[source,graphql]
+----
+mutation {
+ updateMovie(_id: 1, released: null) {
+ title
+ released
+ }
+}
+----
+
+.Cypher params
+[source,json]
+----
+{
+ "updateMovie_id": 1,
+ "updateMovieReleased": null
+}
+----
+
+.Cypher
+[source,cypher]
+----
+MATCH (updateMovie: Movie)
+WHERE ID(updateMovie) = toInteger($updateMovie_id)
+SET updateMovie = { released: $updateMovieReleased }
+WITH updateMovie
+RETURN updateMovie { .title, .released } AS updateMovie
+----
+
+=== Find
+
+.GraphQL-Query
+[source,graphql]
+----
+{
+ movie(released: 1994) {
+ title
+ released
+ }
+}
+----
+
+.Cypher params
+[source,json]
+----
+{
+ "movieReleased": 1994
+}
+----
+
+.Cypher
+[source,cypher]
+----
+MATCH (movie: Movie)
+WHERE movie.released = $movieReleased
+RETURN movie { .title, .released } AS movie
+----
+
+=== Filter
+
+.GraphQL-Query
+[source,graphql]
+----
+{
+ movie(filter:{released_gte: 1994}) {
+ title
+ released
+ }
+}
+----
+
+.Cypher params
+[source,json]
+----
+{
+ "filterMovieReleased_GTE": 1994
+}
+----
+
+.Cypher
+[source,cypher]
+----
+MATCH (movie: Movie)
+WHERE movie.released >= $filterMovieReleased_GTE
+RETURN movie { .title, .released } AS movie
+----
+