Skip to content

Commit 71faad7

Browse files
committed
add integration tests
1 parent d830527 commit 71faad7

File tree

394 files changed

+56542
-33171
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

394 files changed

+56542
-33171
lines changed

core/pom.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@
2626
<version>2.0.0-SNAPSHOT</version>
2727
<scope>test</scope>
2828
</dependency>
29+
<dependency>
30+
<groupId>org.threeten</groupId>
31+
<artifactId>threeten-extra</artifactId>
32+
<version>1.7.0</version>
33+
</dependency>
2934
<dependency>
3035
<groupId>org.neo4j.driver</groupId>
3136
<artifactId>neo4j-java-driver</artifactId>
@@ -122,6 +127,13 @@
122127
<version>2.17.2</version>
123128
<scope>test</scope>
124129
</dependency>
130+
<dependency>
131+
<groupId>org.apache.commons</groupId>
132+
<artifactId>commons-csv</artifactId>
133+
<version>1.12.0</version>
134+
<scope>test</scope>
135+
</dependency>
136+
125137
</dependencies>
126138

127139
<dependencyManagement>

core/src/main/kotlin/org/neo4j/graphql/ExtensionFunctions.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ fun String.toLowerCase(): String = lowercase(Locale.getDefault())
3030

3131
infix fun Condition?.and(rhs: Condition) = this?.and(rhs) ?: rhs
3232
infix fun Condition?.or(rhs: Condition) = this?.or(rhs) ?: rhs
33+
infix fun Condition?.xor(rhs: Condition) = this?.xor(rhs) ?: rhs
34+
3335
fun Collection<Condition?>.foldWithAnd(): Condition? = this
3436
.filterNotNull()
3537
.takeIf { it.isNotEmpty() }
@@ -169,3 +171,4 @@ fun Iterable<Any?>.toDict(): List<Dict> = this.mapNotNull { Dict.create(it) }
169171

170172
fun String.toDeprecatedDirective() = Directive("deprecated", listOf(Argument("reason", StringValue(this))))
171173

174+
fun Collection<Statement>.union(): Statement = if (this.size == 1) this.first() else Cypher.union(this)

core/src/main/kotlin/org/neo4j/graphql/SchemaBuilder.kt

