Skip to content

Commit

Permalink
Augment filter argument for nested fields
Browse files Browse the repository at this point in the history
If the field of GraphQL Type maps to an *..n relation (a list) and there is currently no `filter`-argument defined, we now generate it in the augmented schema.
The logic for handling these nested filters is already implementd, so no further code-adjustments are required.

resolves #129
  • Loading branch information
Andy2003 committed Apr 14, 2021
1 parent fc52604 commit 6e557a2
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 16 deletions.
29 changes: 18 additions & 11 deletions core/src/main/kotlin/org/neo4j/graphql/SchemaBuilder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ object SchemaBuilder {

val handler = getHandler(config)

var targetSchema = augmentSchema(sourceSchema, handler)
var targetSchema = augmentSchema(sourceSchema, handler, config)
targetSchema = addDataFetcher(targetSchema, dataFetchingInterceptor, handler)
return targetSchema
}
Expand Down Expand Up @@ -128,7 +128,7 @@ object SchemaBuilder {
return handler
}

private fun augmentSchema(sourceSchema: GraphQLSchema, handler: List<AugmentationHandler>): GraphQLSchema {
private fun augmentSchema(sourceSchema: GraphQLSchema, handler: List<AugmentationHandler>, schemaConfig: SchemaConfig): GraphQLSchema {
val types = sourceSchema.typeMap.toMutableMap()
val env = BuildingEnv(types, sourceSchema)
val queryTypeName = sourceSchema.queryTypeName()
Expand Down Expand Up @@ -157,11 +157,11 @@ object SchemaBuilder {
builder.clearFields().clearInterfaces()
// to prevent duplicated types in schema
sourceType.interfaces.forEach { builder.withInterface(GraphQLTypeReference(it.name)) }
sourceType.fieldDefinitions.forEach { f -> builder.field(enhanceRelations(f, env)) }
sourceType.fieldDefinitions.forEach { f -> builder.field(enhanceRelations(f, env, schemaConfig)) }
}
sourceType is GraphQLInterfaceType -> sourceType.transform { builder ->
builder.clearFields()
sourceType.fieldDefinitions.forEach { f -> builder.field(enhanceRelations(f, env)) }
sourceType.fieldDefinitions.forEach { f -> builder.field(enhanceRelations(f, env, schemaConfig)) }
}
else -> sourceType
}
Expand All @@ -177,7 +177,7 @@ object SchemaBuilder {
.build()
}

private fun enhanceRelations(fd: GraphQLFieldDefinition, env: BuildingEnv): GraphQLFieldDefinition {
private fun enhanceRelations(fd: GraphQLFieldDefinition, env: BuildingEnv, schemaConfig: SchemaConfig): GraphQLFieldDefinition {
return fd.transform { fieldBuilder ->
// to prevent duplicated types in schema
fieldBuilder.type(fd.type.ref() as GraphQLOutputType)
Expand All @@ -192,14 +192,21 @@ object SchemaBuilder {
if (fd.getArgument(ProjectionBase.OFFSET) == null) {
fieldBuilder.argument { a -> a.name(ProjectionBase.OFFSET).type(Scalars.GraphQLInt) }
}
if (fd.getArgument(ProjectionBase.ORDER_BY) == null && fd.type.isList()) {
(fd.type.inner() as? GraphQLFieldsContainer)?.let { fieldType ->
env.addOrdering(fieldType)?.let { orderingTypeName ->
val orderType = GraphQLList(GraphQLNonNull(GraphQLTypeReference(orderingTypeName)))
fieldBuilder.argument { a -> a.name(ProjectionBase.ORDER_BY).type(orderType) }
}

val fieldType = fd.type.inner() as? GraphQLFieldsContainer ?: return@transform

if (fd.getArgument(ProjectionBase.ORDER_BY) == null) {
env.addOrdering(fieldType)?.let { orderingTypeName ->
val orderType = GraphQLList(GraphQLNonNull(GraphQLTypeReference(orderingTypeName)))
fieldBuilder.argument { a -> a.name(ProjectionBase.ORDER_BY).type(orderType) }

}
}

if (schemaConfig.query.enabled && !schemaConfig.query.exclude.contains(fieldType.name) && fd.getArgument(ProjectionBase.FILTER) == null) {
val filterTypeName = env.addFilterType(fieldType)
fieldBuilder.argument(input(ProjectionBase.FILTER, GraphQLTypeReference(filterTypeName)))
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions core/src/test/resources/augmentation-tests.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ schema {
}
interface HasMovies {
movies(first: Int, offset: Int, orderBy: [_MovieOrdering!]): [Movie]
movies(filter: _MovieFilter, first: Int, offset: Int, orderBy: [_MovieOrdering!]): [Movie]
}
type Knows0 @relation(direction : OUT, from : "source", name : "KNOWS", to : "knows") {
Expand Down Expand Up @@ -454,7 +454,7 @@ type Person4 {
type Person5 implements HasMovies {
id: ID!
movies(first: Int, offset: Int, orderBy: [_MovieOrdering!]): [Movie] @relation(direction : OUT, from : "from", name : "LIKES", to : "to")
movies(filter: _MovieFilter, first: Int, offset: Int, orderBy: [_MovieOrdering!]): [Movie] @relation(direction : OUT, from : "from", name : "LIKES", to : "to")
}
type Publisher {
Expand Down
6 changes: 3 additions & 3 deletions core/src/test/resources/issues/gh-170.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ CREATE
[source,graphql]
----
query {
r: rated( rating_gte : 3) {
r: rated( filter: {rating_gte : 3}) {
rating
movie {
title
Expand All @@ -55,7 +55,7 @@ query {
[source,json]
----
{
"rRatingGte" : 3
"filterRRatingGte" : 3
}
----

Expand All @@ -74,7 +74,7 @@ query {
[source,cypher]
----
MATCH ()-[r:RATED]->()
WHERE r.rating >= $rRatingGte
WHERE r.rating >= $filterRRatingGte
RETURN r {
.rating,
movie: head([()-[r]->(movie:Movie) | movie {
Expand Down

0 comments on commit 6e557a2

Please sign in to comment.