Skip to content

Commit

Permalink
Return Exit for methods in Pagination (#2298)
Browse files Browse the repository at this point in the history
* Return Exit for methods in Pagination

* Add explicit implicits cause Scala 2.12
  • Loading branch information
kyri-petrou authored Jun 23, 2024
1 parent b143a0d commit 0f760dc
Showing 1 changed file with 47 additions and 54 deletions.
101 changes: 47 additions & 54 deletions core/src/main/scala/caliban/relay/PaginationArgs.scala
Original file line number Diff line number Diff line change
@@ -1,89 +1,88 @@
package caliban.relay

import caliban.CalibanError
import zio._
import zio.Exit
import zio.prelude._

object Pagination {
import PaginationCount._
import PaginationCursor._

def apply[C: Cursor](
args: PaginationArgs[C]
): ZIO[Any, CalibanError, Pagination[C]] =
def apply[C: Cursor](args: PaginationArgs[C]): Exit[CalibanError, Pagination[C]] =
apply(args.first, args.last, args.before, args.after)

def apply[C: Cursor](
args: ForwardPaginationArgs[C]
): ZIO[Any, CalibanError, Pagination[C]] =
def apply[C: Cursor](args: ForwardPaginationArgs[C]): Exit[CalibanError, Pagination[C]] =
(args.first match {
case None => ZIO.fail(s"first cannot be empty")
case Some(a) => validatePositive("first", a).map(First(_))
case None => Validation.fail("first cannot be empty")
case Some(a) => validatePositive("first", a).map(First.apply)
})
.validate(args.after match {
case None => ZIO.succeed(NoCursor)
case Some(x) => ZIO.fromEither(Cursor[C].decode(x)).map(After(_))
})
.map { case (count, cursor) => new Pagination[C](count, cursor) }
.parallelErrors
.mapError((errors: ::[String]) => CalibanError.ExecutionError(msg = errors.mkString(", ")))
.zipWith(args.after match {
case None => Validation.succeed(NoCursor)
case Some(x) => Validation.fromEither(Cursor[C].decode(x)).map(After.apply)
})(new Pagination[C](_, _))(
// Explicit implicits cause thanks Scala 2.12
ZValidation.ZValidationIdentityBoth,
implicitly
)
.toExit

def apply[C: Cursor](
args: BackwardPaginationArgs[C]
): ZIO[Any, CalibanError, Pagination[C]] =
def apply[C: Cursor](args: BackwardPaginationArgs[C]): Exit[CalibanError, Pagination[C]] =
(args.last match {
case None => ZIO.fail(s"last cannot be empty")
case Some(a) => validatePositive("last", a).map(Last(_))
case None => Validation.fail("last cannot be empty")
case Some(a) => validatePositive("last", a).map(Last.apply)
})
.validate(args.before match {
case None => ZIO.succeed(NoCursor)
case Some(x) => ZIO.fromEither(Cursor[C].decode(x)).map(Before(_))
})
.map { case (count, cursor) => new Pagination[C](count, cursor) }
.parallelErrors
.mapError((errors: ::[String]) => CalibanError.ExecutionError(msg = errors.mkString(", ")))
.zipWith(args.before match {
case None => Validation.succeed(NoCursor)
case Some(x) => Validation.fromEither(Cursor[C].decode(x)).map(Before.apply)
})(new Pagination[C](_, _))(ZValidation.ZValidationIdentityBoth, implicitly)
.toExit

def apply[C: Cursor](
first: Option[Int],
last: Option[Int],
before: Option[String],
after: Option[String]
): ZIO[Any, CalibanError, Pagination[C]] =
): Exit[CalibanError, Pagination[C]] =
validateFirstLast(first, last)
.validate(
validateCursors(before, after)
)
.map { case (count, cursor) => new Pagination[C](count, cursor) }
.parallelErrors
.mapError((errors: ::[String]) => CalibanError.ExecutionError(msg = errors.mkString(", ")))
.zipWith(validateCursors(before, after))(new Pagination[C](_, _))(ZValidation.ZValidationIdentityBoth, implicitly)
.toExit

private def validateCursors[C: Cursor](
before: Option[String],
after: Option[String]
): ZIO[Any, String, PaginationCursor[C]] =
): Validation[String, PaginationCursor[C]] =
(before, after) match {
case (Some(_), Some(_)) =>
ZIO.fail("before and after cannot both be set")
Validation.fail("before and after cannot both be set")
case (Some(x), _) =>
ZIO.fromEither(Cursor[C].decode(x)).map(Before(_))
Validation.fromEither(Cursor[C].decode(x)).map(Before.apply)
case (_, Some(x)) =>
ZIO.fromEither(Cursor[C].decode(x)).map(After(_))
case (None, None) => ZIO.succeed(NoCursor)
Validation.fromEither(Cursor[C].decode(x)).map(After.apply)
case (None, None) => Validation.succeed(NoCursor)
}

private def validateFirstLast(first: Option[Int], last: Option[Int]) =
(first, last) match {
case (None, None) =>
ZIO.fail("first and last cannot both be empty")
Validation.fail("first and last cannot both be empty")
case (Some(_), Some(_)) =>
ZIO.fail("first and last cannot both be set")
Validation.fail("first and last cannot both be set")
case (Some(a), _) =>
validatePositive("first", a).map(First(_))
validatePositive("first", a).map(First.apply)
case (_, Some(b)) =>
validatePositive("last", b).map(Last(_))
validatePositive("last", b).map(Last.apply)
}

private def validatePositive(which: String, i: Int) =
ZIO.cond(i > -1, i, s"$which cannot be negative")
if (i > -1) Validation.succeed(i) else Validation.fail(s"$which cannot be negative")

private implicit class ValidationOps[E, A](private val v: Validation[String, A]) extends AnyVal {
def toExit: Exit[CalibanError, A] = {
val either = v.toEitherWith(errors => CalibanError.ExecutionError(msg = errors.mkString(", ")))
Exit.fromEither(either)
}
}

}

sealed trait PaginationCount extends Product with Serializable {
Expand Down Expand Up @@ -112,27 +111,21 @@ abstract class PaginationArgs[C: Cursor] { self =>
val before: Option[String]
val after: Option[String]

def toPagination: ZIO[Any, CalibanError, Pagination[C]] = Pagination(
self
)
def toPagination: Exit[CalibanError, Pagination[C]] = Pagination(self)
}

abstract class ForwardPaginationArgs[C: Cursor] { self =>
val first: Option[Int]
val after: Option[String]

def toPagination: ZIO[Any, CalibanError, Pagination[C]] = Pagination(
self
)
def toPagination: Exit[CalibanError, Pagination[C]] = Pagination(self)
}

abstract class BackwardPaginationArgs[C: Cursor] { self =>
val last: Option[Int]
val before: Option[String]

def toPagination: ZIO[Any, CalibanError, Pagination[C]] = Pagination(
self
)
def toPagination: Exit[CalibanError, Pagination[C]] = Pagination(self)
}

case class PaginationError(reason: String)

0 comments on commit 0f760dc

Please sign in to comment.