How to break after response in Play (Scala) - scala

I am new to Play Framework using Scala. I want to evaluate a condition and in that condition evaluates to true, I want to send a response and exit at that point. However, the code below that I am trying continues till the end.
I tried breaking with a return statement - However, I get a type mismatch. Can someone help me out with this?
def hello = Action { request =>
if (true) {
Ok("in If")
// Return at this point
}
print("This line should not be printed")
Ok("final")
}
EDIT
Assume a GET call is being made with 4 parameters - name, age, married, spouse. I want to make sure all 3 params (name, age, married) are passed in, and if married is true, check if spouse is passed in. If this validation fails, I want to respond saying Bad Request. Else, continue with logic. How do I write this?

Here is an alternative way to do it:
case class QueryInput(name: String, age: Int, married: Boolean, spouse: Option[String]) {
def validated = if(married && spouse.isEmpty) None else Some(this)
}
def validateInput(request: RequestHeader) = {
val input = for {
name <- request.queryString.get("name").flatMap(_.headOption)
age <- request.queryString.get("age").flatMap(_.headOption.flatMap(a=>Try(a.toInt).toOption))
married <- request.queryString.get("married").flatMap(_.headOption.map(_=="true"))
} yield {
QueryInput(name, age, married, request.queryString.get("spouse").flatMap(_.headOption))
}
input.flatMap(_.validated)
}
def hello() = Action { request =>
validateInput(request) match {
case Some(input) => Ok(input.toString())
case None => BadRequest
}
}
In fact, there are many options. You could also play with the Either class to do validation: Left value to accumulate errors and return bad request, right value to construct your validated input.

My recommendation would be to have a method for validating the parameters. Then do a simple if/else to check if the parameters are valid and return a success or a general error.
If you really want a specific

First thing:
When the block evaluates, all of its expressions and declarations are processed in order, and then the block returns the value of the last expression as its own value.
Second: don't use return.
And the third one is a Play Framework way of resolving your problem: action composition. Though I would not say that it is trivial.

You can do this, by putting a return Ok in but really, thats not the scala way. What you want to do is to change your mindset and imagine everything as a function. If you didnt know, if-then-else always returns a value. For example, you can actually write if this way:
def hello = Action { request =>
val result = if (true) {
Ok("foo")
} else {
Ok("bar")
}
result
}
of course, an even more scala way is to use matchers
def hello = Action { request =>
val result = true match {
case true => Ok("foo")
case _ => Ok("bar")
}
result
}
Take that one step further and you dont even need to specify the result object at all, because scala figures out the returning object based on the last object returned/created.
def hello = Action { request =>
true match {
case true => Ok("foo")
case _ => Ok("bar")
}
}
EDIT: TO answer the OP's edit, you still want to use the matcher. Assuming your vals are options, heres what you do:
def hello(nameOpt:Option[String], ageOpt:Option[String], marriedOpt:Option[String]) = Action { request =>
(nameOpt, ageOpt, marriedOpt) match {
case (Some(name), Some(age), Some(married)) => Ok("all 3 are given")
case (Some(name), Some(age), _) => Ok("all 2 are given")
// functionally same as above
// case (Some(name), Some(age), None) => Ok("all 2 are given")
// some combination of the above
case (None, None, Some(married)) => Ok("married but no name or age")
// default case
case _ => Ok("bar")
}
}

Related

"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")
}
}

Error value flatMap is not a member of Product with Serializable in for with Future[Option]

