@@ -32,39 +32,52 @@ class Translator(val schema: GraphQLSchema) {
3232 val ast = parse(query) // todo preparsedDocumentProvider
3333 val ctx = context.copy(fragments = ast.definitions.filterIsInstance<FragmentDefinition >().map { it.name to it }.toMap())
3434 val queries = ast.definitions.filterIsInstance<OperationDefinition >()
35- .filter { it.operation == OperationDefinition .Operation .QUERY } // todo variabledefinitions, directives, name
35+ .filter { it.operation == OperationDefinition .Operation .QUERY || it.operation == OperationDefinition . Operation . MUTATION } // todo variabledefinitions, directives, name
3636 .flatMap { it.selectionSet.selections }
3737 .filterIsInstance<Field >() // FragmentSpread, InlineFragment
3838 .map { toQuery(it, ctx).with (params) } // arguments, alias, directives, selectionSet
3939 return queries
4040 }
4141
42- private fun toQuery (queryField : Field , ctx : Context = Context ()): Cypher {
43- val name = queryField.name
44- val queryType = schema.queryType.fieldDefinitions.filter { it.name == name }.firstOrNull() ? : throw IllegalArgumentException (" Unknown Query $name available queries: " + schema.queryType.fieldDefinitions.map { it.name }.joinToString())
45- val returnType = queryType.type.inner()
42+ private fun toQuery (field : Field , ctx : Context = Context ()): Cypher {
43+ val name = field.name
44+ val queryType = schema.queryType.fieldDefinitions.filter { it.name == name }.firstOrNull()
45+ val mutationType = schema.mutationType.fieldDefinitions.filter { it.name == name }.firstOrNull()
46+ val fieldDefinition = queryType ? : mutationType
47+ ? : throw IllegalArgumentException (" Unknown Query $name available queries: " + schema.queryType.fieldDefinitions.map { it.name }.joinToString())
48+ val isQuery = queryType != null
49+ val returnType = fieldDefinition.type.inner()
4650// println(returnType)
4751 val type = schema.getType(returnType.name)
4852 val label = type.name.quote()
49- val variable = queryField .aliasOrName().decapitalize()
50- val cypherDirective = queryType .cypherDirective()
51- val mapProjection = projectFields(variable, queryField , type, ctx)
52- val skipLimit = format(skipLimit(queryField .arguments))
53- val ordering = orderBy(variable, queryField .arguments)
53+ val variable = field .aliasOrName().decapitalize()
54+ val cypherDirective = fieldDefinition .cypherDirective()
55+ val mapProjection = projectFields(variable, field , type, ctx)
56+ val skipLimit = format(skipLimit(field .arguments))
57+ val ordering = orderBy(variable, field .arguments)
5458 if (cypherDirective != null ) {
5559 // todo filters and such from nested fields
56- val (query, params) = cypherDirective(variable, queryType, queryField, cypherDirective, emptyList())
57- return Cypher (" UNWIND $query AS $variable RETURN ${mapProjection.query} AS $variable$ordering$skipLimit " ,
58- (params + mapProjection.params))
60+ return cypherQueryOrMutation(variable, fieldDefinition, field, cypherDirective, mapProjection, ordering, skipLimit, isQuery)
5961
6062 } else {
61- val where = if (ctx.topLevelWhere) where(variable, queryType , type, propertyArguments(queryField )) else Cypher .EMPTY
62- val properties = if (ctx.topLevelWhere) Cypher .EMPTY else properties(variable, queryType , propertyArguments(queryField ))
63+ val where = if (ctx.topLevelWhere) where(variable, fieldDefinition , type, propertyArguments(field )) else Cypher .EMPTY
64+ val properties = if (ctx.topLevelWhere) Cypher .EMPTY else properties(variable, fieldDefinition , propertyArguments(field ))
6365 return Cypher (" MATCH ($variable :$label${properties.query} )${where.query} RETURN ${mapProjection.query} AS $variable$ordering$skipLimit " ,
6466 (mapProjection.params + properties.params + where.params))
6567 }
6668 }
6769
70+ private fun cypherQueryOrMutation (variable : String , fieldDefinition : GraphQLFieldDefinition , field : Field , cypherDirective : Cypher , mapProjection : Cypher , ordering : String , skipLimit : String , isQuery : Boolean ) =
71+ if (isQuery) {
72+ val (query, params) = cypherDirective(variable, fieldDefinition, field, cypherDirective, emptyList())
73+ Cypher (" UNWIND $query AS $variable RETURN ${mapProjection.query} AS $variable$ordering$skipLimit " ,
74+ (params + mapProjection.params))
75+ } else {
76+ val (query, params) = cypherDirectiveQuery(variable, fieldDefinition, field, cypherDirective, emptyList())
77+ Cypher (" CALL apoc.cypher.doIt($query ) YIELD value WITH value[head(keys(value))] AS $variable RETURN ${mapProjection.query} AS $variable$ordering$skipLimit " ,
78+ (params + mapProjection.params))
79+ }
80+
6881 private fun propertyArguments (queryField : Field ) =
6982 queryField.arguments.filterNot { listOf (" first" , " offset" , " orderBy" ).contains(it.name) }
7083
@@ -162,12 +175,18 @@ class Translator(val schema: GraphQLSchema) {
162175 return Cypher (field.aliasOrName() + " :" + query, params)
163176 }
164177 private fun cypherDirective (variable : String , fieldDefinition : GraphQLFieldDefinition , field : Field , cypherDirective : Cypher , additionalArgs : List <CypherArgument >): Cypher {
178+ val suffix = if (fieldDefinition.type.isList()) " Many" else " Single"
179+ val (query, args) = cypherDirectiveQuery(variable, fieldDefinition, field, cypherDirective, additionalArgs)
180+ return Cypher (" apoc.cypher.runFirstColumn$suffix ($query )" , args)
181+ }
182+
183+ private fun cypherDirectiveQuery (variable : String , fieldDefinition : GraphQLFieldDefinition , field : Field , cypherDirective : Cypher , additionalArgs : List <CypherArgument >): Cypher {
165184 val suffix = if (fieldDefinition.type.isList()) " Many" else " Single"
166185 val args = additionalArgs + prepareFieldArguments(fieldDefinition, field.arguments)
167186 val argParams = args.map { ' $' + it.name + " AS " + it.name }.joinNonEmpty(" ," )
168187 val query = (if (argParams.isEmpty()) " " else " WITH $argParams " ) + cypherDirective.escapedQuery()
169188 val argString = (args.map { it.name + ' :' + if (it.name == " this" ) it.value else (' $' + paramName(variable, it.name, it.value)) }).joinToString(" ," , " {" , " }" )
170- return Cypher (" apoc.cypher.runFirstColumn $suffix ( '$query ',$argString ) " , args.filter { it.name != " this" }.associate { paramName(variable, it.name, it.value) to it.value })
189+ return Cypher (" '$query ',$argString " , args.filter { it.name != " this" }.associate { paramName(variable, it.name, it.value) to it.value })
171190 }
172191
173192 fun projectNamedFragments (variable : String , fragmentSpread : FragmentSpread , type : GraphQLObjectType , ctx : Context ) =
@@ -329,23 +348,29 @@ object SchemaBuilder {
329348
330349 val queryDefinition = operations.getOrElse(" query" ) {
331350 ObjectTypeDefinition (" Query" ).also {
332- schemaDefinition.operationTypeDefinitions.add(OperationTypeDefinition (" query" ,TypeName (it.name)))
351+ schemaDefinition.operationTypeDefinitions.add(OperationTypeDefinition (" query" , TypeName (it.name)))
333352 typeDefinitionRegistry.add(it)
334353 }
335354 }
336355 val mutationDefinition = operations.getOrElse(" mutation" ) {
337- ObjectTypeDefinition (" Mutation" ).also { schemaDefinition.operationTypeDefinitions.add(OperationTypeDefinition (" mutation" ,TypeName (it.name)))
338- typeDefinitionRegistry.add(it)
356+ ObjectTypeDefinition (" Mutation" ).also {
357+ schemaDefinition.operationTypeDefinitions.add(OperationTypeDefinition (" mutation" , TypeName (it.name)))
358+ typeDefinitionRegistry.add(it)
339359 }
340360 }
341-
342- augmentations.filter { it.query.isNotBlank() }.map { it.query }.let {
361+ // todo better filter
362+ augmentations
363+ .filter { it.query.isNotBlank() && queryDefinition.fieldDefinitions.none { fd -> it.query.startsWith(fd.name+ " (" ) } }
364+ .map { it.query }.let {
343365 if (! it.isEmpty()) {
344366 val newQueries = schemaParser.parse(" type AugmentedQuery { ${it.joinToString(" \n " )} }" ).getType(" AugmentedQuery" ).get() as ObjectTypeDefinition
345367 queryDefinition.fieldDefinitions.addAll(newQueries.fieldDefinitions)
346368 }
347369 }
348- augmentations.flatMap { listOf (it.create,it.update,it.delete).filter{ it.isNotBlank() }}.let {
370+ augmentations
371+ .flatMap { listOf (it.create,it.update,it.delete)
372+ .filter{ it.isNotBlank() && mutationDefinition.fieldDefinitions.none { fd -> it.startsWith(fd.name+ " (" ) } }}
373+ .let {
349374 if (! it.isEmpty()) {
350375 val newQueries = schemaParser.parse(" type AugmentedMutation { ${it.joinToString(" \n " )} }" ).getType(" AugmentedMutation" ).get() as ObjectTypeDefinition
351376 mutationDefinition.fieldDefinitions.addAll(newQueries.fieldDefinitions)
0 commit comments