Skip to content

Commit

Permalink
Fix schema derivation on Interfaces when renamed via .rename (#1978)
Browse files Browse the repository at this point in the history
* Fix schema generation when renaming interfaces via `.rename`

* Cleanups

* Unpack unions prior to creating interfaces for Scala 3
  • Loading branch information
kyri-petrou authored Nov 2, 2023
1 parent 5ac5004 commit 85d4811
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 2 deletions.
10 changes: 9 additions & 1 deletion core/src/main/scala-3/caliban/schema/SchemaDerivation.scala
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,9 @@ trait CommonSchemaDerivation {
Some(getDirectives(annotations))
)
else {
val impl = subTypes.map(_._2.copy(interfaces = () => Some(List(toType(isInput, isSubscription)))))
val impl = subTypes
.flatMap(v => unpackUnion(v._2))
.map(_.copy(interfaces = () => Some(List(toType(isInput, isSubscription)))))
mkInterface(annotations, info, impl)
}

Expand Down Expand Up @@ -197,6 +199,12 @@ trait CommonSchemaDerivation {
case Some(tpes) => tpes.flatMap(unpackLeafTypes)
}

private def unpackUnion(t: __Type): List[__Type] =
t.kind match {
case __TypeKind.UNION => t.possibleTypes.fold(List(t))(_.flatMap(unpackUnion))
case _ => List(t)
}

// see https://github.com/graphql/graphql-spec/issues/568
private def fixEmptyUnionObject(t: __Type): __Type =
t.fields(__DeprecatedArgs(Some(true))) match {
Expand Down
15 changes: 14 additions & 1 deletion core/src/main/scala/caliban/schema/Schema.scala
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,21 @@ trait Schema[-R, T] { self =>
override def optional: Boolean = self.optional
override def arguments: List[__InputValue] = self.arguments
override def toType(isInput: Boolean, isSubscription: Boolean): __Type = {
val tpe = self.toType_(isInput, isSubscription)
val newName = if (isInput) inputName.getOrElse(Schema.customizeInputTypeName(name)) else name
self.toType_(isInput, isSubscription).copy(name = Some(newName))
val newTpe = tpe.copy(name = Some(newName))

tpe.kind match {
case __TypeKind.INTERFACE =>
val pt = tpe.possibleTypes.map(_.map { t0 =>
val newInterfaces = t0
.interfaces()
.map(_.map(t1 => if (t1.name == tpe.name && t1.name.isDefined) t1.copy(name = Some(newName)) else t1))
t0.copy(interfaces = () => newInterfaces)
})
newTpe.copy(possibleTypes = pt)
case _ => newTpe
}
}

private lazy val renameTypename: Boolean = self.toType_().kind match {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package caliban.schema

import caliban.schema.Annotations.{ GQLInterface, GQLUnion }
import zio.Chunk
import zio.test.ZIOSpecDefault
import zio.test._
Expand Down Expand Up @@ -41,11 +42,42 @@ object SchemaDerivationIssuesSpec extends ZIOSpecDefault {
| param: ASTParameter!
|}""".stripMargin
)
},
test("i1977") {
import i1977._

assertTrue(
schema ==
"""schema {
| query: Queries
|}
|
|union ASTValue = ASTValueBoolean | ASTValueList
|
|interface ASTParameter
|
|type ASTValueBoolean implements ASTParameter {
| value: Boolean!
|}
|
|type ASTValueList implements ASTParameter {
| values: [ASTValue!]!
|}
|
|type ASTVariable implements ASTParameter {
| name: String!
|}
|
|type Queries {
| param: ASTParameter!
|}""".stripMargin
)
}
)
}

private object i1972_i1973 {
@GQLUnion
sealed trait Parameter
object Parameter {
implicit lazy val schema: Schema[Any, Parameter] = Schema.gen[Any, Parameter].rename("ASTParameter")
Expand Down Expand Up @@ -83,3 +115,41 @@ private object i1972_i1973 {
graphQL(RootResolver(queries))
}
}

private object i1977 {
@GQLInterface
sealed trait Parameter
object Parameter {
implicit lazy val schema: Schema[Any, Parameter] = Schema.gen[Any, Parameter].rename("ASTParameter")
}

case class Variable(name: String) extends Parameter
object Variable {
implicit val schema: Schema[Any, Variable] = Schema.gen[Any, Variable].rename("ASTVariable")
}

sealed trait Value extends Parameter
object Value {
case class Boolean(value: scala.Boolean) extends Value
object Boolean {
implicit val schema: Schema[Any, Boolean] = Schema.gen[Any, Boolean].rename("ASTValueBoolean")
}

case class List(values: Chunk[Value]) extends Value
object List {
implicit lazy val schema: Schema[Any, List] = Schema.gen[Any, List].rename("ASTValueList")
}

implicit lazy val schema: Schema[Any, Value] = Schema.gen[Any, Value].rename("ASTValue")
}

case class Queries(param: Parameter)
object Queries {
implicit val schema: Schema[Any, Queries] = Schema.gen
}

val schema = {
val queries = Queries(param = Variable("temp"))
graphQL(RootResolver(queries))
}.render
}

0 comments on commit 85d4811

Please sign in to comment.