From fe589e384c950d72ad1065b58d7937b36df27b61 Mon Sep 17 00:00:00 2001 From: Jakob Odersky Date: Thu, 1 Mar 2018 15:37:11 -0800 Subject: Use Akka's parameter directive to extract a pagination --- src/main/scala/xyz/driver/core/rest/package.scala | 35 +++------------- src/test/scala/xyz/driver/core/rest/RestTest.scala | 49 ++++++++++++++++++---- 2 files changed, 48 insertions(+), 36 deletions(-) diff --git a/src/main/scala/xyz/driver/core/rest/package.scala b/src/main/scala/xyz/driver/core/rest/package.scala index 78d04f4..19752a1 100644 --- a/src/main/scala/xyz/driver/core/rest/package.scala +++ b/src/main/scala/xyz/driver/core/rest/package.scala @@ -14,7 +14,7 @@ import akka.util.ByteString import xyz.driver.tracing.TracingDirectives import scala.concurrent.Future -import scala.util.{Failure, Success, Try} +import scala.util.Try import scalaz.{Functor, OptionT} import scalaz.Scalaz.{intInstance, stringInstance} import scalaz.syntax.equal._ @@ -33,28 +33,10 @@ trait ServiceTransport { implicit mat: Materializer): Future[Unmarshal[ResponseEntity]] } -object Pagination { - - val Default = Pagination(pageSize = 100, pageNumber = 1) - - def parse(query: Seq[(String, String)]): Try[Pagination] = { - val IntString = """(\d+)""".r - def validate(field: String, default: Int) = query.collectFirst { case (`field`, size) => size } match { - case Some(IntString(x)) if x.toInt > 0 => x.toInt - case Some(IntString(x)) => throw new Exception(s"$field must greater than zero (found $x)") - case Some(str) => throw new Exception(s"$field must be an integer (found $str)") - case None => default - } - - Try { - Pagination( - validate("pageSize", Pagination.Default.pageSize), - validate("pageNumber", Pagination.Default.pageNumber)) - } - } -} - final case class Pagination(pageSize: Int, pageNumber: Int) { + require(pageSize > 0, "Page size must be greater than zero") + require(pageNumber > 0, "Page number must be greater than zero") + def offset: Int = pageSize * (pageNumber - 1) } @@ -234,13 +216,8 @@ object `package` { mapRequest(request => request.mapEntity(entity => entity.transformDataBytes(Flow.fromFunction(escapeScriptTags)))) } - val paginated: Directive1[Pagination] = parameterSeq.flatMap { params => - Pagination.parse(params) match { - case Success(pagination) => provide(pagination) - case Failure(ex) => - reject(ValidationRejection("invalid pagination parameter", Some(ex))) - } - } + val paginated: Directive1[Pagination] = + parameters(("pageSize".as[Int] ? 100, "pageNumber".as[Int] ? 1)).as(Pagination) def paginationQuery(pagination: Pagination) = Seq("pageNumber" -> pagination.pageNumber.toString, "pageSize" -> pagination.pageSize.toString) diff --git a/src/test/scala/xyz/driver/core/rest/RestTest.scala b/src/test/scala/xyz/driver/core/rest/RestTest.scala index 2c3fb7f..d36e04d 100644 --- a/src/test/scala/xyz/driver/core/rest/RestTest.scala +++ b/src/test/scala/xyz/driver/core/rest/RestTest.scala @@ -1,15 +1,50 @@ package xyz.driver.core.rest +import akka.http.scaladsl.model.StatusCodes +import akka.http.scaladsl.server.{Directives, Route, ValidationRejection} +import akka.http.scaladsl.testkit.ScalatestRouteTest import akka.util.ByteString -import org.scalatest.{FlatSpec, Matchers} +import org.scalatest.{Matchers, WordSpec} +import xyz.driver.core.rest -class RestTest extends FlatSpec with Matchers { - "`escapeScriptTags` function" should "escap script tags properly" in { - val dirtyString = " + complete(StatusCodes.OK -> s"${paginated.pageNumber},${paginated.pageSize}") + } + "accept a pagination" in { + Get("/?pageNumber=2&pageSize=42") ~> route ~> check { + assert(status == StatusCodes.OK) + assert(entityAs[String] == "2,42") + } + } + "provide a default pagination" in { + Get("/") ~> route ~> check { + assert(status == StatusCodes.OK) + assert(entityAs[String] == "1,100") + } + } + "provide default values for a partial pagination" in { + Get("/?pageSize=2") ~> route ~> check { + assert(status == StatusCodes.OK) + assert(entityAs[String] == "1,2") + } + } + "reject an invalid pagination" in { + Get("/?pageNumber=-1") ~> route ~> check { + assert(rejection.isInstanceOf[ValidationRejection]) + } + } } } -- cgit v1.2.3