Pattern matching after update with ReactiveMongo - mongodb

I try to make pattern matching for checking error after an update document on mongodb with scala.
def update(id: BSONObjectID, post: Post): Future[WriteResult] =
collection.flatMap(_.update.one(BSONDocument("_id" -> id), BSONDocument(
f"$$set" -> BSONDocument(
"title" -> post.title,
"description" -> post.description
)
),
true))
this is my update function in productrepository, and this is my controller
def update (id: String) = Action.async(parse.json) { /*implicit request =>*/
val bsonId = BSONObjectID.parse(id)
_.body.validate[Post].map { post =>
postsRepo.update(bsonId.get, post).map {
case Some(post) => Ok(Json.toJson(post))
case _ => NotFound
// _ => Created
}
}.getOrElse(Future.successful(BadRequest("Invalid Format")))
}
I obtain this error:
constructor cannot be istantiated to expected type;
Found Some[A]
Required Reactivemongo.api.command.writeResult
My goal is to run pattern matching after update

If you have a look at IDE/compiler message, you can see that result of update operation is Future[UpdateWriteResult], so matching on Option in Future.map cannot be OK.
If you read the documentation, you will find a "error handling" section, explaining how to recover write result according DB code or message.
val done: Future[Unit] = future.map(_ => {}).recover {
case WriteResult.Code(11000) =>
// if the result is defined with the error code 11000 (duplicate error)
println("Match the code 11000")
case WriteResult.Message("Must match this exact message") =>
println("Match the error message")
case _ => ()
}
If you are trying to produce a specific response if nothing has been updated, it's not to be done with pattern matching, but just check UpdateWriteResult.n (number of updated docs) in .map.

Related

Handling errors as a Future[Result] in ReactiveMongo 16.6

How can I output mongoDB errors in a Result with ReactiveMongo (16.6)? I've spent virtually the whole day looking through samples but have not been able to achieve this as of yet. The error section of the documentation returns a Future[Unit] rather than a Future[Result]. And every other example/sample that I can find either is outdated or does not do this; example_1, example2
Here is what I would like to do:
def updateById(collName: String, id: BSONObjectID) = authAction.async(parse.json) { implicit request: Request[JsValue] =>
val oWriteJso = request.body.asOpt[JsObject]
lazy val qJso = Json.obj("_id" -> id)
val res = oWriteJso.map(
wJso => mongoRepo.update(collName)(qJso, wJso)().recoverWith {
case WriteResult.Code(11000) => Future.successful(BadRequest("it went bad"))
case _ => Future.successful(BadRequest("also bad"))
}
)
res
}
Of course with the function signature as recoverWith[U >: T](pf: PartialFunction[Throwable, Future[U]])(implicit executor: ExecutionContext): Future[U] this code above will return an error as it needs to return a Future[WriteResult]. But how then would I be able to put any error messages, codes, etc (from mongoDB) into a Result?
The documentation indicates how to recover a Future[WriteResult]:
.recover {
case WriteResult.Code(11000) =>
// if the result is defined with the error code 11000 (duplicate error)
println("Match the code 11000")
case WriteResult.Message("Must match this exact message") =>
println("Match the error message")
// ...
}
Thanks to Future combinators (not specific to ReactiveMongo), it can be used whatever is the type of the successful value to be lifted inside the Future.
def foo[T](future: Future[WriteResult], recoveredValue: => T)(success: WriteResult => T): Future[T] = future.map(success).recover {
case WriteResult.Code(11000) =>
// if the result is defined with the error code 11000 (duplicate error)
recoveredValue
case WriteResult.Message("Must match this exact message") =>
recoveredValue
}

"Redirect" to an external URL using play framework 2.7

