Skip to content

Commit

Permalink
Fix for ArgBuilder with when input is a value type (#2323)
Browse files Browse the repository at this point in the history
* Fix for ArgBuilder with value types

* Add test for derivation of AnyVal ArgBuilder
  • Loading branch information
kyri-petrou committed Jul 2, 2024
1 parent 1a4f14a commit ce502f6
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ trait CommonArgBuilderDerivation {
def build(input: InputValue): Either[ExecutionError, T] =
input match {
case InputValue.ObjectValue(fields) => fromFields(fields)
case _ => Left(ExecutionError("expected an input object"))
case value => ctx.constructMonadic(p => p.typeclass.build(value))
}

private[this] def fromFields(fields: Map[String, InputValue]): Either[ExecutionError, T] =
Expand Down
30 changes: 23 additions & 7 deletions core/src/main/scala-3/caliban/schema/ArgBuilderDerivation.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package caliban.schema

import caliban.CalibanError.ExecutionError
import caliban.InputValue.{ ListValue, VariableValue }
import caliban.Value.*
import caliban.schema.Annotations.{ GQLDefault, GQLName, GQLOneOfInput }
import caliban.schema.macros.Macros
Expand Down Expand Up @@ -140,24 +141,39 @@ trait CommonArgBuilderDerivation {
def build(input: InputValue): Either[ExecutionError, A] =
input match {
case InputValue.ObjectValue(fields) => fromFields(fields)
case _ => Left(ExecutionError("expected an input object"))
case value => fromValue(value)
}

private def fromFields(fields: Map[String, InputValue]): Either[ExecutionError, A] = {
var i = 0
val l = params.length
var acc: Tuple = EmptyTuple
var i = 0
val l = params.length
val arr = Array.ofDim[Any](l)
while (i < l) {
val (label, default, builder) = params(i)
val field = fields.getOrElse(label, null)
val value = if (field ne null) builder.build(field) else default
value match {
case Right(v) => acc :*= v
case e @ Left(_) => return e.asInstanceOf[Either[ExecutionError, A]]
case Right(v) => arr(i) = v
case Left(e) => return Left(e)
}
i += 1
}
Right(fromProduct(acc))
Right(fromProduct(Tuple.fromArray(arr)))
}

private def fromValue(input: InputValue): Either[ExecutionError, A] = {
val l = params.length
val arr = Array.ofDim[Any](l)
var i = 0
while (i < l) {
val (_, _, builder) = params(i)
builder.build(input) match {
case Right(v) => arr(i) = v
case Left(e) => return Left(e)
}
i += 1
}
Right(fromProduct(Tuple.fromArray(arr)))
}

}
Expand Down
27 changes: 27 additions & 0 deletions core/src/test/scala-2/caliban/schema/ArgBuilderScala2Spec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package caliban.schema

import caliban.Value.StringValue
import caliban.schema.ArgBuilder.auto._
import zio.test.Assertion._
import zio.test._

import java.util.UUID

object ArgBuilderScala2Spec extends ZIOSpecDefault {
def spec = suite("ArgBuilderScala2")(
suite("AnyVal") {
test("ArgBuilder that extends AnyVal") {
val id = UUID.randomUUID()
val value = ArgBuilder[UUIDId].build(StringValue(id.toString))
assert(value)(isRight(equalTo(UUIDId(id))))
}
}
)

trait Ids[T] extends Any {
self: AnyVal =>
def value: T
}

final case class UUIDId(value: UUID) extends AnyVal with Ids[UUID]
}
21 changes: 21 additions & 0 deletions core/src/test/scala/caliban/execution/ExecutionSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -860,6 +860,27 @@ object ExecutionSpec extends ZIOSpecDefault {
.flatMap(_.execute(query))
.map(result => assertTrue(result.data.toString == """null"""))
},
test("using value types as inputs") {
@GQLValueType
case class UserArgs(id: Int)
case class User(test: UserArgs => String)
case class Mutations(user: UIO[User])
case class Queries(a: Int)
val api = graphQL(RootResolver(Queries(1), Mutations(ZIO.succeed(User(_.toString)))))

val interpreter = api.interpreter
val query =
"""mutation {
| user {
| test(value: 123)
| }
|}""".stripMargin
interpreter
.flatMap(_.execute(query))
.debug
.map(_.data.toString)
.map(result => assertTrue(result == """{"user":{"test":"UserArgs(123)"}}"""))
},
test("die inside a nullable list") {
case class Queries(test: List[Task[String]])
val api = graphQL(RootResolver(Queries(List(ZIO.succeed("a"), ZIO.die(new Exception("Boom"))))))
Expand Down

0 comments on commit ce502f6

Please sign in to comment.