my function to insert person:
def InsertPerson = Action(BodyParsers.parse.json) { request =>
val b = request.body.validate[Person]
b.fold(
//errorForm => Future.successful(Ok(views.html.index(errorForm, Seq.empty[User]))),
errors => {
BadRequest(Json.obj("status" -> "error", "message" -> JsError.toJson(errors)))
},
person => {
val f = ApplTagService.insertPerson(person)
f.map(res => "User successfully added").recover {
case ex: Error => ex.getMessage
}
f onFailure {
case t => println("An error has occured: " + t.getMessage)
}
Ok(Json.obj("status" -> f.toString() ))
})}
I send Json from CI to scala for insert person, then I want to give an alert if that insert is success or not, I need to save ex.getMessage or t.getMessage on a variable and send it to CI
what I get now on f.toString() in CI is scala.concurrent.impl.Promise$DefaultPromise#793e6657
How can I show ex.getMessage or t.getMessage in CI?
You can use Action.async(BodyParsers.parse.json) instead of Action(BodyParsers.parse.json) when you want to return a Future[Result] instead of a Result
In this case your match will look like this
b.fold(
errors => Future.successful(BadRequest(Json.obj("status" -> "error", "message" -> JsError.toJson(errors)))),
person => {
val f = ApplTagService.insertPerson(person)
val fMessage = f.map(res => "User successfully added").recover {
case ex: Error => ex.getMessage
}
f onFailure {
case t => println("An error has occured: " + t.getMessage)
}
fMessage.map(message => Ok(Json.obj("status" -> message )))
})
Related
I have this actor with the following recieve code:
override def receive: Receive = {
case RefreshAccessTokenRequest(accountId, Some(refreshToken), _, _) =>
concurApi.refreshAccessToken(accountId, refreshToken) onComplete {
case Success(refreshTokenResult) =>
context.parent ! ConcurResultCallbackInformation(accountId, Right(refreshTokenResult))
accountService.updateAccountOAuth2Credentials(accountId, refreshTokenResult.toOAuth2Credentials) recover {
case ex => logger.error("Error updating account Id: " + accountId + " with new refresh token. message: " + ex.getMessage)
}
context.stop(self)
case Failure(t) =>
context.parent ! ConcurResultCallbackInformation(accountId, Left(t))
context.stop(self)
}
case Cancel => context.stop(self)
case x => logger.error(s"ConcurAccessTokenActor: Got an unknown message $x")
}
I am getting a warning on the bolded part which says:
Warning:(18, 42) a type was inferred to be AnyVal; this may indicate a programming error.
case Success(refreshTokenResult) =>
this is the refreshAccessToken function:
def refreshAccessToken(accountId: String, refreshToken: String)(implicit context: ExecutionContext) : Future[ConcurRefreshTokenResult] = {
val body = s"client_id=${sandboxClientId}"
val futureRes = ws.url(sandboxRefreshTokenFullUrl).withHeaders("Content-Type" -> "application/x-www-form-urlencoded").post(body)
futureRes.flatMap { res =>
res.status match {
case 200 =>
val extract = Try {
res.body.fromJson[ConcurRefreshTokenResult]
}.recover { case ex =>
throw ex
}
Promise.fromTry(extract).future
case 400 =>
Future.failed(new RuntimeException(s"refresh Access Token failed with status 400, and body: ${res.body}"))
case x =>
Future.failed(new RuntimeException(s"refresh Access Token failed with status $x, and body: ${res.body}"))
}
}
}
Any ideas?
Ok I got it..
The warning came from this code part:
case Success(refreshTokenResult) =>
context.parent ! ConcurResultCallbackInformation(accountId, Right(refreshTokenResult))
accountService.updateAccountOAuth2Credentials(accountId, refreshTokenResult.toOAuth2Credentials) recover {
case ex => logger.error("Error updating account Id: " + accountId + " with new refresh token. message: " + ex.getMessage)
}
context.stop(self)
the 'updateAccountOAuth2Credentials' function returns Future[Boolean] yet the recover part returns Unit so it interperts it to AnyVal
I think the only thing that is confusing is the location of the warning arrow since this function appears in the middle between 2 other functions, and has no effects on the return type of the entire case block
Hi in my scala application I want to return a Seq[Model] to my Frontend.
def getContentComponentUsageSearch: Action[AnyContent] = Action.async { implicit request =>
println(request.body.asJson)
request.body.asJson.map(_.validate[StepIds] match {
case JsSuccess(stepIds, _) =>
println("VALIDE SUCCESS -------------------------------")
var templates: Seq[Future[Option[ProcessTemplatesModel]]] = Future.sequence(stepIds.s.map(s => {
processTemplateDTO.getProcessStepTemplate(s.processStep_id).flatMap(stepTemplate => {
templates :+ processTemplateDTO.getProcessTemplate(stepTemplate.get.processTemplate_id.get)
})
}))
templates.map(done => {
Future.sequence(templates).map(a => {
Ok(Json.obj("id" -> a))
})
})
case JsError(_) =>
println("NOT VALID -------------------------------")
Future.successful(BadRequest("Process Template not create client"))
case _ => Future.successful(BadRequest("Process Template create client"))
}).getOrElse(Future.successful(BadRequest("Process Template create client")))
}
I need to wait until its finished and return then. What would be a good way to achieve this?
thanks in advance.
UPDATE:
at the moment I try this:
val fList: List[Future[ProcessTemplatesModel]] +: stepIds.s.map(s => {
processTemplateDTO.getProcessStepTemplate(s.processStep_id).map(stepTemplate => {
processTemplateDTO.getProcessTemplate(stepTemplate.get.processTemplate_id.get).map(a => {
a.get
})
})
})
Future.successful( Ok(Json.obj("id" -> fList)))
Issue in this case is the +: I think.
I think you only have to rewrite this part:
templates.map(done => {
Future.sequence(templates).map(a => {
Ok(Json.obj("id" -> a))
})
})
If you would do something like this:
val futSeqOpt: Future[Seq[Option[ProcessTemplatesModel]]] = Future.sequence(templates)
val futSeq: Future[Seq[ProcessTemplatesModel]] = futSeqOpt.map(_.getOrElse("defaultValue"))
futSeq.map(seq => Ok(Json.toJson(seq)))
it should work. (I only added the types for demonstration).
I want to create a registration Action in play framework application. The question is how to implement check for already existing email?
object AccountController extends Controller {
case class AccountInfo(email: String, password: String)
val accountInfoForm = Form(
mapping(
"email" -> email,
"password" -> nonEmptyText
)(AccountInfo.apply)(AccountInfo.unapply)
)
def createAccount = Action {
implicit request => {
accountInfoForm.bindFromRequest fold (
formWithErrors => {
Logger.info("Validation errors")
BadRequest(accountInfoForm.errorsAsJson)
},
accountInfo => {
AccountService.findByEmail(accountInfo.email) map {
case accountOpt: Option[Account] => accountOpt match {
case Some(acc) => {
Logger.info("Email is already in use")
BadRequest(Json.toJson(Json.obj("message" -> "Email is already in use")))
}
case None => {
Logger.info("Created account")
val account = Account.createAccount(accountInfo)
val accountToSave = account copy (password=Account.encryptPassword(accountInfo.password))
Created(Json.toJson(AccountService.add(accountToSave)))
}
}
case _ => {
Logger.info("DB connection error")
InternalServerError(Json.toJson(Json.obj("message" -> "DB connection error")))
}
}
Ok("Ok")
})
}
}
}
AccountService.findByEmail - returns Future[Option[Account]]
Unfortunately my solution always return 'Ok'
Since findByEmail returns a Future, you should use Action.async instead. This is because when you map the Future[Option[Account]], you're mapping it to a Future[Result] instead of Result. Note how I had to use Future.successful with formWithErrors to keep the return type the same.
When returning a simple Result use Action. When returning a Future[Result], use Action.async.
def createAccount = Action.async {
implicit request => {
accountInfoForm.bindFromRequest fold (
formWithErrors => {
Logger.info("Validation errors")
Future.successful(BadRequest(accountInfoForm.errorsAsJson))
},
accountInfo => {
AccountService.findByEmail(accountInfo.email) map {
case accountOpt: Option[Account] => accountOpt match {
case Some(acc) => {
Logger.info("Email is already in use")
BadRequest(Json.toJson(Json.obj("message" -> "Email is already in use")))
}
case None => {
Logger.info("Created account")
val account = Account.createAccount(accountInfo)
val accountToSave = account copy (password=Account.encryptPassword(accountInfo.password))
Created(Json.toJson(AccountService.add(accountToSave)))
}
}
case _ => {
Logger.info("DB connection error")
InternalServerError(Json.toJson(Json.obj("message" -> "DB connection error")))
}
}
})
}
}
I'm trying to write a DRY CRUD restful service using PlayFramework. Here's the code for it.
def crudUsers(operation: String) = Action(parse.json) { request =>
(request.body).asOpt[User].map { jsonUser =>
try {
DBGlobal.db.withTransaction {
val queryResult = operation match {
case "create" =>
UsersTable.forInsert.insertAll(jsonUser)
Json.generate(Map("status" -> "Success", "message" -> "Account inserted"))
case "update" =>
val updateQ = UsersTable.where(_.email === jsonUser.email.bind).map(_.forInsert)
println(updateQ.selectStatement)
updateQ.update(jsonUser)
Json.generate(Map("status" -> "Success", "message" -> "Account updated"))
case "retrieve" =>
val retrieveQ = for(r <- UsersTable) yield r
println(retrieveQ.selectStatement)
Json.generate(retrieveQ.list)
case "delete" =>
val deleteQ = UsersTable.where(_.email === jsonUser.email)
deleteQ.delete
Json.generate(Map("status" -> "Success", "message" -> "Account deleted"))
}
Ok(queryResult)
}
} catch {
case _ =>
val errMsg: String = operation + " error"
BadRequest(Json.generate(Map("status" -> "Error", "message" -> errMsg)))
}
}.getOrElse(BadRequest(Json.generate(Map("status" -> "Error", "message" -> "error"))))
}
}
I've noticed that update, delete and create operations work nicely. However the retrieve operation fails with For request 'GET /1/users' [Invalid Json]. I'm pretty sure this is because the JSON parser is not tolerant of a GET request with no JSON passed in the body.
Is there a way to special case the GET/Retrieve operation without losing losing the DRY approach I've started here?
My guess would be that you split your methods so that you can create a different routes for the ones with and without body.
It seems that the design of the code would not work even if the empty string was parsed as JSON. The map method would not be executed because there would be no user. That would cause the match on operation to never be executed.
Update
Since you mentioned DRY, I would refactor it into something like this:
type Operations = PartialFunction[String, String]
val operations: Operations = {
case "retrieve" =>
println("performing retrieve")
"retrieved"
case "list" =>
println("performing list")
"listed"
}
def userOperations(user: User): Operations = {
case "create" =>
println("actual create operation")
"created"
case "delete" =>
println("actual delete operation")
"updated"
case "update" =>
println("actual update operation")
"updated"
}
def withoutUser(operation: String) = Action {
execute(operation, operations andThen successResponse)
}
def withUser(operation: String) = Action(parse.json) { request =>
request.body.asOpt[User].map { user =>
execute(operation, userOperations(user) andThen successResponse)
}
.getOrElse {
errorResponse("invalid user data")
}
}
def execute(operation: String, actualOperation: PartialFunction[String, Result]) =
if (actualOperation isDefinedAt operation) {
try {
DBGlobal.db.withTransaction {
actualOperation(operation)
}
} catch {
case _ => errorResponse(operation + " error")
}
} else {
errorResponse(operation + " not found")
}
val successResponse = createResponse(Ok, "Success", _: String)
val errorResponse = createResponse(BadRequest, "Error", _: String)
def createResponse(httpStatus: Status, status: String, message: String): Result =
httpStatus(Json.toJson(Map("status" -> status, "message" -> message)))
Integrating redis with my Scala application using Akka but for some reason it does not receive any messages. I can confirm that redis does have a ton of traffic on its side by opening the redis-cli on the command line.
After a pSubscribe it receives: subscribed to * and count = 1
My guess is that it might be related to the way Akka is set up to receive callbacks. I had to strip out Scala Actors in the scala-redis lib and replace them with Akka actors due to some conflicts.
Here's the code:
The Subscriber Actor
class Subscriber(client: RedisClient) extends Actor {
var callback: PubSubMessage => Any = { m => }
def receive: Receive = {
case Subscribe(channels) =>
client.subscribe(channels.head, channels.tail: _*)(callback)
case pSubscribe(channels) =>
client.pSubscribe(channels.head, channels.tail: _*)(callback)
case pSubscribeAll(channels) =>
Logger.info("Subscribing to all channels")
client.pSubscribe(channels.head, channels.tail: _*)(callback)
case Register(cb) =>
Logger.info("Callback is registered")
callback = cb
case Unsubscribe(channels) =>
client.unsubscribe(channels.head, channels.tail: _*)
case UnsubscribeAll =>
client.unsubscribe
}
}
Initializing the Subscriber
class RelaySub extends Actor {
// important config values
val system = ActorSystem("pubsub")
val conf = play.api.Play.current.configuration
val relayPubHost = conf.getString("relays.redis.host").get
val relayPubPort = conf.getInt("relays.redis.port").get
val rs = new RedisClient(relayPubHost, relayPubPort)
val s = system.actorOf(Props(new Subscriber(rs)))
s ! Register(callback)
s ! pSubscribeAll(Array("*"))
Logger.info("Engine Relay Subscriber has started up")
def receive: Receive = {
case Register(callback) =>
}
def callback(pubsub: PubSubMessage) = pubsub match {
case S(channel, no) => Logger.info("subscribed to " + channel + " and count = " + no)
case U(channel, no) => Logger.info("unsubscribed from " + channel + " and count = " + no)
case M(channel, msg) =>
msg match {
// exit will unsubscribe from all channels and stop subscription service
case "exit" =>
Logger.info("unsubscribe all ... no handler yet ;)")
// message "+x" will subscribe to channel x
case x if x startsWith "+" =>
Logger.info("subscribe to ... no handler yet ;)")
// message "-x" will unsubscribe from channel x
case x if x startsWith "-" =>
Logger.info("unsubscribe from ... no handler yet ;)")
// other message receive
case x =>
Logger.info("Engine: received redis message")
val channelVars = channel.split(".").toArray[String]
if(channelVars(0)!=Engine.instanceID)
channelVars(1) match {
case "relay" =>
EngineSyncLocal.constructRelay(channel, msg)
case _ =>
Logger.error("Engine: received unknown redis message")
}
}
}
}
Thanks for your help!
I found the problem. It appears to be a bug in the scala-redis client.
I added some logging in the consumer class and began receiving Engine: weird message errors which means that it doesn't recognize the incoming traffic. I'll contact the author and put in a pull request.
The code:
class Consumer(fn: PubSubMessage => Any) extends Runnable {
def start () {
val myThread = new Thread(this) ;
myThread.start() ;
}
def run {
whileTrue {
asList match {
case Some(Some(msgType) :: Some(channel) :: Some(data) :: Nil) =>
Logger.info("Engine: redis traffic")
msgType match {
case "subscribe" | "psubscribe" => fn(S(channel, data.toInt))
case "unsubscribe" if (data.toInt == 0) =>
fn(U(channel, data.toInt))
break
case "punsubscribe" if (data.toInt == 0) =>
fn(U(channel, data.toInt))
break
case "unsubscribe" | "punsubscribe" =>
fn(U(channel, data.toInt))
case "message" | "pmessage" =>
fn(M(channel, data))
case x => throw new RuntimeException("unhandled message: " + x)
}
case _ => Logger.error("Engine: weird redis message")
}
}
}
}
case x => throw new RuntimeException("unhandled message: " + x)
}
case Some(Some("pmessage")::Some(pattern)::Some(channel):: Some(message)::Nil)=>
fn(M(channel, message))
asList match is missing a case