Lines changed: 52 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -38,61 +38,45 @@ import org.neo4j.graphql.schema.model.outputs.NodeSelection
3838
*/
3939
class SchemaBuilder @JvmOverloads constructor(
4040
val typeDefinitionRegistry: TypeDefinitionRegistry,
41-
val schemaConfig: SchemaConfig = SchemaConfig()
41+
val schemaConfig: SchemaConfig = SchemaConfig(),
4242
) {
4343

4444
companion object {
45-
/**
46-
* @param sdl the schema to augment
47-
* @param neo4jAdapter the adapter to run the generated cypher queries
48-
* @param config defines how the schema should get augmented
49-
*/
45+
5046
@JvmStatic
5147
@JvmOverloads
52-
fun buildSchema(
53-
sdl: String,
54-
config: SchemaConfig = SchemaConfig(),
55-
neo4jAdapter: Neo4jAdapter = Neo4jAdapter.NO_OP,
56-
addLibraryDirectivesToSchema: Boolean = true,
57-
): GraphQLSchema {
48+
fun fromSchema(sdl: String, config: SchemaConfig = SchemaConfig()): SchemaBuilder {
5849
val schemaParser = SchemaParser()
5950
val typeDefinitionRegistry = schemaParser.parse(sdl)
60-
return buildSchema(typeDefinitionRegistry, config, neo4jAdapter, addLibraryDirectivesToSchema)
51+
return SchemaBuilder(typeDefinitionRegistry, config)
6152
}
6253

6354
/**
64-
* @param typeDefinitionRegistry a registry containing all the types, that should be augmented
65-
* @param config defines how the schema should get augmented
55+
* @param sdl the schema to augment
6656
* @param neo4jAdapter the adapter to run the generated cypher queries
57+
* @param config defines how the schema should get augmented
6758
*/
6859
@JvmStatic
6960
@JvmOverloads
7061
fun buildSchema(
71-
typeDefinitionRegistry: TypeDefinitionRegistry,
62+
sdl: String,
7263
config: SchemaConfig = SchemaConfig(),
73-
neo4jAdapter: Neo4jAdapter,
64+
neo4jAdapter: Neo4jAdapter = Neo4jAdapter.NO_OP,
7465
addLibraryDirectivesToSchema: Boolean = true,
75-
): GraphQLSchema {
76-
77-
val builder = RuntimeWiring.newRuntimeWiring()
78-
val codeRegistryBuilder = GraphQLCodeRegistry.newCodeRegistry()
79-
val schemaBuilder = SchemaBuilder(typeDefinitionRegistry, config)
80-
schemaBuilder.augmentTypes(addLibraryDirectivesToSchema)
81-
schemaBuilder.registerScalars(builder)
82-
schemaBuilder.registerTypeNameResolver(builder)
83-
schemaBuilder.registerNeo4jAdapter(codeRegistryBuilder, neo4jAdapter)
84-
85-
return SchemaGenerator().makeExecutableSchema(
86-
typeDefinitionRegistry,
87-
builder.codeRegistry(codeRegistryBuilder).build()
88-
)
89-
}
66+
): GraphQLSchema = fromSchema(sdl, config)
67+
.withNeo4jAdapter(neo4jAdapter)
68+
.addLibraryDirectivesToSchema(addLibraryDirectivesToSchema)
69+
.build()
9070
}
9171

9272
private val handler: List<AugmentationHandler>
9373
private val neo4jTypeDefinitionRegistry: TypeDefinitionRegistry = getNeo4jEnhancements()
9474
private val augmentedFields = mutableListOf<AugmentationHandler.AugmentedField>()
9575
private val ctx = AugmentationContext(schemaConfig, typeDefinitionRegistry)
76+
private var addLibraryDirectivesToSchema: Boolean = false;
77+
private var codeRegistryBuilder: GraphQLCodeRegistry.Builder? = null
78+
private var runtimeWiringBuilder: RuntimeWiring.Builder? = null
79+
private var neo4jAdapter: Neo4jAdapter = Neo4jAdapter.NO_OP
9680

9781
init {
9882
handler = mutableListOf(
@@ -102,6 +86,42 @@ class SchemaBuilder @JvmOverloads constructor(
10286
)
10387
}
10488

89+
fun addLibraryDirectivesToSchema(addLibraryDirectivesToSchema: Boolean): SchemaBuilder {
90+
this.addLibraryDirectivesToSchema = addLibraryDirectivesToSchema
91+
return this
92+
}
93+
94+
fun withCodeRegistryBuilder(codeRegistryBuilder: GraphQLCodeRegistry.Builder): SchemaBuilder {
95+
this.codeRegistryBuilder = codeRegistryBuilder
96+
return this
97+
}
98+
99+
fun withRuntimeWiringBuilder(runtimeWiring: RuntimeWiring.Builder): SchemaBuilder {
100+
this.runtimeWiringBuilder = runtimeWiring
101+
return this
102+
}
103+
104+
fun withNeo4jAdapter(neo4jAdapter: Neo4jAdapter): SchemaBuilder {
105+
this.neo4jAdapter = neo4jAdapter
106+
return this
107+
}
108+
109+
fun build(): GraphQLSchema {
110+
augmentTypes(addLibraryDirectivesToSchema)
111+
val runtimeWiringBuilder = this.runtimeWiringBuilder ?: RuntimeWiring.newRuntimeWiring()
112+
registerScalars(runtimeWiringBuilder)
113+
registerTypeNameResolver(runtimeWiringBuilder)
114+
115+
val codeRegistryBuilder = this.codeRegistryBuilder ?: GraphQLCodeRegistry.newCodeRegistry()
116+
registerNeo4jAdapter(codeRegistryBuilder, neo4jAdapter)
117+
118+
return SchemaGenerator().makeExecutableSchema(
119+
typeDefinitionRegistry,
120+
runtimeWiringBuilder.codeRegistry(codeRegistryBuilder).build()
121+
)
122+
123+
}
124+
105125

106126
/**
107127
* Generated additionally query and mutation fields according to the types present in the [typeDefinitionRegistry].

core/src/main/kotlin/org/neo4j/graphql/scalars/DurationScalar.kt

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,17 @@ import graphql.execution.CoercedVariables
66
import graphql.language.*
77
import graphql.schema.*
88
import org.neo4j.graphql.Constants
9-
import java.time.Duration
10-
import java.time.Period
11-
import java.time.format.DateTimeParseException
12-
import java.time.temporal.TemporalAmount
9+
import org.threeten.extra.PeriodDuration
1310
import java.util.*
1411

1512
object DurationScalar {
1613

1714
val INSTANCE: GraphQLScalarType = GraphQLScalarType.newScalar()
1815
.name(Constants.DURATION)
19-
.coercing(object : Coercing<TemporalAmount, String> {
16+
.coercing(object : Coercing<PeriodDuration, String> {
2017

21-
private fun parse(value: String): TemporalAmount {
22-
try {
23-
return Duration.parse(value)
24-
} catch (e: DateTimeParseException){
25-
return Period.parse(value)
26-
}
18+
private fun parse(value: String): PeriodDuration {
19+
return PeriodDuration.parse(value)
2720
}
2821

2922
@Throws(CoercingSerializeException::class)
@@ -32,7 +25,7 @@ object DurationScalar {
3225
}
3326

3427
@Throws(CoercingParseValueException::class)
35-
override fun parseValue(input: Any, graphQLContext: GraphQLContext, locale: Locale): TemporalAmount? {
28+
override fun parseValue(input: Any, graphQLContext: GraphQLContext, locale: Locale): PeriodDuration? {
3629
return when (input) {
3730
is StringValue -> parse(input.value)
3831
is String -> parse(input)
@@ -46,7 +39,7 @@ object DurationScalar {
4639
variables: CoercedVariables,
4740
graphQLContext: GraphQLContext,
4841
locale: Locale
49-
): TemporalAmount? {
42+
): PeriodDuration? {
5043
return parseValue(input, graphQLContext, locale)
5144
}
5245
})

core/src/main/kotlin/org/neo4j/graphql/schema/model/outputs/PageInfoSelection.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package org.neo4j.graphql.schema.model.outputs
22

33
import org.neo4j.graphql.Constants
44
import org.neo4j.graphql.NonNull
5+
import org.neo4j.graphql.asDescription
56
import org.neo4j.graphql.schema.AugmentationBase
67
import org.neo4j.graphql.schema.AugmentationContext
78
import org.neo4j.graphql.utils.IResolveTree
@@ -18,7 +19,9 @@ class PageInfoSelection(
1819
object Augmentation : AugmentationBase {
1920

2021
fun generateNodeSelection(ctx: AugmentationContext) =
21-
ctx.getOrCreateObjectType(TYPE_NAME) { fields, _ ->
22+
ctx.getOrCreateObjectType(
23+
TYPE_NAME,
24+
{ description("Pagination information (Relay)".asDescription()) }) { fields, _ ->
2225
fields += field(PageInfoSelection::startCursor, Constants.Types.String)
2326
fields += field(PageInfoSelection::endCursor, Constants.Types.String)
2427
fields += field(PageInfoSelection::hasNextPage, Constants.Types.Boolean.NonNull)

core/src/main/kotlin/org/neo4j/graphql/translate/ProjectionTranslator.kt

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import org.neo4j.graphql.translate.projection.createInterfaceProjectionAndParams
1616
import org.neo4j.graphql.translate.projection.projectScalarField
1717
import org.neo4j.graphql.translate.where.createWhere
1818
import org.neo4j.graphql.utils.ResolveTree
19+
import org.neo4j.graphql.utils.SelectionOfType
1920

2021
class ProjectionTranslator {
2122

@@ -41,19 +42,27 @@ class ProjectionTranslator {
4142
}
4243

4344
var selectedFields = resolveTree?.fieldsByTypeName?.get(node.name)
44-
?: return Projection(projection = projections)
4545

46-
selectedFields.values.forEach { field ->
47-
val nodeField = node.getField(field.name) as? ComputedField ?: return@forEach
48-
nodeField.annotations.customResolver?.requires?.run {
49-
selectedFields = selectedFields.merge(this)
46+
for (it in node.interfaces) {
47+
val interfaceSelection = resolveTree?.fieldsByTypeName?.get(it.name)
48+
if (selectedFields == null) {
49+
selectedFields = interfaceSelection
50+
} else if (interfaceSelection != null) {
51+
selectedFields = selectedFields.merge(interfaceSelection)
5052
}
5153
}
54+
var requiredSelection: SelectionOfType = selectedFields ?: return Projection(projection = projections)
55+
56+
requiredSelection.values.forEach { field ->
57+
val requirements = (node.getField(field.name) as? ComputedField)?.annotations?.customResolver?.requires
58+
?: return@forEach
59+
requiredSelection = requiredSelection.merge(requirements)
60+
}
5261

5362
val subQueries = mutableListOf<Statement>()
5463
val subQueriesBeforeSort = mutableListOf<Statement>()
5564

56-
selectedFields.values.forEach { field ->
65+
requiredSelection.values.forEach { field ->
5766
val alias = field.aliasOrName
5867
val nodeField = node.getField(field.name) ?: return@forEach
5968

@@ -72,8 +81,13 @@ class ProjectionTranslator {
7281
projections += returnVariable
7382

7483
} else if (nodeField.isUnion) {
84+
val unionWhere = arguments.where as? WhereInput.UnionWhereInput
7585

76-
val referenceNodes = requireNotNull(nodeField.union).nodes.values
86+
var referenceNodes = requireNotNull(nodeField.union).nodes.values
87+
if (unionWhere != null) {
88+
referenceNodes =
89+
referenceNodes.filter { !unionWhere.getDataForNode(it)?.predicates.isNullOrEmpty() }
90+
}
7791
val aliasVar = queryContext.getNextVariable(alias)
7892

7993
val unionSubQueries = referenceNodes.map { refNode ->
@@ -114,7 +128,7 @@ class ProjectionTranslator {
114128
}
115129

116130
subQueries += Cypher.with(varName)
117-
.call(Cypher.union(unionSubQueries))
131+
.call(unionSubQueries.union())
118132
.with(aliasVar) // TODO remove
119133
.applySortingSkipAndLimit(aliasVar, arguments.options, queryContext)
120134
.returning(Cypher.collect(aliasVar).`as`(aliasVar))

core/src/main/kotlin/org/neo4j/graphql/translate/connection_clause/CreateConnectionClause.kt

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ fun createConnectionClause(
3939
},
4040
onInterface = {
4141
createConnectionClauseForMultipleNodes(
42-
it.implementations.values, arguments,
42+
it.implementations.values,
43+
arguments,
4344
resolveTree,
4445
field,
4546
context,
@@ -48,9 +49,15 @@ fun createConnectionClause(
4849
returnVariable,
4950
)
5051
},
51-
onUnion = {
52+
onUnion = { union ->
53+
val unionConnectionWhere = arguments.where as? ConnectionWhere.UnionConnectionWhere
54+
var nodes = union.nodes.values
55+
if (unionConnectionWhere != null) {
56+
nodes = nodes.filter { !unionConnectionWhere.getDataForNode(it)?.predicates.isNullOrEmpty() }
57+
}
5258
createConnectionClauseForMultipleNodes(
53-
it.nodes.values, arguments,
59+
nodes,
60+
arguments,
5461
resolveTree,
5562
field,
5663
context,
@@ -98,7 +105,7 @@ private fun createConnectionClauseForMultipleNodes(
98105
var targetEdges = edges
99106
return Cypher
100107
.with(nodeVariable)
101-
.call(Cypher.union(subQueries))
108+
.call(subQueries.union())
102109
.with(Cypher.collect(collectUnionVariable).`as`(edges))
103110
.with(edges, Cypher.size(edges).`as`(totalCount))
104111
.let {

core/src/main/kotlin/org/neo4j/graphql/translate/where/CreateWhere.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import org.neo4j.graphql.domain.fields.HasCoalesceValue
99
import org.neo4j.graphql.domain.fields.RelationField
1010
import org.neo4j.graphql.domain.predicates.ConnectionFieldPredicate
1111
import org.neo4j.graphql.domain.predicates.RelationFieldPredicate
12+
import org.neo4j.graphql.domain.predicates.RelationOperator
1213
import org.neo4j.graphql.domain.predicates.ScalarFieldPredicate
1314
import org.neo4j.graphql.schema.model.inputs.WhereInput
1415
import org.neo4j.graphql.schema.model.inputs.connection.ConnectionWhere
@@ -71,9 +72,15 @@ fun createWhere(
7172
if (field is RelationField) {
7273
val relation = field.createDslRelation(propertyContainer, endNode)
7374
val cond = op.createRelationCondition(relation, nestedCondition)
74-
allConditions = allConditions and cond.let {
75+
76+
val condition = cond.let {
7577
if (predicate.where == null) it.not() else it
7678
}
79+
allConditions = when (op) {
80+
RelationOperator.SOME -> allConditions or condition
81+
RelationOperator.SINGLE -> allConditions xor condition
82+
else -> allConditions and condition
83+
}
7784
} else {
7885
TODO()
7986
}

0 commit comments

Comments
 (0)