I am trying to redirect to an external URL with some authorization code attached to it. I am using the "Redirect" function available in play framework using Scala. But instead of redirecting to the provided URL, the message in "Ok" gets printed and is not redirected
I am a beginner in Play and Scala. It is my understanding that an "Action" should send a "Result". This "Result" can either be "Ok" or a "Redirect". In the below code:
1) If I don't mention "Ok", there is a compile error
2) If I execute the code below, it doesn't get redirected, it just prints the message in "Ok"
// This is inside a controller
// it is defined as a GET in the routes file
def test = Action { implicit request =>
async {
await(userAuth.getAuth()) match{
case Some(userAuth) if <>
val url = <FunctionReturnsUrlwithcode>
Redirect(url)
case _ if flag
val url = <FunctionReturnsUrlwithcode>
Redirect(url)
}
Ok("Some message") // Without this , there is an error
}
I assume this is pseudocode, since that pattern matching isn't syntactically correct...
Your pattern matching isn't exhaustive. For example:
def test(): String = {
val x = Some(1)
x match {
case Some(y) if y == 2 => return "matched 1"
case _ if false == true => return "matched 2"
}
"matched nothing"
}
The above code will return "matched nothing", since 1 != 2 and false != true. There needs to be a default case, otherwise if any of the specified conditions aren't met it'll ignore the pattern matching altogether.
In your case, I'm guessing that neither of the conditions in your pattern matching are being met. Therefore, the pattern matching is skipped over and Ok(whatever) is returned - if you take out the Ok, your app will blow up since an Action must return a Result. Your code will also return the Ok if you write it like this:
def test = Action { implicit request =>
async {
await(userAuth.getAuth()) match{
case Some(userAuth) if <> =>
val url = <FunctionReturnsUrlwithcode>
Redirect(url)
case _ if flag =>
val url = <FunctionReturnsUrlwithcode>
Redirect(url)
case _ =>
Ok("Some message")
}
}
}
Also,
This "Result" can either be "Ok" or a "Redirect"
This is not true; it isn't limited to only two values. It can be a number of other things, such as NoContent, Created, BadRequest, InternalServerError, etc. (basically any valid HTTP status).
You can try the below:
def test: Action[AnyContent] = Action.async { implicit request =>
//your logic
async {
await(userAuth.getAuth()) match{
case Some(userAuth) if <> =>
val url = <FunctionReturnsUrlwithcode>
(Redirect(url, Map("traceid"-> Set("trace")))
case _ if flag =>
val url = <FunctionReturnsUrlwithcode>
(Redirect(url, Map("traceid"-> Set("trace")))
case _ =>
Ok("Some message")
}
}

How to check if case class parameter has value or not in Scala

I have a case class QueryParamsas follows:
case class QueryParams(
limit: Option[Integer] = None,
refresh: Option[Boolean] = None,
organisationalUnit: Option[String] = None)
These values limit,refresh,organisationalUnit are actually passed as query parameters in request url for play application.
I need to write a code to check if request URL contains any value for organisationalUnit and if yes I need to throw error .If no, I need to proceed with further operations.
Can anyone help me here
Options are quite good for this kind of thing:
val params: QueryParams = ???
params.organizationalUnit.foreach(_ => throw new Exception("your error message"))
In this way you'll throw only if organizationalUnit is defined. You can also express it as follows:
for (_ <- params.organizationalUnit) {
throw new Exception("your error message")
}
Or alternatively:
if (params.organizationalUnit.isDefined) {
throw new Exception("your error message")
}
The latter is probably the most readable, even though it may not be recognized as very idiomatic according to certain coding styles.
The answer from stefanobaghino is good but I prefer pattern matching for such cases:
params.organisationalUnit match {
case Some(_) => // processing
case _ => //logging
}
If you need other values you can match the whole instance
params match {
case QueryParams(Some(limit), Some(refresh), Some(organisationalUnit)) =>
case QueryParams(mayBeLimit, mayBeRefresh, Some(organisationalUnit)) =>
case _ =>
}

Handling update failure in Play 2.5 and ReactiveMongo

I have written simple rest API that will add music track data to mongodb.
I am able to write track successfully, however I would like to handle error case.
My code as below -
def saveTrack = Action.async(parse.json) {
import model.Track.trackFormat
implicit request =>
request.body.validate[Track].map { data =>
trackService.save(data).onComplete {
case Success(value) => Ok(Json.obj("message" -> Messages("track.save.ack")))
case Failure(e) => Ok(Json.obj("message" -> Messages("track.save.nack")))
}
}
Future.successful(Ok(Json.obj("message" -> Messages("track.save.fin"))))
}
Lets suppose there is some failure happen in trackService.save(data) then I want to return error code. I am wondering how to achieve that.
Thanks
Pari
def saveTrack = Action.async(parse.json) { implicit request =>
request
.body
.validate[Track]
.map { data =>
trackService.save(data).map { v =>
Ok(Json.toJson(Json.obj("message" -> Messages("ack"))))
}.recover {
case _ =>
Ok(Json.toJson(Json.obj("message" -> Messages("nack"))))
}
}.getOrElse {Future.successful(BadRequest(Json.obj("error" -> "bad json")))
}
}
Use recover to handle errors and getOrElse in case the json body is not matching your model.

Handling errors in ReactiveMongo

I am working on a simple RESTful web service using Play Framework 2.1.5 and ReactiveMongo 0.9 using ReactiveMongo Play plugin. It has been a long time since I used Play Framework for the last time. I am trying to insert a document using:
def create = Action(parse.json) { request =>
Async {
val coll = db.collection[JSONCollection](...)
val obj = Json.obj(
"username" -> ...,
...
)
users.insert(obj).map { err => err match {
case e if !e.ok => InternalServerError(Json.obj("result" -> 0, "error" -> e.message))
case _ => Ok(Json.obj("result" -> 1))
}}
}
}
I have expected that once the query execution fails (e.g. due to the duplicate value in an index), I will handle it without any problem. But it is working differently - in case of failure a DatabaseException is thrown instead of satisfying the Promise[LastError] with an appropriate value. What am I missing please?
When an exception happens in a future any calls to map will be ignored and the exception will be passed along the chain of futures.
Explicitly handling the exceptions in a chain of Futures can be done with recover and recoverWith. You can read more about it in the overview of futures in the scala-lang docs:
http://docs.scala-lang.org/overviews/core/futures.html#exceptions
Try this code-
def insert(coll: BSONCollection, doc: BSONDocument): Future[Unit] = {
val p = Promise[Unit]
val f = coll.insert(doc)
f onComplete {
case Failure(e) => p failure (e)
case Success(lastError) => {
p success ({})
}
}
p.future
}
I hope this simplifies your need...
def create = Action (parse.json) { request =>
Async {
val coll = db.collection[JSONCollection](...)
val obj = Json.obj ("username" -> ...)
users.insert(obj).map {
case ins if ins.ok => OK (...)
case ins => InternalServerError (...)
} recover {
case dex: DatabaseException =>
log.error(..)
InternalServerEror(...)
case e: Throwable =>
log.error (..)
InternalServerError (...)
}
}
}