Skip to content

Commit

Permalink
fix compact rendering of queries
Browse files Browse the repository at this point in the history
  • Loading branch information
paulpdaniels committed Aug 20, 2023
1 parent ba46d74 commit 2ee4d21
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 41 deletions.
45 changes: 45 additions & 0 deletions core/src/main/scala/caliban/execution/Field.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import caliban.introspection.adt.__Type
import caliban.parsing.SourceMapper
import caliban.parsing.adt.Definition.ExecutableDefinition.FragmentDefinition
import caliban.parsing.adt.Selection.{ Field => F, FragmentSpread, InlineFragment }
import caliban.parsing.adt.Type.NamedType
import caliban.parsing.adt.{ Directive, LocationInfo, Selection, VariableDefinition }
import caliban.schema.{ RootType, Types }
import caliban.{ InputValue, Value }
Expand Down Expand Up @@ -58,6 +59,50 @@ case class Field(
case (None, None) => None
}
)

def toSelection: Selection = {
def loop(f: Field): Selection = {
// Not pretty, but it avoids computing the hashmap if it isn't needed
var map: mutable.Map[String, List[Selection]] =
null.asInstanceOf[mutable.Map[String, List[Selection]]]

val children = f.fields.flatMap { child =>
val childSelection = loop(child)
child.targets match {
case Some(targets) =>
targets.foreach { target =>
if (map eq null) map = mutable.LinkedHashMap.empty
map.update(target, childSelection :: map.getOrElse(target, Nil))
}
None
case None =>
Some(childSelection)
}
}

val inlineFragments =
if (map eq null) Nil
else
map.map { case (name, selections) =>
Selection.InlineFragment(
Some(NamedType(name, nonNull = false)),
Nil,
selections
)
}

Selection.Field(
f.alias,
f.name,
f.arguments,
f.directives,
children ++ inlineFragments,
0
)
}

loop(this)
}
}

