From 6765a7c09d1aebe082295d30b6ff1ea4859b3929 Mon Sep 17 00:00:00 2001 From: Kyri Petrou Date: Thu, 2 Nov 2023 21:05:05 +1100 Subject: [PATCH 1/3] Fix schema generation when renaming interfaces via `.rename` --- .../main/scala/caliban/schema/Schema.scala | 17 ++++- .../schema/SchemaDerivationIssuesSpec.scala | 70 +++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/core/src/main/scala/caliban/schema/Schema.scala b/core/src/main/scala/caliban/schema/Schema.scala index 929b655614..16d8008c0e 100644 --- a/core/src/main/scala/caliban/schema/Schema.scala +++ b/core/src/main/scala/caliban/schema/Schema.scala @@ -101,8 +101,23 @@ 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)) + + if (tpe.kind != __TypeKind.INTERFACE) newTpe + else { + val pt = tpe.possibleTypes.map(_.map { t => + val newInterfaces = t + .interfaces() + .map(_.map { tt => + if (tt.name == tpe.name) tt.copy(name = Some(newName)) + else tt + }) + t.copy(interfaces = () => newInterfaces) + }) + newTpe.copy(possibleTypes = pt) + } } private lazy val renameTypename: Boolean = self.toType_().kind match { diff --git a/core/src/test/scala/caliban/schema/SchemaDerivationIssuesSpec.scala b/core/src/test/scala/caliban/schema/SchemaDerivationIssuesSpec.scala index 9bbb2ffc6c..f44acb2d7d 100644 --- a/core/src/test/scala/caliban/schema/SchemaDerivationIssuesSpec.scala +++ b/core/src/test/scala/caliban/schema/SchemaDerivationIssuesSpec.scala @@ -1,5 +1,6 @@ package caliban.schema +import caliban.schema.Annotations.{ GQLInterface, GQLUnion } import zio.Chunk import zio.test.ZIOSpecDefault import zio.test._ @@ -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") @@ -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 +} From c79bc2a09841abea48be11c9f66873e80a94bd40 Mon Sep 17 00:00:00 2001 From: Kyri Petrou Date: Thu, 2 Nov 2023 21:09:42 +1100 Subject: [PATCH 2/3] Cleanups --- .../main/scala/caliban/schema/Schema.scala | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/core/src/main/scala/caliban/schema/Schema.scala b/core/src/main/scala/caliban/schema/Schema.scala index 16d8008c0e..cdbf17249e 100644 --- a/core/src/main/scala/caliban/schema/Schema.scala +++ b/core/src/main/scala/caliban/schema/Schema.scala @@ -105,18 +105,16 @@ trait Schema[-R, T] { self => val newName = if (isInput) inputName.getOrElse(Schema.customizeInputTypeName(name)) else name val newTpe = tpe.copy(name = Some(newName)) - if (tpe.kind != __TypeKind.INTERFACE) newTpe - else { - val pt = tpe.possibleTypes.map(_.map { t => - val newInterfaces = t - .interfaces() - .map(_.map { tt => - if (tt.name == tpe.name) tt.copy(name = Some(newName)) - else tt - }) - t.copy(interfaces = () => newInterfaces) - }) - newTpe.copy(possibleTypes = pt) + 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 } } From 0589cb9e13a81f68fd0faad1e9ddacf1f2ec3874 Mon Sep 17 00:00:00 2001 From: Kyri Petrou Date: Thu, 2 Nov 2023 21:46:21 +1100 Subject: [PATCH 3/3] Unpack unions prior to creating interfaces for Scala 3 --- .../main/scala-3/caliban/schema/SchemaDerivation.scala | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/core/src/main/scala-3/caliban/schema/SchemaDerivation.scala b/core/src/main/scala-3/caliban/schema/SchemaDerivation.scala index 6b0afdf6f9..6faefb4d6c 100644 --- a/core/src/main/scala-3/caliban/schema/SchemaDerivation.scala +++ b/core/src/main/scala-3/caliban/schema/SchemaDerivation.scala @@ -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) } @@ -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 {