Could someone explain how is the this code executing? - scala

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)

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.

Related

How can I serialize Sangria responses with json4s and Akka HTTP?

I'm working through a slight variation of Sangria's Getting Started, using Akka HTTP. I'm attempting to use json4s-jackson as the serializaltion lib, but am running in to some trouble getting the response I want.
Specifically, the serialized response I get is the JSON version of the (StatusCode, Node) tuple:
{
"_1": {
"allowsEntity": true,
"defaultMessage": "OK",
"intValue": 200,
"reason": "OK"
},
"_2": {
"data": {
"foo": {
"id": "1",
"name": "Foo"
}
}
}
}
The data portion is correct, but obviously I just want that and not the first element of the serialized tuple.
I'm using akka-http-json4s, so my trait with route looks like:
case class GraphQlData(query: String, operation: Option[String])
trait FooController {
import de.heikoseeberger.akkahttpjson4s.Json4sSupport._
implicit val serialization = jackson.Serialization
implicit val formats = DefaultFormats
val fooRoutes = post {
entity(as[GraphQlData]) { data =>
QueryParser.parse(data.query) match {
// query parsed successfully, time to execute it!
case Success(queryAst) =>
complete {
Executor
.execute(
SchemaDefinition.FooSchema,
queryAst,
new FooService,
operationName = data.operation
)
.map(OK -> _)
.recover {
case error: QueryAnalysisError => BadRequest -> error.resolveError
case error: ErrorWithResolver => InternalServerError -> error.resolveError
}
}
// can't parse GraphQL query, return error
case Failure(error) =>
complete(BadRequest -> error.getMessage)
}
}
}
implicit def executionContext: ExecutionContext
}
For the life of me I can't figure out what's wrong. I've been looking at sangria-akka-http-example but it seems to be exactly the same, with the exception of using spray-json instead of json4s.
Ideas? Thanks!
Ah, figured it out. I neglected to add
import sangria.marshalling.json4s.jackson._
to the trait defining the route. Adding it does the trick.
Just wanted to provide a quick update to this answer for the full GraphQLRequest. Now the variables are included in the request.
import de.heikoseeberger.akkahttpjson4s.Json4sSupport
import org.json4s._
import org.json4s.JsonAST.JObject
import sangria.marshalling.json4s.jackson._
case class GQLRequest(query: String, operationName: Option[String], variables: JObject)
trait SomeJsonSupport extends Json4sSupport {
implicit val serialization = jackson.Serialization
implicit val formats = DefaultFormats
}
trait GraphQLResource extends SomeJsonSupport{
implicit val timeout:Timeout
implicit val system:ActorSystem
import system.dispatcher
def graphqlRoute: Route =
(post & path("graphql")) {
entity(as[GQLRequest]) { requestJson =>
println(s"This is the requestJson = $requestJson")
graphQLEndpoint(requestJson)
}
} ~
get {
println(s"This is working")
getFromResource("graphiql.html")
}
def graphQLEndpoint(requestJson: GQLRequest): Route = {
val route = QueryParser.parse(requestJson.query) match {
case Success(query) =>
println(s"This is the query $query")
val vars = requestJson.variables match {
case jObj:JObject => jObj
case _ => JObject(List.empty)
}
val futureJValue = Executor.execute(clientSchema,
query,
NclhGqlRequest(this),
operationName = requestJson.operationName,
variables = vars)
val futureTupleStatusCodeJValue = futureJValue.map(OK -> _).recover {
case error: QueryAnalysisError => BadRequest -> error.resolveError
case error: ErrorWithResolver => InternalServerError -> error.resolveError
}
complete(futureTupleStatusCodeJValue)
case Failure(error) =>
complete(BadRequest, error.getMessage)
}
route
}

How to transform submitted json in Play 2.0?

I'm trying to build a REST API using play 2.0. I have a User case class that contains some fields (like username & password) that shouldn't be updatable by the updateMember method.
Is there a good, functional way, of dealing with multiple Options somehow, because request.body.asJson returns an Option[JsValue], and my user lookup also returns an Option:
package controllers.api
import org.joda.time.LocalDate
import play.api.Play.current
import play.api.db.slick.{DB, Session}
import play.api.mvc._
import play.api.libs.json._
import play.api.libs.functional.syntax._
import models.{Gender, User, UserId}
import repositories.UserRepository
object Member extends Controller {
def updateMember(id: Long) = Action {
DB.withSession {
implicit session: Session =>
val json: JsValue = request.body.asJson // how to deal with this?
val repository = new UserRepository
repository.findById(new UserId(id)).map {
user =>
def usernameAppender = __.json.update(
__.read[JsObject].map { o => o ++ Json.obj("username" -> user.username) }
)
json.transform(usernameAppender) // transform not found
Ok("updated")
}.getOrElse(NotFound)
}
}
}
I could move the map call to where I try to parse the request, but then inside there I guess I'd need another map over the user Option like I already have. So in that style, I'd need a map per Option.
Is there a better way of dealing with multiple Options like this in FP?
You're basically dealing with a nested monad, and the main tool for working with such is flatMap, particularly if both options being None has the same semantic meaning to your program:
request.body.asJson.flatMap { requestJson =>
val repository = new UserRepository
repository.findById(new UserId(id)).map { user =>
def usernameAppender = __.json.update(
__.read[JsObject].map { o => o ++ Json.obj("username" -> user.username) }
)
requestJson.transform(usernameAppender)
Ok("updated") // EDIT: Do you not want to return the JSON?
}
}.getOrElse(NotFound)
But it might also be the case that your Nones have different meanings, in which case, you probably just want to pattern match, and handle the error cases separately:
request.body.asJson match {
case Some(requestJson) =>
val repository = new UserRepository
repository.findById(new UserId(id)).map { user =>
def usernameAppender = __.json.update(
__.read[JsObject].map { o => o ++ Json.obj("username" -> user.username) }
)
requestJson.transform(usernameAppender)
Ok("updated")
}.getOrElse(NotFound)
case None => BadRequest // Or whatever you response makes sense for this case
}

Secured trait causing spec2 unit test compilation errors

I've implemented authorization for my Play Framework (version 2.3.5) application as per the official security documentation:
trait Secured {
def username(request: RequestHeader) = request.session.get(Security.username)
def onUnauthorized(request: RequestHeader) = Results.Redirect(routes.Login.index)
def withAuth(f: => String => Request[AnyContent] => Result) = {
Security.Authenticated(username, onUnauthorized) { user =>
Action(request => f(user)(request))
}
}
}
However, when I decorate my controller actions with the withAuth function like so:
def export = withAuth { username => implicit request =>
val csv = csv()
Ok(csv)
.as("text/csv")
.withHeaders(
CONTENT_DISPOSITION -> ("attachment; filename=export.csv"),
CONTENT_LENGTH -> csv.length.toString
)
}
I get compilation errors in my controller specs2 unit tests:
"export data as CSV" in {
val controller = new controllers.DataController
val result = controller.export().apply(FakeRequest())
headers(result) must havePair("Content-Disposition" -> ("attachment; filename=export.csv"))
}
The call to the headers test helper function fails with the following compiler error message:
type mismatch; found : play.api.libs.iteratee.Iteratee[Array[Byte],play.api.mvc.Result] required: scala.concurrent.Future[play.api.mvc.Result]
Am I doing something wrong? I've tried some of the remedies suggested by other Stackoverflow users but they all seem to rely on setting the body type, but my actions do not have a body type.
It looks as though the call to withAuth obscures some of the types of the Action being wrapped so maybe there is a more type-safe way of expressing the Secured trait?
Try changing this:
val result = controller.export().apply(FakeRequest())
into this:
val result = controller.export().apply(FakeRequest()).run
And then it should work.

How to query OrientDB asynchronously from Play controller?

I am writing a Play (2.2) controller in Scala, which should return the result of a query against OrientDB. Now, I have succeeded in writing a synchronous version of said controller, but I'd like to re-write it to work asynchronously.
My question is; given the below code (just put together for demonstration purposes), how do I re-write my controller to interact asynchronously with OrientDB (connecting and querying)?
import play.api.mvc.{Action, Controller}
import play.api.libs.json._
import com.orientechnologies.orient.`object`.db.OObjectDatabasePool
import java.util
import com.orientechnologies.orient.core.sql.query.OSQLSynchQuery
import scala.collection.JavaConverters._
object Packages extends Controller {
def packages() = Action { implicit request =>
val db = OObjectDatabasePool.global().acquire("http://localhost:2480", "reader", "reader")
try {
db.getEntityManager().registerEntityClass(classOf[models.Package])
val packages = db.query[util.List[models.Package]](new OSQLSynchQuery[models.Package]("select from Package")).asScala.toSeq
Ok(Json.obj(
"packages" -> Json.toJson(packages)
))
}
finally {
db.close()
}
}
}
EDIT:
Specifically, I wish to use OrientDB's asynchronous API. I know that asynchronous queries are supported by the API, though I'm not sure if you can connect asynchronously as well.
Attempted Solution
Based on Jean's answer, I've tried the following asynchronous implementation, but it fails due to a compilation error value execute is not a member of Nothing possible cause: maybe a semicolon is missing before 'value execute'?:
def getPackages(): Future[Seq[models.Package]] = {
val db = openDb
try {
val p = promise[Seq[models.Package]]
val f = p.future
db.command(
new OSQLAsynchQuery[ODocument]("select from Package",
new OCommandResultListener() {
var acc = List[ODocument]()
#Override
def result(iRecord: Any): Boolean = {
val doc = iRecord.asInstanceOf[ODocument]
acc = doc :: acc
true
}
#Override
def end() {
// This is just a dummy
p.success(Seq[models.Package]())
}
// Fails
})).execute()
f
}
finally {
db.close()
}
}
One way could be to start a promise, return the future representing the result of that promise, locally accumulate the results as they come and complete de promise ( thus resolving the future ) when orient db notifies you that the command has completed.
def executeAsync(osql: String, params: Map[String, String] = Map()): Future[List[ODocument]] = {
import scala.concurrent._
val p = promise[List[ODocument]]
val f =p.future
val req: OCommandRequest = database.command(
new OSQLAsynchQuery[ODocument]("select * from animal where name = 'Gipsy'",
new OCommandResultListener() {
var acc = List[ODocument]()
#Override
def result(iRecord:Any):Boolean= {
val doc = iRecord.asInstanceOf[ODocument]
acc=doc::acc
true
}
#Override
def end() {
p.success(acc)
}
}))
req.execute()
f
}
Be careful though, to enable graph navigation and field lazy loading, orientdb objects used to keep an internal reference to the database instance they were loaded from ( or to depend on a threadlocal database connected instance ) for lazily loading elements from the database. Manipulating these objects asynchronously may result in loading errors. I haven't checked changes from 1.6 but that seemed to be deeply embedded in the design.
It's as simple as wrapping the blocking call in a Future.
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import scala.concurrent.Future
object Packages extends Controller {
def packages = Action.async { implicit request =>
val db = OObjectDatabasePool.global().acquire("http://localhost:2480", "reader", "reader")
db.getEntityManager().registerEntityClass(classOf[models.Package])
val futureResult: Future[Result] = Future(
db.query[util.List[models.Package]](new OSQLSynchQuery[models.Package]("select from Package")).asScala.toSeq
).map(
queryResult => Ok(Json.obj("packages" -> Json.toJson(packages)))
).recover {
// Handle each of the exception cases legitimately
case e: UnsupportedOperationException => UnsupportedMediaType(e.getMessage)
case e: MappingException => BadRequest(e.getMessage)
case e: MyServiceException => ServiceUnavailable(e.toString)
case e: Throwable => InternalServerError(e.toString + "\n" + e.getStackTraceString)
}
futureResult.onComplete { case _ =>
db.close()
}
futureResult
}
}
Note that I did not compile the code. There is a lot of room to improve the code.

Comparing type mapped values in Slick queries

Consider the Favorites table object below, we want to write a query to find Favorites by their type (defined below). We have also defined a Typemapper, to map a FavoriteType to a String for the database
import scala.slick.driver.PostgresDriver.simple._
//Other imports have been omitted in this question
object Favorites extends Table[Favorite]("favorites") {
// Convert the favoriteTypes to strings for the database
implicit val favoriteMapping: TypeMapper[FavorietType] = MappedTypeMapper.base[FavorietType, String](
favType => FavorietType.values.find(_ == favType).get.mapping,
mapping => FavorietType.values.find(_.mapping == mapping).get
)
def favoriteType = column[FavoriteType]("type")
//other columns here
This is the query I want to write (however it does not compile)
def queryByFavoriteType(ftype : FavoriteType)(implicit s: Session) = {
for(
f <- Favorieten if f.favoriteType === ftype
) yield f
}
}
Here I have defined de different FavoriteType objects (this is outside the Favorieten Object)
sealed case class FavorietType(mapping: String) {
override def toString = mapping.capitalize
}
object FavoriteType {
object Exam extends FavoriteType("examen")
object Topic extends FavoriteType("onderwerp")
object Paper extends FavoriteType("profielwerkstuk")
val values = Seq(Exam , Topic , Paper )
}
The problem I have here is that the query does not compile:
value === is not a member of scala.slick.lifted.Column[models.gebruiker.FavorietType]
It appears that === can not be used to compare a User-defined type, is this true? Is there an alternative way to do this?
Edit
Related issue: before I had my TypeMapper without explicit type, it was defined as implicit val favoriteMapping = MappedTypeMapper.base[FavorietType, String]( ...
When I would write a query that would compare a FavoriteType.Exam (for example) such as
def queryByFavoriteExam()(implicit s: Session) = {
for(f <- Favorieten if f.favorietType === FavorietType.Exam) yield f
}
This would result in the error could not find implicit value for evidence parameter of type scala.slick.lifted.TypeMapper[models.gebruiker.FavorietType.Exam.type]
The solution for this is the same as the one presented below
When in doubt with Slick, go check out the unit tests. After reading their docs on mapping in a custom type and then looking at their unit tests, I got your query code to compile by changing it to:
def queryByFavoriteType(ftype : FavoriteType)(implicit s: Session) = {
for(f <- Favorites if f.favoriteType === (ftype:FavoriteType)) yield f
}
Also, I had imported the H2Driver just to get things to compile (import scala.slick.driver.H2Driver.simple._). I was assuming that you also had imported whatever driver it is that you need for your db.
EDIT
My full code example is as follows:
import scala.slick.driver.PostgresDriver.simple._
import scala.slick.session.Session
sealed case class FavoriteType(mapping: String) {
override def toString = mapping.capitalize
}
case class Favorite(ft:FavoriteType, foo:String)
object FavoriteType {
object Exam extends FavoriteType("examen")
object Topic extends FavoriteType("onderwerp")
object Paper extends FavoriteType("profielwerkstuk")
val values = Seq(Exam , Topic , Paper )
}
object Favorites extends Table[Favorite]("favorites") {
// Convert the favoriteTypes to strings for the database
implicit val favoriteMapping = MappedTypeMapper.base[FavoriteType, String](
{favType => FavoriteType.values.find(_ == favType).get.mapping},
{mapping => FavoriteType.values.find(_.mapping == mapping).get}
)
def favoriteType = column[FavoriteType]("type")
def foo = column[String]("foo")
def * = favoriteType ~ foo <> (Favorite.apply _, Favorite.unapply _)
def queryByFavoriteType(ftype : FavoriteType)(implicit s: Session) = {
for(f <- Favorites if f.favoriteType === (ftype:FavoriteType)) yield f
}
}