Skip to content

Commit f8e26b3

Browse files
authored
Fix for relationship mutations are not using the correct direction (resolves #98) (#107)
This bugfix solves the problem that the mapping was wrong for incoming relations
1 parent 9602a55 commit f8e26b3

File tree

7 files changed

+344
-22
lines changed

7 files changed

+344
-22
lines changed

src/main/kotlin/org/neo4j/graphql/GraphQLExtensions.kt

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,13 @@ fun GraphQLFieldsContainer.relationshipFor(name: String): RelationshipInfo? {
4949
?: throw IllegalArgumentException("$name is not defined on ${this.name}")
5050
val fieldObjectType = field.type.inner() as? GraphQLFieldsContainer ?: return null
5151

52-
val (relDirective, isRelFromType) = if (isRelationType()) {
52+
val (relDirective, inverse) = if (isRelationType()) {
53+
val typeName = this.name
5354
(this as? GraphQLDirectiveContainer)
54-
?.getDirective(DirectiveConstants.RELATION)?.let { it to false }
55+
?.getDirective(DirectiveConstants.RELATION)?.let {
56+
// do inverse mapping, if the current type is the `to` mapping of the relation
57+
it to (fieldObjectType.getFieldDefinition(it.getArgument(RELATION_TO, null))?.name == typeName)
58+
}
5559
?: throw IllegalStateException("Type ${this.name} needs an @relation directive")
5660
} else {
5761
(fieldObjectType as? GraphQLDirectiveContainer)
@@ -60,11 +64,8 @@ fun GraphQLFieldsContainer.relationshipFor(name: String): RelationshipInfo? {
6064
?: throw IllegalStateException("Field $field needs an @relation directive")
6165
}
6266

63-
// TODO direction is depending on source/target type
64-
65-
val relInfo = relDetails(fieldObjectType) { argName, defaultValue -> relDirective.getArgument(argName, defaultValue) }
67+
val relInfo = relDetails(fieldObjectType, relDirective)
6668

67-
val inverse = isRelFromType && fieldObjectType.getFieldDefinition(relInfo.startField)?.name != this.name
6869
return if (inverse) relInfo.copy(out = relInfo.out?.let { !it }, startField = relInfo.endField, endField = relInfo.startField) else relInfo
6970
}
7071

@@ -121,21 +122,19 @@ fun GraphQLType.ref(): GraphQLType = when (this) {
121122
else -> GraphQLTypeReference(name)
122123
}
123124

124-
fun relDetails(type: GraphQLFieldsContainer,
125-
// TODO simplify uasage (no more callback)
126-
directiveResolver: (name: String, defaultValue: String?) -> String?): RelationshipInfo {
127-
val relType = directiveResolver(RELATION_NAME, "")!!
128-
val outgoing = when (directiveResolver(RELATION_DIRECTION, null)) {
125+
fun relDetails(type: GraphQLFieldsContainer, relDirective: GraphQLDirective): RelationshipInfo {
126+
val relType = relDirective.getArgument(RELATION_NAME, "")!!
127+
val outgoing = when (relDirective.getArgument<String>(RELATION_DIRECTION, null)) {
129128
RELATION_DIRECTION_IN -> false
130129
RELATION_DIRECTION_BOTH -> null
131130
RELATION_DIRECTION_OUT -> true
132-
else -> throw IllegalStateException("Unknown direction ${directiveResolver(RELATION_DIRECTION, null)}")
131+
else -> throw IllegalStateException("Unknown direction ${relDirective.getArgument<String>(RELATION_DIRECTION, null)}")
133132
}
134133
return RelationshipInfo(type,
135134
relType,
136135
outgoing,
137-
directiveResolver(RELATION_FROM, null),
138-
directiveResolver(RELATION_TO, null))
136+
relDirective.getArgument<String>(RELATION_FROM, null),
137+
relDirective.getArgument<String>(RELATION_TO, null))
139138
}
140139

141140
data class RelationshipInfo(

src/main/kotlin/org/neo4j/graphql/handler/projection/ProjectionBase.kt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -344,9 +344,6 @@ open class ProjectionBase {
344344
return Cypher(comprehension + slice.query, (where.params + fieldProjection.params + slice.params))
345345
}
346346

347-
private fun relDetails(type: GraphQLFieldsContainer, relDirective: GraphQLDirective) =
348-
relDetails(type) { name, defaultValue -> relDirective.getArgument(name, defaultValue) }
349-
350347
class SkipLimit(variable: String,
351348
arguments: List<Argument>,
352349
private val skip: Translator.CypherArgument? = convertArgument(variable, arguments, OFFSET),
@@ -395,4 +392,4 @@ open class ProjectionBase {
395392
}
396393
}
397394
}
398-
}
395+
}

src/main/kotlin/org/neo4j/graphql/handler/relation/CreateRelationHandler.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ class CreateRelationHandler private constructor(
6868

6969
return Cypher("MATCH ${startSelect.query}" +
7070
" MATCH ${endSelect.query}" +
71-
" MERGE (${relation.startField})-[:${relation.relType.quote()}${properties.query}]->(${relation.endField})" +
71+
" MERGE (${relation.startField})${relation.arrows.first}-[:${relation.relType.quote()}${properties.query}]-${relation.arrows.second}(${relation.endField})" +
7272
" WITH DISTINCT ${relation.startField} AS $variable" +
7373
" RETURN ${mapProjection.query} AS $variable",
7474
startSelect.params + endSelect.params + properties.params)

src/main/kotlin/org/neo4j/graphql/handler/relation/DeleteRelationHandler.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class DeleteRelationHandler private constructor(
5151

5252
return Cypher("MATCH ${startSelect.query}" +
5353
" MATCH ${endSelect.query}" +
54-
" MATCH (${relation.startField})-[r:${relation.relType.quote()}]->(${relation.endField})" +
54+
" MATCH (${relation.startField})${relation.arrows.first}-[r:${relation.relType.quote()}]-${relation.arrows.second}(${relation.endField})" +
5555
" DELETE r" +
5656
" WITH DISTINCT ${relation.startField} AS $variable" +
5757
" RETURN ${mapProjection.query} AS $variable",

src/test/kotlin/org/neo4j/graphql/CypherTests.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ class CypherTests {
1414
@TestFactory
1515
fun `filter-tests`() = CypherTestSuite("filter-tests.adoc").run()
1616

17+
@TestFactory
18+
fun `relationship-tests`() = CypherTestSuite("relationship-tests.adoc").run()
19+
1720
@TestFactory
1821
fun `movie-tests`() = CypherTestSuite("movie-tests.adoc").run()
1922

src/test/resources/movie-tests.adoc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,7 +1008,7 @@ mutation {
10081008
----
10091009
MATCH (from:Genre { name: $fromName })
10101010
MATCH (to:Movie) WHERE to.movieId IN $toMovies
1011-
MERGE (from)-[:IN_GENRE]->(to)
1011+
MERGE (from)<-[:IN_GENRE]-(to)
10121012
WITH DISTINCT from AS addGenreMovies
10131013
RETURN addGenreMovies { .name } AS addGenreMovies
10141014
----
@@ -1239,4 +1239,4 @@ CREATE (actor:Actor:Person {
12391239
})
12401240
WITH actor
12411241
RETURN actor { .name,born: { year: actor.born.year, month: actor.born.month } } AS actor
1242-
----
1242+
----

0 commit comments

Comments
 (0)