Reputation: 11887
I know it's possible to define Case classes that we want to match some given json against (we use JsValue.validate[T]
):
For example:
case class UpdateDashboardModel(id: Long,
maybeName: Option[String],
containers: Option[List[UpdateContainerModel]],
description: Option[String])
And then we must write a Reads[T]
to define how to actually turn a json object into an instance of our case class (and optionally define a few custom validators for individual attributes):
val exists: Reads[Long] =
Reads.LongReads.filter(ValidationError("Dashboard does not exist"))(long => Dashboard.findById(long).isDefined)
implicit val reads: Reads[UpdateDashboardModel] = (
(JsPath \ "id").read[Long](exists) and
(JsPath \ "name").readNullable[String] and
(JsPath \ "containers").readNullable[List[UpdateContainerModel]] and
(JsPath \ "description").readNullable[String]) (UpdateDashboardModel.apply _ )
In this example, I run a simple validation for the given id -> it must exist in the database otherwise I have to throw an error.
Problem is, I can't seem to be able to write a validator for something that requires two attributes.
For example, I would like to write a short validator that takes the id and the name attributes because I want to check whether that name is not already in use by another dashboard (if it's the current dashboard, it's ok).
Can anybody think of a way to do this?
Thanks in advance.
Upvotes: 0
Views: 235
Reputation: 8608
Well, it's not pretty, but I think something like this should work... Let's assume isNameAvailable
is a function that ensures that the name is not already in use by another dashboard, and returns true if name is available (false otherwise).
import play.api.data.validation.ValidationError
implicit val reads: Reads[UpdateDashboardModel] = (
(JsPath \ "id").read[Long](exists) and
(
(JsPath \ "id").read[Long] and
(JsPath \ "name").readNullable[String]
).tupled.filter(
ValidationError("Name is already in use")
)
{ case (id, name) => isNameAvailable(name, id) }.map(t => t._2) and
(JsPath \ "containers").readNullable[List[UpdateContainerModel]] and
(JsPath \ "description").readNullable[String]) (UpdateDashboardModel.apply _ )
Personally I find the tupled.filter
syntax a little strange, but this is how I have been able to overcome the issue you're having.
Upvotes: 3