Unexpected scala.MatchError - scala

I have a method with the following definition :
def userPosts(userId: String): Future[List[Post]]
I would like to have a method which returns cached date or calls userPosts if there is no cached data. I've prepared something like this:
def cachedUserPosts(userId: String): Future[List[Post]] = {
for {
cached <- bucket.get[List[Post]]("posts_" + userId)
} yield {
cached match {
case Some(list) => list
case None => userPosts(userId) match {
case list:List[Post] => list
}
}
}
}
I have an error:
[error] play - Cannot invoke the action, eventually got an error: scala.MatchError: scala.concurrent.impl.Promise$DefaultPromise#23cf4a0c (of class scala.concurrent.impl.Promise$DefaultPromise)
play.api.Application$$anon$1: Execution exception[[MatchError: scala.concurrent.impl.Promise$DefaultPromise#23cf4a0c (of class scala.concurrent.impl.Promise$DefaultPromise)]]
at play.api.Application$class.handleError(Application.scala:296) ~[play_2.11-2.3.6.jar:2.3.6]
at play.api.DefaultApplication.handleError(Application.scala:402) [play_2.11-2.3.6.jar:2.3.6]
at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$3$$anonfun$applyOrElse$4.apply(PlayDefaultUpstreamHandler.scala:320) [play_2.11-2.3.6.jar:2.3.6]
at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$3$$anonfun$applyOrElse$4.apply(PlayDefaultUpstreamHandler.scala:320) [play_2.11-2.3.6.jar:2.3.6]
at scala.Option.map(Option.scala:145) [scala-library-2.11.2.jar:na]
Caused by: scala.MatchError: scala.concurrent.impl.Promise$DefaultPromise#23cf4a0c (of class scala.concurrent.impl.Promise$DefaultPromise)
at models.Post$$anonfun$fastUserPosts$1.apply(Post.scala:130) ~[classes/:na]
at models.Post$$anonfun$fastUserPosts$1.apply(Post.scala:126) ~[classes/:na]
at scala.util.Success$$anonfun$map$1.apply(Try.scala:236) ~[scala-library-2.11.2.jar:na]
at scala.util.Try$.apply(Try.scala:191) ~[scala-library-2.11.2.jar:na]
at scala.util.Success.map(Try.scala:236) ~[scala-library-2.11.2.jar:na]
2015-07-26 19:33:24.211 INFO net.spy.memcached.auth.AuthThread: Authenticated to 192.168.10.42/192.168.10.42:11210
Do you have any idea what's going on?

You are pattern matching on a Future, if we try an analogous pattern match in the console :
val p = Promise[Int]
val f = p.future
p.success(5) // fulfill the promise
Pattern matching on f gives us :
scala> f match { case i: Int => i }
<console>:15: error: pattern type is incompatible with expected type;
found : Int
required: scala.concurrent.Future[Int]
f match { case i: Int => i }
^
Your userPosts function returns a Future[List[Post]] and you can't pattern match on the future directly to get the List[Post]. You can find more information on working with Future on the Scala website.
Lets fix your cachedUserPosts function. Your version could also be written as (does not compile):
bucket.get[List[Post]]("posts_" + userId) map ( cached => cached match {
// List[Post] in Future.map => Future[List[Post]]
case Some(list) => list
// userPosts already gives us a Future[List[Post]]
// We could have used Await.result(userPosts(userId), 5.seconds)
// to get the List[Post] from the Future, but then we would be blocking
case None => userPosts(userId)
})
If we convert the map into a flatMap we can return a Future[List[Post]], but then we would also need to return a Future[List[Post]] in the Some case, which can easily be done using Future.successful :
bucket.get[List[Post]]("posts_" + userId) flatMap ( cached => cached match {
// List[Post] to Future[List[Post]] using Future.successful
case Some(list) => Future.successful(list)
case None => userPosts(userId)
})
We could clean this up a little bit, by using map/getOrElse instead of pattern matching on the Option :
def cachedUserPosts(userId: String): Future[List[Post]] =
bucket.get[List[Post]]("posts_" + userId).flatMap { cached =>
cached.map(list => Future.successful(list))
.getOrElse(userPosts(userId))
}

Related

Creating a custom directive that uses extractCredentials directive - value map is not a member calling extractCredentials

I want to create a custom directive that will wrap some Routes, which will then give the inner routes access to the UserContext object (if it exists).
authenticated { uc =>
post {
// ... can use uc object here
}
}
I am getting a compile time error with the below code:
case class UserContext(username: String)
def authenticated: Directive1[Option[UserContext]] =
for {
credentials <- extractCredentials
result <- {
credentials match {
case Some(c) if c.scheme.equalsIgnoreCase("Bearer") => UserContext(c.token()) // authenticate(c.token)
case _ => None //rejectUnauthenticated(AuthenticationFailedRejection.CredentialsMissing)
}
}
} yield result
The error is:
value map is not a member of UserRoutes.this.UserContext
[error] credentials match {
[error] ^
[error] one error found
[error] (Compile / compileIncremental) Compilation failed
I am using VS Code with bloop, and the mouseover error says:
value map is not a member of Product with java.io.Serializable
extractCredentials returns an Option[HttpCredentials] so I'm not sure why I can't match on it or map on it.
In addition to the error of the Product with Serializable, you are trying to create a Directive. If you fix your code with Some(UserContext) the next error is:
Error:(113, 14) type mismatch;
required: akka.http.scaladsl.server.Directive
The compiler says that when you do a flatMap to a Directive you need to return a new Directive and not an Option.
At this point you can create a Directive from a function (Tuple1(Option[User) => Route) => Route. Considering that a Route is a RequestContext => Future[RouteResult] like:
def authenticated: Directive1[Option[UserContext]] = {
extractCredentials.flatMap { credentials =>
Directive { inner =>
ctx => {
val result = credentials match {
case Some(c) if c.scheme.equalsIgnoreCase("Bearer") => Some(UserContext(c.token())) // authenticate(c.token)
case _ => None //rejectUnauthenticated(AuthenticationFailedRejection.CredentialsMissing)
}
inner(Tuple1(result))(ctx)
}
}
}
}
With this you have a new directive authenticated that uses extractCredentials.
What you do here:
case Some(c) if c.scheme.equalsIgnoreCase("Bearer") => UserContext(c.token()) // authenticate(c.token)
case _ => None //rejectUnauthenticated(AuthenticationFailedRejection.CredentialsMissing)
is returning either UserContext or None. Some compiler has to infer a common type between these two. Each case class or case object implement Product with Serializable so it will be a LUB of each two case instances that don't implement other common shared supertype.
So what is on right side of result will be inferred to be Product with Serializable instead of Option[UserContext]. for comprehension expects that there is Option with a map, on which it could call .map { result => result } and as we see the types aren't matching.
Is believe you wanted to have here
case Some(c) if c.scheme.equalsIgnoreCase("Bearer") => Some(UserContext(c.token()))
case _ => None
Just in case, reminder: Option is not the same as #nullable in Java, so a: A is not automatically promoted to Some(a): Option[A] and None is different to null.
EDIT:
The other thing is that you have flatMap and map on 2 different types (Directive and Option) so you cannot just combine both of them in one for comprehension. I assume you wanted to do something like:
extractCredentials.map { credentials =>
credentials.collect {
case c if c.scheme.equalsIgnoreCase("Bearer") => UserContext(c.token())
}
}

scala type mismatch found Future[A] expected Future[B]

A bit new to scala, sort of confused at the type definitions here and how to resolve it.
private def getContract(organizationId: String, sc: SecurityContext): Future[Contract] = {
val configRequest = ConfigsActor.ReadConfigRequest(CONFIG_KEY, Option(organizationId))(sc)
(configActor ? configRequest).mapTo[Config] andThen {
case Success(config) =>
JsonUtil.extract[Contract](config.data)
case otherwise =>
log.warning("some unknown case has happened", otherwise)
}
}
I would expect the akka ask to return the result, map it to a Config. In my andThen clause to convert it into a Contract type and return it.
but I get a type mismatch
[error]
[error] found : scala.concurrent.Future[com.example.service.Config]
[error] required: scala.concurrent.Future[com.example.service.Contract]
[error] (configActor ? configRequest).mapTo[Config] andThen
[error]
Future#andThen is designed to execute a side-effect without transforming the value inside the future. To transform the value inside the Future simply map over the future
(configActor ? configRequest).mapTo[Config] map { config =>
JsonUtil.extract[Contract](config.data)
} andThen { case Failure(e) => log.warning("some unknown case has happened", e) }
The following is worth remembering
someFuture
.map { value => /* transform value */ }
.recover { error => /* transform error */ }
.andThen { /* execute side-effect */
case Success(value) => logger.info("Successfully ...")
case Failure(error) => logger.error("Failed to ...", error)
}
You can think of andThen as tap for Futures.

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
}

Type mismatch in Play controller action when recovering a future

I'm having a problem to return the correct type in a scala play controller method can someone give me a hint here? I'm using for comprehantion to deal with two service methods that returns a Future, and I would like to handle elegantly the result and the errors.
What is the best practice to do this?
def registerUser = Action { implicit request =>
Logger.info("Start play actoin")
RegisterForm.form.bindFromRequest.fold(
formWithErrors => {
BadRequest(views.html.register(formWithErrors))
},
formData => {
val registerResult = for {
reCaptchaOk <- registerUserService.checkRecaptcha(formData.gRecaptchaResponse)
userId <- registerUserService.registerUser(formData) if reCaptchaOk
} yield userId
registerResult.map(
result => Redirect(routes.DashboardController.dashboard).withSession("USER_ID" -> result.toString))
.recover{
e => handleRegisterError(e)
}
})
}
def handleRegisterError(cause: Throwable)(implicit request: Request[_]) : Result = {
val form = RegisterForm.form.bindFromRequest
cause match {
case dae: DataAccessException =>
val globalError = dae.getCause.asInstanceOf[PSQLException].getSQLState match {
case "23505" => GlobalMessages(Seq(GlobalMessage(Messages("errors.db.userAlreadyExists") ,ERROR)))
case _ => GlobalMessages(Seq(GlobalMessage(Messages("errors.system.error"),ERROR)))
}
BadRequest(views.html.register(form,globalError))
case _ =>
BadRequest(views.html.register(form))
}
the error:
[error] (compile:compileIncremental) Compilation failed
[info] Compiling 1 Scala source to C:\repos\scala\SocerGladiatorWeb\target\scala-2.11\classes...
[error] C:\repos\scala\SocerGladiatorWeb\app\controllers\RegisterController.scala:56: type mismatch;
[error] found : Throwable => play.api.mvc.Result
[error] required: PartialFunction[Throwable,?]
[error] e => handleRegisterError(e)
[error] ^
[error] one error found
[error] (compile:compileIncremental) Compilation failed
Short answer
You need a partial function to recover future failures:
def handleRegisterError(implicit request: Request[_]): PartialFunction[Throwable, Result] = {
case dae: DataAccessException =>
val form = RegisterForm.form.bindFromRequest
val globalError = dae.getCause.asInstanceOf[PSQLException].getSQLState match {
case "23505" => GlobalMessages(Seq(GlobalMessage(Messages("errors.db.userAlreadyExists"), ERROR)))
case _ => GlobalMessages(Seq(GlobalMessage(Messages("errors.system.error"), ERROR)))
}
BadRequest(views.html.register(form, globalError))
case _ =>
val form = RegisterForm.form.bindFromRequest
BadRequest(views.html.register(form))
}
then change the controller code to
registerResult
.map { result =>
Redirect(routes.DashboardController.dashboard).withSession("USER_ID" -> result.toString)
}
.recover {
handleRegisterError
}
Also note that you need an async action, i.e.
def registerUser = Action.async { implicit request =>
...
}
because you are not returning a Result but a Future[Result]. You can find more about actions in Play docs.
Details
If you look at the docs of the recover method of Future (see here) you'll see that it needs a pf: PartialFunction[Throwable, U].
Partial functions are just like normal functions but they might reject some values (for instance here, the recover method does not accept all exceptions, but only those specified in the body).
Defining a partial function needs a special syntax. It's very much like pattern matching but with no match expression.
Future(someAsyncWork).recover {
case my: MyException => ....
case _ => ....
}
Here we are using a partial recover function inline, so the type will be inferred automatically but if you want to define the recover as a separate function you need to explicitly state its type.
Advanced
The partial function syntax (pattern matching with no match keyword) is very concise and handy in most situations, but sometimes you need more than that.
For instance, note that using this syntax, we had to duplicate parts of the code (val form = RegisterForm.form.bindFromRequest) in the recover function.
Although in your case there might be better solutions but you can always convert a normal function to a partial function. First you need to define a function of the type Throwable => Option[Result] and then you can use Function#unlift to convert it to the desired partial function.
Also you can directly inherit from a PartialFunction and implement its two methods (apply and isDefinedAt).

Scala-way to handle conditions in for-comprehensions?

I am trying to create a neat construction with for-comprehension for business logic built on futures. Here is a sample which contains a working example based on Exception handling:
(for {
// find the user by id, findUser(id) returns Future[Option[User]]
userOpt <- userDao.findUser(userId)
_ = if (!userOpt.isDefined) throw new EntityNotFoundException(classOf[User], userId)
user = userOpt.get
// authenticate it, authenticate(user) returns Future[AuthResult]
authResult <- userDao.authenticate(user)
_ = if (!authResult.ok) throw new AuthFailedException(userId)
// find the good owned by the user, findGood(id) returns Future[Option[Good]]
goodOpt <- goodDao.findGood(goodId)
_ = if (!good.isDefined) throw new EntityNotFoundException(classOf[Good], goodId)
good = goodOpt.get
// check ownership for the user, checkOwnership(user, good) returns Future[Boolean]
ownership <- goodDao.checkOwnership(user, good)
if (!ownership) throw new OwnershipException(user, good)
_ <- goodDao.remove(good)
} yield {
renderJson(Map(
"success" -> true
))
})
.recover {
case ex: EntityNotFoundException =>
/// ... handle error cases ...
renderJson(Map(
"success" -> false,
"error" -> "Your blahblahblah was not found in our database"
))
case ex: AuthFailedException =>
/// ... handle error cases ...
case ex: OwnershipException =>
/// ... handle error cases ...
}
However this might be seen as a non-functional or non-Scala way to handle the things. Is there a better way to do this?
Note that these errors come from different sources - some are at the business level ('checking ownership') and some are at controller level ('authorization') and some are at db level ('entity not found'). So approaches when you derive them from a single common error type might not work.
Don't use exceptions for expected behaviour.
It's not nice in Java, and it's really not nice in Scala. Please see this question for more information about why you should avoid using exceptions for regular control flow. Scala is very well equipped to avoid using exceptions: you can use Eithers.
The trick is to define some failures you might encounter, and convert your Options into Eithers that wrap these failures.
// Failures.scala
object Failures {
sealed trait Failure
// Four types of possible failures here
case object UserNotFound extends Failure
case object NotAuthenticated extends Failure
case object GoodNotFound extends Failure
case object NoOwnership extends Failure
// Put other errors here...
// Converts options into Eithers for you
implicit class opt2either[A](opt: Option[A]) {
def withFailure(f: Failure) = opt.fold(Left(f))(a => Right(a))
}
}
Using these helpers, you can make your for comprehension readable and exception free:
import Failures._
// Helper function to make ownership checking more readable in the for comprehension
def checkGood(user: User, good: Good) = {
if(checkOwnership(user, good))
Right(good)
else
Left(NoOwnership)
}
// First create the JSON
val resultFuture: Future[Either[Failure, JsResult]] = for {
userRes <- userDao.findUser(userId)
user <- userRes.withFailure(UserNotFound).right
authRes <- userDao.authenticate(user)
auth <- authRes.withFailure(NotAuthenticated).right
goodRes <- goodDao.findGood(goodId)
good <- goodRes.withFailure(GoodNotFound).right
checkedGood <- checkGood(user, good).right
} yield renderJson(Map("success" -> true)))
// Check result and handle any failures
resultFuture.map { result =>
result match {
case Right(json) => json // serve json
case Left(failure) => failure match {
case UserNotFound => // Handle errors
case NotAuthenticated =>
case GoodNotFound =>
case NoOwnership =>
case _ =>
}
}
}
You could clean up the for comprehension a little to look like this:
for {
user <- findUser(userId)
authResult <- authUser(user)
good <- findGood(goodId)
_ <- checkOwnership(user, good)
_ <- goodDao.remove(good)
} yield {
renderJson(Map(
"success" -> true
))
}
Assuming these methods:
def findUser(id:Long) = find(id, userDao.findUser)
def findGood(id:Long) = find(id, goodDao.findGood)
def find[T:ClassTag](id:Long, f:Long => Future[Option[T]]) = {
f(id).flatMap{
case None => Future.failed(new EntityNotFoundException(implicitly[ClassTag[T]].runtimeClass, id))
case Some(entity) => Future.successful(entity)
}
}
def authUser(user:User) = {
userDao.authenticate(user).flatMap{
case result if result.ok => Future.failed(new AuthFailedException(userId))
case result => Future.successful(result)
}
}
def checkOwnership(user:User, good:Good):Future[Boolean] = {
val someCondition = true //real logic for ownership check goes here
if (someCondition) Future.successful(true)
else Future.failed(new OwnershipException(user, good))
}
The idea here is to use flatMap to turn things like Options that are returned wrapped in Futures into failed Futures when they are None. There are going to be a lot of ways to do clean up that for comp and this is one possible way to do it.
The central challenge is that for-comprehensions can only work on one monad at a time, in this case it being the Future monad and the only way to short-circuit a sequence of future calls is for the future to fail. This works because the subsequent calls in the for-comprehension are just map and flatmap calls, and the behavior of a map/flatmap on a failed Future is to return that future and not execute the provided body (i.e. the function being called).
What you are trying to achieve is the short-cicuiting of a workflow based on some conditions and not do it by failing the future. This can be done by wrapping the result in another container, let's call it Result[A], which gives the comprehension a type of Future[Result[A]]. Result would either contain a result value, or be a terminating result. The challenge is how to:
provide subsequent function calls the value contained by a prior non-terminating Result
prevent the subsequent function call from being evaluated if the Result is terminating
map/flatmap seem like the candidates for doing these types of compositions, except we will have to call them manually, since the only map/flatmap that the for-comprehension can evaluate is one that results in a Future[Result[A]].
Result could be defined as:
trait Result[+A] {
// the intermediate Result
def value: A
// convert this result into a final result based on another result
def given[B](other: Result[B]): Result[A] = other match {
case x: Terminator => x
case v => this
}
// replace the value of this result with the provided one
def apply[B](v: B): Result[B]
// replace the current result with one based on function call
def flatMap[A2 >: A, B](f: A2 => Future[Result[B]]): Future[Result[B]]
// create a new result using the value of both
def combine[B](other: Result[B]): Result[(A, B)] = other match {
case x: Terminator => x
case b => Successful((value, b.value))
}
}
For each call, the action is really a potential action, as calling it on or with a terminating result, will simply maintain the terminating result. Note that Terminator is a Result[Nothing] since it will never contain a value and any Result[+A] can be a Result[Nothing].
The terminating result is defined as:
sealed trait Terminator extends Result[Nothing] {
val value = throw new IllegalStateException()
// The terminator will always short-circuit and return itself as
// the success rather than execute the provided block, thus
// propagating the terminating result
def flatMap[A2 >: Nothing, B](f: A2 => Future[Result[B]]): Future[Result[B]] =
Future.successful(this)
// if we apply just a value to a Terminator the result is always the Terminator
def apply[B](v: B): Result[B] = this
// this apply is a convenience function for returning this terminator
// or a successful value if the input has some value
def apply[A](opt: Option[A]) = opt match {
case None => this
case Some(v) => Successful[A](v)
}
// this apply is a convenience function for returning this terminator or
// a UnitResult
def apply(bool: Boolean): Result[Unit] = if (bool) UnitResult else this
}
The terminating result makes it possible to to short-circuit calls to functions that require a value [A] when we've already met our terminating condition.
The non-terminating result is defined as:
trait SuccessfulResult[+A] extends Result[A] {
def apply[B](v: B): Result[B] = Successful(v)
def flatMap[A2 >: A, B](f: A2 => Future[Result[B]]): Future[Result[B]] = f(value)
}
case class Successful[+A](value: A) extends SuccessfulResult[A]
case object UnitResult extends SuccessfulResult[Unit] {
val value = {}
}
The non-teminating result makes it possible to provide the contained value [A] to functions. For good measure, I've also predefined a UnitResult for functions that are purely side-effecting, like goodDao.removeGood.
Now let's define your good, but terminating conditions:
case object UserNotFound extends Terminator
case object NotAuthenticated extends Terminator
case object GoodNotFound extends Terminator
case object NoOwnership extends Terminator
Now we have the tools to create the the workflow you were looking for. Each for comprehention wants a function that returns a Future[Result[A]] on the right-hand side, producing a Result[A] on the left-hand side. The flatMap on Result[A] makes it possible to call (or short-circuit) a function that requires an [A] as input and we can then map its result to a new Result:
def renderJson(data: Map[Any, Any]): JsResult = ???
def renderError(message: String): JsResult = ???
val resultFuture = for {
// apply UserNotFound to the Option to conver it into Result[User] or UserNotFound
userResult <- userDao.findUser(userId).map(UserNotFound(_))
// apply NotAuthenticated to AuthResult.ok to create a UnitResult or NotAuthenticated
authResult <- userResult.flatMap(user => userDao.authenticate(user).map(x => NotAuthenticated(x.ok)))
goodResult <- authResult.flatMap(_ => goodDao.findGood(goodId).map(GoodNotFound(_)))
// combine user and good, so we can feed it into checkOwnership
comboResult = userResult.combine(goodResult)
ownershipResult <- goodResult.flatMap { case (user, good) => goodDao.checkOwnership(user, good).map(NoOwnership(_))}
// in order to call removeGood with a good value, we take the original
// good result and potentially convert it to a Terminator based on
// ownershipResult via .given
_ <- goodResult.given(ownershipResult).flatMap(good => goodDao.removeGood(good).map(x => UnitResult))
} yield {
// ownership was the last result we cared about, so we apply the output
// to it to create a Future[Result[JsResult]] or some Terminator
ownershipResult(renderJson(Map(
"success" -> true
)))
}
// now we can map Result into its value or some other value based on the Terminator
val jsFuture = resultFuture.map {
case UserNotFound => renderError("User not found")
case NotAuthenticated => renderError("User not authenticated")
case GoodNotFound => renderError("Good not found")
case NoOwnership => renderError("No ownership")
case x => x.value
}
I know that's a whole lot of setup, but at least the Result type can be used for any Future for-comprehension that has terminating conditions.