Skip to content

Commit

Permalink
Root query: generate {x}Option method for fields with an optional i…
Browse files Browse the repository at this point in the history
…nterface type (ghostdogpr#1223)

* Root query: generate `{x}Option` method for fields with an optional interface type

* Root mutation/subscription: generate `{x}Option` method for fields with an optional interface type

* Fix root mutation
  • Loading branch information
iRevive authored and Fluxx committed Jan 13, 2022
1 parent acc31c1 commit ca2b158
Show file tree
Hide file tree
Showing 2 changed files with 274 additions and 33 deletions.
131 changes: 98 additions & 33 deletions tools/src/main/scala/caliban/tools/ClientWriter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,15 @@ object ClientWriter {
safeTypeName(name) -> op
}.toMap

val knownInterfaceTypes = typesMap.collect { case (key, _: InterfaceTypeDefinition) => key }
val knownUnionTypes = typesMap.collect { case (key, _: UnionTypeDefinition) => key }

def isOptionalInterfaceType(field: FieldDefinition): Boolean =
knownInterfaceTypes.exists(_.compareToIgnoreCase(Type.innerType(field.ofType)) == 0)

def isOptionalUnionType(field: FieldDefinition): Boolean =
knownUnionTypes.exists(_.compareToIgnoreCase(Type.innerType(field.ofType)) == 0)

def writeFieldInfo(fieldInfo: FieldInfo): String = {
val FieldInfo(
name,
Expand Down Expand Up @@ -309,16 +318,36 @@ object ClientWriter {

def writeRootQuery(typedef: ObjectTypeDefinition): String =
s"""object ${typedef.name} {
| ${typedef.fields
.map(
writeField(
_,
"_root_.caliban.client.Operations.RootQuery",
optionalUnion = false,
optionalInterface = false,
commonInterface = false
| ${typedef.fields.flatMap { field =>
if (isOptionalInterfaceType(field)) {
Vector(
writeField(
field,
"_root_.caliban.client.Operations.RootQuery",
optionalUnion = false,
optionalInterface = false,
commonInterface = false
),
writeField(
field,
"_root_.caliban.client.Operations.RootQuery",
optionalUnion = false,
optionalInterface = true,
commonInterface = false
)
)
)
} else {
Vector(
writeField(
field,
"_root_.caliban.client.Operations.RootQuery",
optionalUnion = false,
optionalInterface = false,
commonInterface = false
)
)
}
}
.mkString("\n ")}
|}
|""".stripMargin
Expand All @@ -330,16 +359,36 @@ object ClientWriter {

def writeRootMutation(typedef: ObjectTypeDefinition): String =
s"""object ${typedef.name} {
| ${typedef.fields
.map(
writeField(
_,
"_root_.caliban.client.Operations.RootMutation",
optionalUnion = false,
optionalInterface = false,
commonInterface = false
| ${typedef.fields.flatMap { field =>
if (isOptionalInterfaceType(field)) {
Vector(
writeField(
field,
"_root_.caliban.client.Operations.RootMutation",
optionalUnion = false,
optionalInterface = false,
commonInterface = false
),
writeField(
field,
"_root_.caliban.client.Operations.RootMutation",
optionalUnion = false,
optionalInterface = true,
commonInterface = false
)
)
)
} else {
Vector(
writeField(
field,
"_root_.caliban.client.Operations.RootMutation",
optionalUnion = false,
optionalInterface = false,
commonInterface = false
)
)
}
}
.mkString("\n ")}
|}
|""".stripMargin
Expand All @@ -351,16 +400,36 @@ object ClientWriter {

def writeRootSubscription(typedef: ObjectTypeDefinition): String =
s"""object ${typedef.name} {
| ${typedef.fields
.map(
writeField(
_,
"_root_.caliban.client.Operations.RootSubscription",
optionalUnion = false,
optionalInterface = false,
commonInterface = false
| ${typedef.fields.flatMap { field =>
if (isOptionalInterfaceType(field)) {
Vector(
writeField(
field,
"_root_.caliban.client.Operations.RootSubscription",
optionalUnion = false,
optionalInterface = false,
commonInterface = false
),
writeField(
field,
"_root_.caliban.client.Operations.RootSubscription",
optionalUnion = false,
optionalInterface = true,
commonInterface = false
)
)
)
} else {
Vector(
writeField(
field,
"_root_.caliban.client.Operations.RootSubscription",
optionalUnion = false,
optionalInterface = false,
commonInterface = false
)
)
}
}
.mkString("\n ")}
|}
|""".stripMargin
Expand All @@ -376,10 +445,8 @@ object ClientWriter {

val objectName: String = safeTypeName(typedef.name)

val unionTypes = typesMap.collect { case (key, _: UnionTypeDefinition) => key }
val optionalUnionTypeFields = typedef.fields.flatMap { field =>
val isOptionalUnionType = unionTypes.exists(_.compareToIgnoreCase(Type.innerType(field.ofType)) == 0)
if (isOptionalUnionType)
if (isOptionalUnionType(field))
Some(
collectFieldInfo(
field,
Expand All @@ -392,10 +459,8 @@ object ClientWriter {
else None
}

val interfaceTypes = typesMap.collect { case (key, _: InterfaceTypeDefinition) => key }
val optionalInterfaceTypeFields = typedef.fields.flatMap { field =>
val isOptionalInterfaceType = interfaceTypes.exists(_.compareToIgnoreCase(Type.innerType(field.ofType)) == 0)
if (isOptionalInterfaceType)
if (isOptionalInterfaceType(field))
Vector(
collectFieldInfo(
field,
Expand Down
176 changes: 176 additions & 0 deletions tools/src/test/scala/caliban/tools/ClientWriterViewSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1152,6 +1152,182 @@ object Client {
"""
)
)
},
testM("root schema optional interface") {
val schema =
"""
schema {
query: Queries
mutation: Mutations
subscription: Subscriptions
}
type Queries {
node(id: ID!): Node
}
type Mutations {
updateNode(id: ID!, name: String): Node
}
type Subscriptions {
node(id: ID!): Node
}
interface Node {
id: ID!
}
type NodeA implements Node {
id: ID!
a: String
}
type NodeB implements Node {
id: ID!
b: Int
}
"""

assertM(gen(schema))(
equalTo("""import caliban.client.FieldBuilder._
import caliban.client._
object Client {
type Node
object Node {
final case class NodeView(id: String)
type ViewSelection = SelectionBuilder[Node, NodeView]
def view: ViewSelection = id.map(id => NodeView(id))
def id: SelectionBuilder[Node, String] = _root_.caliban.client.SelectionBuilder.Field("id", Scalar())
}
type NodeA
object NodeA {
final case class NodeAView(id: String, a: Option[String])
type ViewSelection = SelectionBuilder[NodeA, NodeAView]
def view: ViewSelection = (id ~ a).map { case (id, a) => NodeAView(id, a) }
def id: SelectionBuilder[NodeA, String] = _root_.caliban.client.SelectionBuilder.Field("id", Scalar())
def a: SelectionBuilder[NodeA, Option[String]] =
_root_.caliban.client.SelectionBuilder.Field("a", OptionOf(Scalar()))
}
type NodeB
object NodeB {
final case class NodeBView(id: String, b: Option[Int])
type ViewSelection = SelectionBuilder[NodeB, NodeBView]
def view: ViewSelection = (id ~ b).map { case (id, b) => NodeBView(id, b) }
def id: SelectionBuilder[NodeB, String] = _root_.caliban.client.SelectionBuilder.Field("id", Scalar())
def b: SelectionBuilder[NodeB, Option[Int]] = _root_.caliban.client.SelectionBuilder.Field("b", OptionOf(Scalar()))
}
type Queries = _root_.caliban.client.Operations.RootQuery
object Queries {
def node[A](id: String)(onNodeA: SelectionBuilder[NodeA, A], onNodeB: SelectionBuilder[NodeB, A])(implicit
encoder0: ArgEncoder[String]
): SelectionBuilder[_root_.caliban.client.Operations.RootQuery, Option[A]] =
_root_.caliban.client.SelectionBuilder.Field(
"node",
OptionOf(ChoiceOf(Map("NodeA" -> Obj(onNodeA), "NodeB" -> Obj(onNodeB)))),
arguments = List(Argument("id", id, "ID!")(encoder0))
)
def nodeOption[A](
id: String
)(onNodeA: Option[SelectionBuilder[NodeA, A]] = None, onNodeB: Option[SelectionBuilder[NodeB, A]] = None)(implicit
encoder0: ArgEncoder[String]
): SelectionBuilder[_root_.caliban.client.Operations.RootQuery, Option[Option[A]]] =
_root_.caliban.client.SelectionBuilder.Field(
"node",
OptionOf(
ChoiceOf(
Map(
"NodeA" -> onNodeA.fold[FieldBuilder[Option[A]]](NullField)(a => OptionOf(Obj(a))),
"NodeB" -> onNodeB.fold[FieldBuilder[Option[A]]](NullField)(a => OptionOf(Obj(a)))
)
)
),
arguments = List(Argument("id", id, "ID!")(encoder0))
)
}
type Mutations = _root_.caliban.client.Operations.RootMutation
object Mutations {
def updateNode[A](
id: String,
name: Option[String] = None
)(onNodeA: SelectionBuilder[NodeA, A], onNodeB: SelectionBuilder[NodeB, A])(implicit
encoder0: ArgEncoder[String],
encoder1: ArgEncoder[Option[String]]
): SelectionBuilder[_root_.caliban.client.Operations.RootMutation, Option[A]] =
_root_.caliban.client.SelectionBuilder.Field(
"updateNode",
OptionOf(ChoiceOf(Map("NodeA" -> Obj(onNodeA), "NodeB" -> Obj(onNodeB)))),
arguments = List(Argument("id", id, "ID!")(encoder0), Argument("name", name, "String")(encoder1))
)
def updateNodeOption[A](
id: String,
name: Option[String] = None
)(onNodeA: Option[SelectionBuilder[NodeA, A]] = None, onNodeB: Option[SelectionBuilder[NodeB, A]] = None)(implicit
encoder0: ArgEncoder[String],
encoder1: ArgEncoder[Option[String]]
): SelectionBuilder[_root_.caliban.client.Operations.RootMutation, Option[Option[A]]] =
_root_.caliban.client.SelectionBuilder.Field(
"updateNode",
OptionOf(
ChoiceOf(
Map(
"NodeA" -> onNodeA.fold[FieldBuilder[Option[A]]](NullField)(a => OptionOf(Obj(a))),
"NodeB" -> onNodeB.fold[FieldBuilder[Option[A]]](NullField)(a => OptionOf(Obj(a)))
)
)
),
arguments = List(Argument("id", id, "ID!")(encoder0), Argument("name", name, "String")(encoder1))
)
}
type Subscriptions = _root_.caliban.client.Operations.RootSubscription
object Subscriptions {
def node[A](id: String)(onNodeA: SelectionBuilder[NodeA, A], onNodeB: SelectionBuilder[NodeB, A])(implicit
encoder0: ArgEncoder[String]
): SelectionBuilder[_root_.caliban.client.Operations.RootSubscription, Option[A]] =
_root_.caliban.client.SelectionBuilder.Field(
"node",
OptionOf(ChoiceOf(Map("NodeA" -> Obj(onNodeA), "NodeB" -> Obj(onNodeB)))),
arguments = List(Argument("id", id, "ID!")(encoder0))
)
def nodeOption[A](
id: String
)(onNodeA: Option[SelectionBuilder[NodeA, A]] = None, onNodeB: Option[SelectionBuilder[NodeB, A]] = None)(implicit
encoder0: ArgEncoder[String]
): SelectionBuilder[_root_.caliban.client.Operations.RootSubscription, Option[Option[A]]] =
_root_.caliban.client.SelectionBuilder.Field(
"node",
OptionOf(
ChoiceOf(
Map(
"NodeA" -> onNodeA.fold[FieldBuilder[Option[A]]](NullField)(a => OptionOf(Obj(a))),
"NodeB" -> onNodeB.fold[FieldBuilder[Option[A]]](NullField)(a => OptionOf(Obj(a)))
)
)
),
arguments = List(Argument("id", id, "ID!")(encoder0))
)
}
}
""")
)
}
) @@ TestAspect.sequential
}

0 comments on commit ca2b158

Please sign in to comment.