How can I use a Future inside an Akka HTTP Directive? - scala

I currently have a directive that I'm using to secure resources in an Akka HTTP app, like so:
def authenticate: Directive1[Login] =
optionalHeaderValueByName("Authorization") flatMap {
val accessToken = authz.split(' ').last
case Some(authz) =>
LoggedInUser findByAccessToken accessToken match {
case Some(user) => provide(user)
case None => reject(AuthorizationFailedRejection)
}
case None => reject(AuthorizationFailedRejection)
}
where LoggedInUser.findByAccessToken() makes a blocking query against a database, I would like to switch this for an asynchronous ask to an actor which which can provide the same data, I'm OK with passing in the ActorRef as a parameter to the directive but I cannot work out how to handle the Future that the ask returns.
None of the Directive1 examples that come with Akka HTTP seem to do this (at least I could;t find any) although there are examples of directives returning Route which do.
Is what I want to do even possible? Is a possible approach to create a StandardRoute subclass with a field for the user credentials and return that somehow?

Yes, it is possible. As far as I understand you need something like this:
def authenticate: Directive1[Login] = {
def findByAccessToken(accessToken:String): Future[Option[Login]] = ???
optionalHeaderValueByName("Authorization").flatMap {
case Some(authz) =>
val accessToken = authz.split(' ').last
onSuccess(findByAccessToken(accessToken)).flatMap {
case Some(user) => provide(user)
case None => reject(AuthorizationFailedRejection)
}
case None => reject(AuthorizationFailedRejection)
}
}

Related

Can http4s do different status codes given a Future?

I'm using http4s, and I have a Try that generates some json data for a response:
case GET -> Root / "something" =>
getSomethingTry() match {
case Success(something) => Ok(something)
case Failure(CustomNotFoundException(reason)) => NotFound(reason)
case Failure(CustomConflictException()) => Conflict()
}
This function correctly returns a Task[Response]
However, I want to replace the Try with a Future. Matching no longer works, because the future may not have been resolved at the time of the match. So, I can map the future:
case GET -> Root / "something" =>
getSomethingFuture().map {
something => Ok(something)
}.recover {
case CustomNotFoundException(reason) => NotFound(reason)
case CustomConflictException() => Conflict()
}
But this returns a Future[Task[Response]] which is not what http4s wants. It doesn't seem appropriate to use Await.result to unbox the Future - I think this could cause thread pool issues - but it does make the code work.
http4s accepts futures as the argument to the task creator:
case GET -> Root / "something" =>
Ok(getSomethingFuture())
But this doesn't let me set different status codes in the event of different errors. A solution could be to do a .recover on a task, but I can't see an obvious way to do that.
How can I call different http4s task wrappers in the event of different Future failure cases? Do I need to use middleware?
Assuming you're using http4s 0.17 and higher, your Task is fs2.Task.
It's easy to convert the Future to Task and then deal with the latter:
case GET -> Root / "something" =>
Task.fromFuture(getSomethingFuture())
.flatMap {
something => Ok(something)
}
.handleWith {
case CustomNotFoundException(reason) => NotFound(reason)
case CustomConflictException() => Conflict()
}
I'd recommend, however, to use Task throughout your program instead of Try or Future
You dont really need to unwrap the future. Play framework provides action.async for returning Future.
You can use it in the following way
Action.async {
getSomethingFuture().map {
something => Ok(something)
}.recover {
case CustomNotFoundException(reason) => NotFound(reason)
case CustomConflictException() => Conflict()
}
}
https://www.playframework.com/documentation/2.6.x/ScalaAsync#returning-futures

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 _ =>
}

Scala proper using of Try and Option combined together

I'm using Try() to wrap calls to not-100%-reliable 3-rd party service which return Option() of some type therefore receiving Some(DataDto) or None.
So it looks something like this:
private def fetchData(): Option(DataDto) {
val data: Try[Option[DataDto]] = Try(problematicService.getMeStuff())
data match {
case Success(maybeDataDto) => {
maybeDataDto match {
case Some(dataDto) => Some(dataDto)
case None => None
}
}
case Failure(_) => None
}
}
What is the best way to implement such behavior? I feel that this implementation is to verbose.
You can convert a Try to an Option and flatten it.
Try(s.getMeStuff()).toOption.flatten

How should I use the cache API with futures

I want to use the built in playframework cache API but not sure how to cache this web service request when it has futures.
def getUser(username: String): Future[Option[User]] = {
ws.url("...").get.map { response =>
response.json.validate[User] match {
case JsSuccess(user, _) => Some(user)
case JsError(errors) => {
// log errors
None
}
}
}
}
The cache key will just be the username for now.
How would I use the cache API here?
I wanted to use the getOrElse pattern:
val user: User = cache.getOrElse[User]("item.key") {
User.findById(connectedUser)
}
But the Future and Option is confusing me on how to go about using it.
I would do it like this:
def fromWebservice(username: String): Future[Option[User]] =
ws.url("..").get.map(validate) // validate does the json validate thing
def getUser(username: String): Future[Option[User]] =
cache.getAs[User](username) match {
case Some(x) =>
Future.successful(Some(x))
case None =>
fromWebservice(username).map(_.map { user =>
cache.set(username, user)
x
})
}
As an aside, the Play cache API does not have an API that returns Futures, but they're working on it: https://github.com/playframework/playframework/issues/5912

Serving rest requests: how to avoid code duplication

In the rest part of my lift application I often have code like this:
object UserRest extends RestHelper {
serve("user" :: Nil prefix {
case Req("remove-item" :: itemId :: Nil, "json", PostRequest) => {
User.currentUser.map{ u =>
//doing some things and returning message
"Ready, all right."
}.getOrElse("You must be logged in to perform this operation.")
}: JValue
case Req("update-item" :: itemId :: Nil, "json", PostRequest) => {
User.currentUser.map{ u =>
//doing some things and returning message
"Ready, all right."
}.getOrElse("You must be logged in to perform this operation.")
}: JValue
}
}
As you can see for every user operation I have this piece of code:
User.currentUser.map{ u =>
//...
}.getOrElse("You must be logged in to perform this operation.")
My question is - do I have a way to put this piece of code to one place to avoid repeating it for every request?
You could write a function to handle unboxing objects for you. Something like this should help:
def unbox[A](t:Box[A])(a: A => JValue) = {
val msg = "You must be logged in to perform this operation."
t.map { u => a(u) }.getOrElse(JString(msg))
}
Then, you'd just call it like :
unbox(User.current){ u:User =>
//doing something
JString("Ready, all right.")
}
If you are just doing this for the purposes of authentication, you should be able to use guarded LiftRules.dispatch calls. Described in Simply Lift and in more detail here.
You may also just be able to use LiftRules.httpAuthProtectedResource(UserRest), but I'm not really sure about that.