toddler
toddler

Reputation: 45

Could someone explain how is the this code executing?

How is the createUser or updateUser using the unmarshalValueJs function and passing arguments.

    package controllers.user

import play.api._
import play.api.mvc._
import play.api.libs.json._
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax._
import services.user.UserServiceComponent
import domain.user.User

trait UserController extends Controller {
    self: UserServiceComponent =>0

def emailAlreadyExists(implicit reads: Reads[String]) =
    Reads[String](js => reads.reads(js).flatMap { e =>
      userService.tryFindByEmail(e).map(_ => JsError("error.custom.emailAlreadyExists")).getOrElse(JsSuccess(e))
    })

implicit val userReads = (__ \ "email").read[String](email andKeep emailAlreadyExists)
                                       .map(resource => UserResource(resource))

implicit val userWrites = new Writes[User] {
    override def writes(user: User): JsValue = {
        Json.obj(
            "id" -> user.id,
            "email" -> user.email
        )
    }
}

What create User passes to the unmarshalJsValue? Where does resource come from?

def createUser = Action(parse.json) {request =>
    unmarshalJsValue(request) { resource: UserResource =>
        val user = User(Option.empty, resource.email)
        userService.createUser(user)
        Created
    }
}

def updateUser(id: Long) = Action(parse.json) {request =>
    unmarshalJsValue(request) { resource: UserResource =>
        val user = User(Option(id), resource.email)
        userService.updateUser(user)
        NoContent
    }
}

def findUserById(id: Long) = Action {
    val user = userService.tryFindById(id)
    if (user.isDefined) {
        Ok(Json.toJson(user))
    } else {
        NotFound
    }
}

def deleteUser(id: Long) = Action {
    userService.delete(id)
    NoContent
}

What is R over here? What get passed back to the createUser?

    def unmarshalJsValue[R](request: Request[JsValue])(block: R => Result)(implicit rds : Reads[R]): Result =
        request.body.validate[R](rds).fold(
            valid = block,
            invalid = e => {
                val error = e.mkString
                Logger.error(error)
                BadRequest(error)
            }
        )

}

case class UserResource(email: String)

Upvotes: 0

Views: 73

Answers (1)

acjay
acjay

Reputation: 36491

R is a type variable. The definition def unmarshalJsValue[R](request: Request[JsValue])(block: R => Result)(implicit rds : Reads[R]) reads:

  • I'm a method called unmarshallJsValue.

  • I require a type parameter that could potentially be anything.

  • I require a value of Request[JsValue] (presumably a request contianin a JSON entity).

  • I require a function that, given some value of type R, produces a Result.

  • I require a value to be in implicit scope that is a Reads for type R. In other words, I need a way to convert a JsValue to an R, whatever R might happen to be. (If you look at the docs for Reads, you'll notice that its only method, reads, has this effect.)

To put it all together, entire function just says, give me some block of code that produces a Result from some piece of data I know how to convert to from JSON. The body of the function simply attempts to convert the JSON to the desired type (known as unmarshalling). If this succeeds, it runs the block. Otherwise, you get a BadRequest client error.

The compiler can't know whether the JSON that gets provided in real life by the client will be convertible to some type R, but it can require a JSON -> R converter, and some error handling code if it fails on real data.

Upvotes: 1

Related Questions