diff --git a/.run/AsciidocReformater.run.xml b/.run/AsciidocReformater.run.xml
index 70c82a08..0bb7888c 100644
--- a/.run/AsciidocReformater.run.xml
+++ b/.run/AsciidocReformater.run.xml
@@ -1,5 +1,5 @@
-
+
@@ -8,4 +8,4 @@
-
+
\ No newline at end of file
diff --git a/.run/JsTemplateDeleter.run.xml b/.run/JsTemplateDeleter.run.xml
new file mode 100644
index 00000000..1e89f156
--- /dev/null
+++ b/.run/JsTemplateDeleter.run.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/JsTestCaseSync.run.xml b/.run/JsTestCaseSync.run.xml
index b42b1ff3..d867530b 100644
--- a/.run/JsTestCaseSync.run.xml
+++ b/.run/JsTestCaseSync.run.xml
@@ -1,5 +1,5 @@
-
+
@@ -8,4 +8,4 @@
-
+
\ No newline at end of file
diff --git a/core/src/main/kotlin/org/atteo/evo/inflector/EnglischInflector.kt b/core/src/main/kotlin/org/atteo/evo/inflector/EnglischInflector.kt
index fb528fc6..51f02215 100644
--- a/core/src/main/kotlin/org/atteo/evo/inflector/EnglischInflector.kt
+++ b/core/src/main/kotlin/org/atteo/evo/inflector/EnglischInflector.kt
@@ -8,6 +8,7 @@ object EnglischInflector : English() {
workaround_irregular("person", "people")
// TODO
workaround_irregular("two", "twos")
+ workaround_irregular("aircraft", "aircraft")
}
private fun workaround_irregular(singular: String, plural: String) {
diff --git a/core/src/main/kotlin/org/neo4j/graphql/Constants.kt b/core/src/main/kotlin/org/neo4j/graphql/Constants.kt
index cad50286..f39ddd3f 100644
--- a/core/src/main/kotlin/org/neo4j/graphql/Constants.kt
+++ b/core/src/main/kotlin/org/neo4j/graphql/Constants.kt
@@ -25,7 +25,7 @@ object Constants {
const val CURSOR_FIELD = "cursor"
const val NODE_FIELD = "node"
const val RELATIONSHIP_FIELD = "relationship"
- const val TYPENAME_IN = "typename_IN"
+ const val TYPENAME_IN = "typename"
const val RESOLVE_TYPE = TYPE_NAME
const val RESOLVE_ID = "__id"
@@ -60,7 +60,6 @@ object Constants {
RelationshipDirective.NAME,
)
- const val OPTIONS = "options"
const val WHERE = "where"
object Types {
@@ -71,7 +70,6 @@ object Constants {
val Boolean = TypeName("Boolean")
val PageInfo = TypeName("PageInfo")
- val QueryOptions = TypeName("QueryOptions")
val SortDirection = TypeName("SortDirection")
val PointDistance = TypeName("PointDistance")
val CartesianPointDistance = TypeName("CartesianPointDistance")
diff --git a/core/src/main/kotlin/org/neo4j/graphql/QueryContext.kt b/core/src/main/kotlin/org/neo4j/graphql/QueryContext.kt
index 3e1caba6..d925465a 100644
--- a/core/src/main/kotlin/org/neo4j/graphql/QueryContext.kt
+++ b/core/src/main/kotlin/org/neo4j/graphql/QueryContext.kt
@@ -14,21 +14,27 @@ data class QueryContext @JvmOverloads constructor(
private var paramKeysPerValues = mutableMapOf>>()
fun resolve(string: String): String {
- return contextParams?.let { params ->
- CONTEXT_VARIABLE_PATTERN.replace(string) {
- val path = it.groups[1] ?: it.groups[2] ?: throw IllegalStateException("expected a group")
- val parts = path.value.split(".")
- var o: Any = params
- for (part in parts) {
- if (o is Map<*, *>) {
- o = o[part] ?: return@replace ""
+ return CONTEXT_VARIABLE_PATTERN.replace(string) {
+ val path = it.groups[1] ?: it.groups[2] ?: throw IllegalStateException("expected a group")
+ val parts = path.value.split(".")
+ var o: Any? = null
+ for ((index, part) in parts.withIndex()) {
+ if (index == 0) {
+ if (part == "context") {
+ o = contextParams
+ continue
} else {
- TODO("only maps are currently supported")
+ TODO("query context does not provide a `$part`")
}
}
- return@replace o.toString()
+ if (o is Map<*, *>) {
+ o = o[part] ?: return@replace ""
+ } else {
+ TODO("only maps are currently supported")
+ }
}
- } ?: string
+ return@replace o.toString()
+ }
}
fun getNextVariable(relationField: RelationField) = getNextVariable(
@@ -61,6 +67,8 @@ data class QueryContext @JvmOverloads constructor(
const val KEY = "Neo4jGraphQLQueryContext"
private const val PATH_PATTERN = "([a-zA-Z_][a-zA-Z_0-9]*(?:.[a-zA-Z_][a-zA-Z_0-9]*)*)"
+
+ // matches ${path} or $path
private val CONTEXT_VARIABLE_PATTERN = Regex("\\\$(?:\\{$PATH_PATTERN}|$PATH_PATTERN)")
}
}
diff --git a/core/src/main/kotlin/org/neo4j/graphql/domain/Node.kt b/core/src/main/kotlin/org/neo4j/graphql/domain/Node.kt
index ed0440e6..500df97a 100644
--- a/core/src/main/kotlin/org/neo4j/graphql/domain/Node.kt
+++ b/core/src/main/kotlin/org/neo4j/graphql/domain/Node.kt
@@ -37,7 +37,7 @@ class Node(
}
fun asCypherNode(queryContext: QueryContext?, name: String? = null) =
- Cypher.node(mainLabel, additionalLabels(queryContext)).let {
+ Cypher.node(mapLabelWithContext(mainLabel, queryContext), additionalLabels(queryContext)).let {
when {
name != null -> it.named(name)
else -> it
@@ -45,7 +45,7 @@ class Node(
}
fun asCypherNode(queryContext: QueryContext?, name: SymbolicName) =
- Cypher.node(mainLabel, additionalLabels(queryContext)).named(name)
+ Cypher.node(mapLabelWithContext(mainLabel, queryContext), additionalLabels(queryContext)).named(name)
}
diff --git a/core/src/main/kotlin/org/neo4j/graphql/domain/NodeFactory.kt b/core/src/main/kotlin/org/neo4j/graphql/domain/NodeFactory.kt
index 5288e801..a7e0a1d5 100644
--- a/core/src/main/kotlin/org/neo4j/graphql/domain/NodeFactory.kt
+++ b/core/src/main/kotlin/org/neo4j/graphql/domain/NodeFactory.kt
@@ -22,7 +22,7 @@ object NodeFactory {
val schemeDirectives =
typeDefinitionRegistry.schemaExtensionDefinitions?.map { it.directives }?.flatten() ?: emptyList()
val annotations = Annotations(schemeDirectives + definition.directives, typeDefinitionRegistry, definition.name)
- if (annotations.relationshipProperties != null) {
+ if (annotations.node == null || annotations.relationshipProperties != null) {
return null
}
val interfaces = definition.implements.mapNotNull { interfaceFactory(it.name()) }
diff --git a/core/src/main/kotlin/org/neo4j/graphql/domain/directives/RelationshipDirective.kt b/core/src/main/kotlin/org/neo4j/graphql/domain/directives/RelationshipDirective.kt
index a639a281..6ced401b 100644
--- a/core/src/main/kotlin/org/neo4j/graphql/domain/directives/RelationshipDirective.kt
+++ b/core/src/main/kotlin/org/neo4j/graphql/domain/directives/RelationshipDirective.kt
@@ -26,7 +26,7 @@ class RelationshipDirective private constructor(
val queryDirection =
directive.readArgument(RelationshipDirective::queryDirection) { RelationField.QueryDirection.valueOf((it as EnumValue).name) }
- ?: RelationField.QueryDirection.DEFAULT_DIRECTED
+ ?: RelationField.QueryDirection.DIRECTED
return RelationshipDirective(direction, type, properties, queryDirection)
}
diff --git a/core/src/main/kotlin/org/neo4j/graphql/domain/fields/PointField.kt b/core/src/main/kotlin/org/neo4j/graphql/domain/fields/PointField.kt
index cf2ed16f..3f134e88 100644
--- a/core/src/main/kotlin/org/neo4j/graphql/domain/fields/PointField.kt
+++ b/core/src/main/kotlin/org/neo4j/graphql/domain/fields/PointField.kt
@@ -31,12 +31,13 @@ class PointField(
override val predicateDefinitions: Map = initPredicates()
override val whereType
get() = when (coordinateType) {
- CoordinateType.GEOGRAPHIC -> POINT_INPUT_TYPE.asType()
- CoordinateType.CARTESIAN -> CARTESIAN_POINT_INPUT_TYPE.asType()
+ CoordinateType.GEOGRAPHIC -> TypeName(POINT_INPUT_TYPE)
+ CoordinateType.CARTESIAN -> TypeName(CARTESIAN_POINT_INPUT_TYPE)
}
private fun initPredicates(): Map {
val result = mutableMapOf()
+ .add(FieldOperator.IMPLICIT_EQUAL, deprecated = "Please use the explicit _EQ version")
.add(FieldOperator.EQUAL)
if (isList()) {
result.addIncludesResolver(FieldOperator.INCLUDES)
@@ -77,7 +78,7 @@ class PointField(
val paramPointArray = Cypher.listWith(p).`in`(parameter).returning(Cypher.point(p))
op.conditionCreator(property, paramPointArray)
},
- type = whereType.NonNull.List
+ type = whereType.makeRequired(type.isRequired()).List
)
}
@@ -88,7 +89,7 @@ class PointField(
val paramPoint = Cypher.point(parameter)
op.conditionCreator(property, paramPoint)
},
- type = whereType.inner()
+ type = whereType
)
}
diff --git a/core/src/main/kotlin/org/neo4j/graphql/domain/fields/RelationField.kt b/core/src/main/kotlin/org/neo4j/graphql/domain/fields/RelationField.kt
index f8c276b2..19cf030a 100644
--- a/core/src/main/kotlin/org/neo4j/graphql/domain/fields/RelationField.kt
+++ b/core/src/main/kotlin/org/neo4j/graphql/domain/fields/RelationField.kt
@@ -41,39 +41,22 @@ class RelationField(
}
enum class QueryDirection {
- DEFAULT_DIRECTED,
- DEFAULT_UNDIRECTED,
- DIRECTED_ONLY,
- UNDIRECTED_ONLY,
+ DIRECTED,
+ UNDIRECTED,
}
fun createQueryDslRelation(
start: Node,
end: Node,
- directed: Boolean?,
): Relationship {
- val useDirected = when (queryDirection) {
- QueryDirection.DEFAULT_DIRECTED -> directed ?: true
- QueryDirection.DEFAULT_UNDIRECTED -> directed ?: false
- QueryDirection.DIRECTED_ONLY -> {
- check(directed == null || directed == true, { "Invalid direction in 'DIRECTED_ONLY' relationship" })
- true
+ return when (queryDirection) {
+ QueryDirection.DIRECTED -> when (direction) {
+ Direction.IN -> end.relationshipTo(start, relationType)
+ Direction.OUT -> start.relationshipTo(end, relationType)
}
- QueryDirection.UNDIRECTED_ONLY -> {
- check(directed == null || directed == false, { "Invalid direction in 'UNDIRECTED_ONLY' relationship" })
- false
- }
- }
- if (useDirected) {
- return createDslRelation(start, end)
+ QueryDirection.UNDIRECTED -> start.relationshipBetween(end, relationType)
}
- return start.relationshipBetween(end, relationType)
}
- fun createDslRelation(start: Node, end: Node, name: String? = null): Relationship = when (direction) {
- Direction.IN -> end.relationshipTo(start, relationType)
- Direction.OUT -> start.relationshipTo(end, relationType)
- }.let { if (name != null) it.named(name) else it }
-
}
diff --git a/core/src/main/kotlin/org/neo4j/graphql/domain/fields/ScalarField.kt b/core/src/main/kotlin/org/neo4j/graphql/domain/fields/ScalarField.kt
index 16dbc972..30894a9b 100644
--- a/core/src/main/kotlin/org/neo4j/graphql/domain/fields/ScalarField.kt
+++ b/core/src/main/kotlin/org/neo4j/graphql/domain/fields/ScalarField.kt
@@ -37,6 +37,7 @@ abstract class ScalarField(
}
val result = mutableMapOf()
+ .add(FieldOperator.IMPLICIT_EQUAL, resolver, deprecated = "Please use the explicit _EQ version")
.add(FieldOperator.EQUAL, resolver)
if (fieldType == Constants.BOOLEAN) {
return result
diff --git a/core/src/main/kotlin/org/neo4j/graphql/domain/naming/ImplementingTypeNames.kt b/core/src/main/kotlin/org/neo4j/graphql/domain/naming/ImplementingTypeNames.kt
index 6f42c94e..2482a737 100644
--- a/core/src/main/kotlin/org/neo4j/graphql/domain/naming/ImplementingTypeNames.kt
+++ b/core/src/main/kotlin/org/neo4j/graphql/domain/naming/ImplementingTypeNames.kt
@@ -8,7 +8,6 @@ sealed class ImplementingTypeNames(
) : EntityNames(name, annotations) {
val connectOrCreateWhereInputTypeName get() = "${name}ConnectOrCreateWhere"
- val optionsInputTypeName get() = "${name}Options"
val sortInputTypeName get() = "${name}Sort"
override val rootTypeFieldNames get() = ImplementingTypeRootTypeFieldNames()
val rootTypeSelection get() = ImplementingTypeRootTypeSelection()
diff --git a/core/src/main/kotlin/org/neo4j/graphql/domain/predicates/FieldOperator.kt b/core/src/main/kotlin/org/neo4j/graphql/domain/predicates/FieldOperator.kt
index d5877dbc..7d6ad3f3 100644
--- a/core/src/main/kotlin/org/neo4j/graphql/domain/predicates/FieldOperator.kt
+++ b/core/src/main/kotlin/org/neo4j/graphql/domain/predicates/FieldOperator.kt
@@ -9,7 +9,11 @@ enum class FieldOperator(
val conditionCreator: (Expression, Expression) -> Condition,
val conditionEvaluator: (Any?, Any?) -> Boolean,
) {
- EQUAL("",
+ IMPLICIT_EQUAL("",
+ { lhs, rhs -> if (rhs == Cypher.literalNull()) lhs.isNull else lhs.eq(rhs) },
+ { lhs, rhs -> lhs == rhs }
+ ),
+ EQUAL("EQ",
{ lhs, rhs -> if (rhs == Cypher.literalNull()) lhs.isNull else lhs.eq(rhs) },
{ lhs, rhs -> lhs == rhs }
),
diff --git a/core/src/main/kotlin/org/neo4j/graphql/domain/predicates/RelationOperator.kt b/core/src/main/kotlin/org/neo4j/graphql/domain/predicates/RelationOperator.kt
index 9700ec21..64ef7259 100644
--- a/core/src/main/kotlin/org/neo4j/graphql/domain/predicates/RelationOperator.kt
+++ b/core/src/main/kotlin/org/neo4j/graphql/domain/predicates/RelationOperator.kt
@@ -14,7 +14,6 @@ enum class RelationOperator(
NONE("NONE", list = true, wrapInNotIfNeeded = { it.not() }),
SINGLE("SINGLE", list = true),
SOME("SOME", list = true),
- NOT_EQUAL("NOT", list = false, wrapInNotIfNeeded = { it.not() }),
EQUAL(null, list = false);
fun createRelationCondition(
diff --git a/core/src/main/kotlin/org/neo4j/graphql/handler/BaseDataFetcher.kt b/core/src/main/kotlin/org/neo4j/graphql/handler/BaseDataFetcher.kt
index e4518710..070fe42a 100644
--- a/core/src/main/kotlin/org/neo4j/graphql/handler/BaseDataFetcher.kt
+++ b/core/src/main/kotlin/org/neo4j/graphql/handler/BaseDataFetcher.kt
@@ -39,6 +39,12 @@ internal abstract class BaseDataFetcher(protected val schemaConfig: SchemaConfig
}
val result = neo4jAdapter.executeQuery(query, params)
+ return mapResult(env, result)
+ }
+
+ protected abstract fun generateCypher(env: DataFetchingEnvironment): Statement
+
+ protected open fun mapResult(env: DataFetchingEnvironment, result: List