How to bind enum on routes using scala?

Viewed 408

I'm trying to receive some filter list as query param of a get request, which is also an enum on my controller. To do so, I'm using play framework with scala. The problem is: I can't putting the enum type as query param on it, because IDE doesn't recognize as a valid type.

So, I have something like this on routes file

GET /service-orders/ controllers.ServiceOrdersController.listServiceOrders(status: ServiceStatus)

My enum file:

object ServiceStatus extends Enumeration {

  type ServiceStatus = Value

  val Pending = Value("pending")
  val Started = Value("started")
  val Completed = Value("completed")
  val Error = Value("error")
  
}

On build.sbt, I use this trying to inject package on routes file

  routesImport ++= Seq(
      "serviceOrders.models.ServiceStatus"
    ),

I tried a lot of things, but with no success. I read in some place that I could use QueryStringBindable function, but I can't did this works well... Can you guys please help me to solve this?

Edit: Btw, there's a way to check if status is contained on a list of filters without making this?

.filter {
        serviceOrder =>
          status.map(serviceOrder.serviceStatus === _)
            .reduceOption(_ || _)
            .getOrElse(true: Rep[Boolean])
      }

This was the only way I could thought to filter status by a list of filters received as query param from API.

1 Answers

You can implement the QueryStringBindable instance like so:

package serviceOrders.models

object ServiceStatus extends Enumeration {

  type ServiceStatus = Value

  val Pending = Value("pending")
  val Started = Value("started")
  val Completed = Value("completed")
  val Error = Value("error")

  implicit val queryStringBindable: QueryStringBindable[ServiceStatus] =
    new QueryStringBindable[ServiceStatus] {
      override def bind(
        key: String,
        params: Map[String, Seq[String]]
      ): Option[Either[String, ServiceStatus]] =
        params.get(key).collect {
          case Seq(s) =>
            ServiceStatus.values.find(_.toString == s).toRight("invalid value")
        }

      override def unbind(key: String, value: ServiceStatus): String =
        implicitly[QueryStringBindable[String]].unbind(key, value.toString)
    }
}

In build.sbt you need this:

routesImport ++= Seq("serviceOrders.models.ServiceStatus._")

And this in your routes file:

GET     /some/route    controllers.SomeController.index(status: ServiceStatus)

Then you can create an index method that takes a ServiceStatus parameter in SomeController and Play will take care of the query parameters.

// edit: You could actually use the QueryStringBindable.Parsing class to simplify the implementation further.

Related