object Field {
Expand Down
40 changes: 18 additions & 22 deletions core/src/main/scala/caliban/rendering/DocumentRenderer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ object DocumentRenderer extends Renderer[Document] {
private[caliban] lazy val documentRenderer: Renderer[Document] = Renderer.combine(
directiveDefinitionRenderer.list(Renderer.char('\n')).contramap(_.directiveDefinitions),
schemaRenderer.optional.contramap(_.schemaDefinition),
operationDefinitionRenderer.list.contramap(_.operationDefinitions),
operationDefinitionRenderer.list(Renderer.newlineOrSpace).contramap(_.operationDefinitions),
typeDefinitionsRenderer.contramap(_.typeDefinitions),
fragmentRenderer.list.contramap(_.fragmentDefinitions)
)
Expand Down Expand Up @@ -135,15 +135,14 @@ object DocumentRenderer extends Renderer[Document] {
override def unsafeRender(definition: OperationDefinition, indent: Option[Int], writer: StringBuilder): Unit =
definition match {
case OperationDefinition(operationType, name, variableDefinitions, directives, selectionSet) =>
newlineOrSpace(indent, writer)
operationTypeRenderer.unsafeRender(operationType, indent, writer)
name.foreach { n =>
writer += ' '
writer ++= n
}
variableDefinitionsRenderer.unsafeRender(variableDefinitions, indent, writer)
directivesRenderer.unsafeRender(directives, indent, writer)
selectionRenderer.unsafeRender(selectionSet, indent, writer)
selectionsRenderer.unsafeRender(selectionSet, indent, writer)
}
}

Expand Down Expand Up @@ -179,19 +178,25 @@ object DocumentRenderer extends Renderer[Document] {
}
}

private[caliban] lazy val selectionRenderer: Renderer[List[Selection]] = new Renderer[List[Selection]] {
private[caliban] lazy val selectionsRenderer: Renderer[List[Selection]] = new Renderer[List[Selection]] {
private val inner = selectionRenderer.list(Renderer.newlineOrSpace)

override def unsafeRender(selections: List[Selection], indent: Option[Int], builder: StringBuilder): Unit = {
space(indent, builder)
builder += '{'
selections.foreach(loop(_, increment(indent), builder))
inner.unsafeRender(selections, increment(indent), builder)
newline(indent, builder)
pad(indent, builder)
builder += '}'

}
}

private def loop(selection: Selection, indent: Option[Int], builder: StringBuilder): Unit = {
newlineOrSpace(indent, builder)
private[caliban] lazy val selectionRenderer: Renderer[Selection] = new Renderer[Selection] {
override protected[caliban] def unsafeRender(
selection: Selection,
indent: Option[Int],
builder: StringBuilder
): Unit = {
pad(indent, builder)
selection match {
case Selection.Field(alias, name, arguments, directives, selectionSet, _) =>
Expand All @@ -204,12 +209,7 @@ object DocumentRenderer extends Renderer[Document] {
inputArgumentsRenderer.unsafeRender(arguments, indent, builder)
directivesRenderer.unsafeRender(directives, indent, builder)
if (selectionSet.nonEmpty) {
space(indent, builder)
builder += '{'
selectionSet.foreach(loop(_, increment(indent), builder))
newline(indent, builder)
pad(indent, builder)
builder += '}'
selectionsRenderer.unsafeRender(selectionSet, increment(indent), builder)
}
case Selection.FragmentSpread(name, directives) =>
builder ++= "..."
Expand All @@ -218,17 +218,13 @@ object DocumentRenderer extends Renderer[Document] {
case Selection.InlineFragment(typeCondition, dirs, selectionSet) =>
builder ++= "..."
typeCondition.foreach { t =>
builder ++= " on "
space(indent, builder)
builder ++= "on "
builder ++= t.name
}
directivesRenderer.unsafeRender(dirs, indent, builder)
if (selectionSet.nonEmpty) {
space(indent, builder)
builder += '{'
selectionSet.foreach(loop(_, increment(indent), builder))
newline(indent, builder)
pad(indent, builder)
builder += '}'
selectionsRenderer.unsafeRender(selectionSet, increment(indent), builder)
}
}
}
Expand Down Expand Up @@ -321,7 +317,7 @@ object DocumentRenderer extends Renderer[Document] {
write ++= " on "
write ++= typeCondition.name
directivesRenderer.unsafeRender(directives, indent, write)
selectionRenderer.unsafeRender(selectionSet, indent, write)
selectionsRenderer.unsafeRender(selectionSet, indent, write)
}
}

Expand Down
26 changes: 9 additions & 17 deletions tools/src/main/scala/caliban/tools/stitching/RemoteQuery.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@ import caliban.Value.FloatValue._
import caliban.Value.IntValue._
import caliban.parsing.SourceMapper
import caliban.parsing.adt.Definition.ExecutableDefinition.OperationDefinition
import caliban.parsing.adt.Type.NamedType
import caliban.parsing.adt.{ Document, OperationType, Selection }
import caliban.rendering.DocumentRenderer

import scala.collection.mutable

case class RemoteQuery(field: Field) { self =>
def toGraphQLRequest: GraphQLRequest =
GraphQLRequest(
query = Some(DocumentRenderer.render(toDocument))
query = Some(DocumentRenderer.renderCompact(toDocument))
)

def toDocument: Document = RemoteQuery.toDocument(OperationType.Query, field)
Expand All @@ -24,7 +27,7 @@ case class RemoteMutation(field: Field) { self =>
def toGraphQLRequest: GraphQLRequest =
GraphQLRequest(query =
Some(
DocumentRenderer.render(toDocument)
DocumentRenderer.renderCompact(toDocument)
)
)

Expand All @@ -34,33 +37,22 @@ case class RemoteMutation(field: Field) { self =>
object RemoteQuery {
object QueryRenderer {
def render(r: RemoteMutation): String =
DocumentRenderer.render(toDocument(OperationType.Mutation, r.field))
DocumentRenderer.renderCompact(r.toDocument)
def render(r: RemoteQuery): String =
DocumentRenderer.render(toDocument(OperationType.Query, r.field))
DocumentRenderer.renderCompact(r.toDocument)
}

def toDocument(operationType: OperationType, field: Field): Document = {
def loop(f: Field): Selection.Field =
Selection.Field(
f.alias,
f.name,
f.arguments,
f.directives,
f.fields.map(loop),
0
)

def toDocument(operationType: OperationType, field: Field): Document =
Document(
List(
OperationDefinition(
operationType,
None,
Nil,
Nil,
List(loop(field))
List(field.toSelection)
)
),
SourceMapper.empty
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ object RemoteQuerySpec extends ZIOSpecDefault {
i <- api(ref)
_ <- i.execute(query)
actual <- ref.get
} yield assertTrue(actual == """query { union(value: "foo\"") { ...on Interface { id } } }""")
} yield assertTrue(
actual == """query{union(value:"foo\""){...on Interface{id}}}"""
)
},
test("correctly renders a query for a field") {
val query = gqldoc("""{
Expand All @@ -66,7 +68,7 @@ object RemoteQuerySpec extends ZIOSpecDefault {
_ <- i.execute(query)
actual <- ref.get
} yield assertTrue(
actual == """query { union(value: "bar") { ...on Interface { id } ...on A { id } ...on B { id } } }"""
actual == """query{union(value:"bar"){...on Interface{id} ...on A{id} ...on B{id}}}"""
)
}
)
Expand Down

0 comments on commit 2ee4d21

Please sign in to comment.