The following block of code fails to build with error :
value flatMap is not a member of Product with Serializable
[error] if (matchingUser.isDefined) {
Here's the code:
for {
matchingUser <- userDao.findOneByEmail(email)
user <- {
if (matchingUser.isDefined) {
matchingUser.map(u => {
// update u with new values...
userDao.save(u)
u
})
} else {
val newUser = new User(email)
userDao.create(newUser)
newUser
}
}
} yield user
Method userDao.findOneByEmail(email) returns anFuture[Option[User]]object.
My Google searches are only aboutEitherwithRightandLeft` types.
Maybe I'm not doing this the proper way, please teach me how to properly do this.
The first branch of the if statement returns Option[User], the other one returns User. So, the result of the entire statement is inferred to have type Product with Serializable because it is the only common supertype of the two.
You could wrap the last statement inside the if into an Option (just do Option(newUser) instead of newUser) or, better yet, use fold instead of the whole if(matchingUser.isDefined) {...} thingy:
matchingUser.fold {
val u = new User(email)
userDao.create(u)
u
} { u =>
userDao.save(u)
u
}
This will make the result of that statement to be Option[User] as you probably intended ... but it still will not compile.
The problem is that you cannot mix different types of monads in the for-comprehension: since the first one was Future, all the others have to be as well. You can't have an Option there.
How to get around that? Well, one possibility is to make userDao.create and userDao.save return a future of the object they just saved. That is, probably a better thing to do in general, then what you have, because now you are returning the user before it was actually stored ... What if the create operation fails afterwards? Then you can just rewrite your for-comprehension like this:
for {
matchingUser <- userDao.findOneByEmail(email)
user <- matchingUser.fold(userDao.create(new User(email)))(userDao.save)
} yield user
Or just get rid of it entirely (for-comprehension is an overkill for simple cases like this):
userDao
.findOneByEmail(email)
.flatMap(_.fold(usrDao.create(new User(email)))(userDao.save))
Or, it may look a little nicer with pattern matching instead of fold in this case:
userDao
.findOneByEmail(email)
.flatMap {
case Some(u) => userDao.save(u)
case None => userDao.create(new User(email))
}
Here is my test example to reproduce your issue with solution.
Basically your user returns wrapper around Future (Option of Future), but it expected to be Future, as the first statement in for-comprehension.
That is why I've applied some addition unwrapping. See sample below.
Note: it does not looks so nice, I'd prefer to rewrite it with flatMap map.
object T {
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
def future1: Future[Option[Int]] = ???
def future2(i: Int): Future[Double] = ???
for {
matchingUser <- future1
user <- {
if (matchingUser.isDefined) {
matchingUser.map { i =>
future2(i)
}
} else {
Some(future2(42))
}
} match {
case None => Future.successful(-42.0)
case Some(x) => x
}
} yield user
}
And the same implemented with flatMap:
val userFuture = future1.flatMap {
case Some(i) => future2(i)
case None => future2(42)
}

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

How to properly use future/actorSelection/resolveOne in Play app?

Below is my code:
def runAsync(crewType: String) = Action.async {
val temp: Future[Result] = Future(Crew.findCaptainByCrewType(crewType) match {
case None =>
BadRequest(s"Invalid crew name provided: $crewType")
case Some(crew) =>
system.actorSelection(s"/user/${crew.cptName}").resolveOne().map { actorRef =>
Ok(println("hi hi"))
}
})
temp
}
I don't understand why this doesn't work?
My objective is to have user pass in a name, which then I try to find an actor with that name using the actorSelection and resolveOne. I assume I am using it incorrectly?!
ActorSelection.resolveOne() returns a Future[ActorRef], and because you are inside a Future(...) you get a Future[Future[Result]] in case of a valid crewname.
You can solve this by using flatMap in which case you should also return a Future in case of an invalid crewname (None).
Unrelated: You can also leave out the temp value and can replace the pattern matching on Option by Option.fold.
def runAsync(crewType: String) = Action.async {
Future(Crew.findCaptainByCrewType(crewType)).flatMap( _.fold(
Future.successful(BadRequest(s"Invalid crew name provided: $crewType"))
)( crew =>
system.actorSelection(s"/user/${crew.cptName}").resolveOne().map {
actorRef => Ok(println("hi hi")) // not sure you want println here
}
))
}

Request.session.get(String) returns Option[String] How do I use this?

I want to use a session value in pattern matching but since my request.get("profileType") returns Option[String] I cannot use it in pattern matching like I have in my code.
Here's my code snippet.
def editorProfile = Action { implicit request =>
request.session.get("profileType").toString() match {
case "editor" => {
request.session.get("userEmail").map {
userEmail => Ok(html.profile.editorProfile("my profile"))
}.getOrElse {
Unauthorized(html.error("Not logged in"))
}
}
}
}
Here is the error:
[MatchError: Some(editor) (of class java.lang.String)]
My question is. How do I use this Some(editor) from session.get in my pattern matching?
You should probably try to use a for comprehension because it might scale easier when you add more checks of a similar type.
val email = for {
profileType <- request.session.get("profileType") if profileType == "editor"
userEmail <- request.session.get("userEmail")
} yield userEmail
// email is of type Option[String] now, so we do the matching accordingly
email match {
case m: Some => Ok(html.profile.editorProfile("my profile"))
case None => Unauthorized(html.error("Not logged in or not an editor."))
}
You can of course write all that in an even more concise way but as a beginner, it does not hurt being more explicit.
Addition:
If you want to use the mail address later on, you could change it to:
email match {
case Some(address) => Ok(html.profile.editorProfileWithEmail("my profile", address))
case None => Unauthorized(html.error("Not logged in or not an editor."))
}
You call toString on Option[String] and get "Some(editor)". Instead you must match on this:
request.session.get("profileType") match {
case Some("editor") => { /* your code */}
case _ => /* something else */
}
Note that I added default case _ =>. Without it you can get MatchError if session didn't contains "profileType" attribute or attribute has another value.