ReactiveMongo: How to use projection - json

I'm trying to filter out the password field when querying a document from MongoDB with ReactiveMongo:
val projection = Json.obj("password" -> 0)
def find(selector: JsValue, projection: Option[JsValue]) = {
val query = collection.genericQueryBuilder.query(selector)
projection.map(query.projection(_))
query.cursor[JsValue].collect[Vector](perPage).transform(
success => success,
failure => failure match {
case e: LastError => DaoException(e.message, Some(DATABASE_ERROR))
}
)
}
The code above has no effect... I also get the password field. If I try the following from the mongo client, then it works and password is not returned:
db.users.find( { username: 'j3d' }, { password:0} )
Am I missing something?

Here is the solution:
def find(selector: JsValue, projection: Option[JsValue]) = {
var query = collection.genericQueryBuilder.query(selector)
projection.map(query = query.projection(_))
query.cursor[JsValue].collect[Vector](perPage).transform(
success => success,
failure => failure match {
case e: LastError => DaoException(e.message, Some(DATABASE_ERROR))
}
)
}
or alternatively:
def find(selector: JsValue, projection: Option[JsValue]) = {
val query = collection.genericQueryBuilder
.query(selector)
.projection(projection.getOrElse(Json.obj())
query.cursor[JsValue].collect[Vector](perPage).transform(
success => success,
failure => failure match {
case e: LastError => DaoException(e.message, Some(DATABASE_ERROR))
}
)
}
I hope that helps.

Related

Handle exception for Scala Async and Future variable, error accessing variable name outside try block

#scala.throws[scala.Exception]
def processQuery(searchQuery : scala.Predef.String) : scala.concurrent.Future[io.circe.Json] = { /* compiled code */ }
How do I declare the searchResult variable at line 3 so that it can be initailized inside the try block and can be processed if it's successful after and outside the try block. Or, is there any other way to handle the exception? The file containing processQuery function is not editable to me, it's read-only.
def index = Action.async{ implicit request =>
val query = request.body.asText.get
var searchResult : scala.concurrent.Future[io.circe.Json] = Future[io.circe.Json] //line 3
var jsonVal = ""
try {
searchResult = search.processQuery(query)
} catch {
case e :Throwable => jsonVal = e.getMessage
}
searchResult onSuccess ({
case result => jsonVal = result.toString()
})
searchResult.map{ result =>
Ok(Json.parse(jsonVal))
}
}
if declared in the way it has been declared it's showing compilation error
Would using the recover method help you? I also suggest to avoid var and use a more functional approach if possible. In my world (and play Json library), I would hope to get to something like:
def index = Action.async { implicit request =>
processQuery(request.body.asText.get).map { json =>
Ok(Json.obj("success" -> true, "result" -> json))
}.recover {
case e: Throwable => Ok(Json.obj("success" -> false, "message" -> e.getMessage))
}
}
I guess it may be necessary to put the code in another whole try catch:
try {
processQuery....
...
} catch {
...
}
I have here a way to validate on the incoming JSON and fold on the result of the validation:
def returnToNormalPowerPlant(id: Int) = Action.async(parse.tolerantJson) { request =>
request.body.validate[ReturnToNormalCommand].fold(
errors => {
Future.successful{
BadRequest(
Json.obj("status" -> "error", "message" -> JsError.toJson(errors))
)
}
},
returnToNormalCommand => {
actorFor(id) flatMap {
case None =>
Future.successful {
NotFound(s"HTTP 404 :: PowerPlant with ID $id not found")
}
case Some(actorRef) =>
sendCommand(actorRef, id, returnToNormalCommand)
}
}
)
}

How to validate Complete in AKKA-HTTP

I want to validate the result of the complete function of my API, the Response generates a null when it can not transform the String to a Json type, I do not want it to generate null but say: "not found", this is my route:
val route = pathPrefix("auth") {
path("signIn") {
pathEndOrSingleSlash {
post {
entity(as[LoginPassword]) { loginPassword =>
val a = signIn(loginPassword.login, loginPassword.password).map(_.asJson)
if(signIn(loginPassword.login, loginPassword.password).map(_.asJson) == null){
complete(states.map(_.asJson))
}else {
def getObject : Option[Any] = Option(signIn(loginPassword.login, loginPassword.password).map(_.asJson))
val ahh = signIn(loginPassword.login, loginPassword.password).map(_.asJson)
if(getObject.isEmpty || getObject == null){ ///////NOT FOUND
complete("Not Found")
}else {
complete(signIn(loginPassword.login, loginPassword.password).map(_.asJson)
}
//complete(signIn(loginPassword.login, loginPassword.password).map(_.asJson))
}
}
}
}
}
this does not work, since it always enters the else within the condition, responding a Json when it gets the value in BD and null when not.
Function Sign
def signIn(login: String, password: String): Future[Option[TokenEntity]] = {
db.run(users.filter(u => u.username === login).result).flatMap { users =>
users.find(user => Bcrypt.validate(password, user.password)) match {
case Some(user) => db.run(tokens.filter(_.userId === user.id).result.headOption).flatMap {
case Some(token) => Future.successful(Some(token))
case None => createToken(user).map(token => Some(token))
}
case None => Future.successful(None)
}
}
}
The Json library I use is: Json
Some help? Thanks.
I stopped to think things through and how to solve what I needed and first to validate that it was a valid response was to analyze the response of signIn:
Assign the response to a val
val token = signIn(loginPassword.login, loginPassword.password)
token is of type: Future[Option[TokenEntity]] and I need to work with: Option[TokenEntity] and validate if it is a correct answer:
val response = token.map(_ match {
case Some(token) => prepareHttpResponse(StatusCodes.OK, token.asJson.toString)
case None => prepareHttpResponse(StatusCodes.Unauthorized, "{reason: \"not found\"")
})
if it is valid I respond in format Json the Token but I respond with: not found
finally was:
val route = pathPrefix("auth") {
path("signIn") {
post {
entity(as[LoginPassword]) { loginPassword =>
val token = signIn(loginPassword.login, loginPassword.password)
val response = token.map(_ match {
case Some(token) => prepareHttpResponse(StatusCodes.OK, token.asJson.toString)
case None => prepareHttpResponse(StatusCodes.Unauthorized, "{reason: \"not found\"")
})
complete(response)
}
}
}

How to return json from Play Scala controller?

I would like to know that how can I return json response data from Play(2.2.x) Scala controller class to display on my view page ? I have json objects in Postgresql database(table name: "test" and having: id and name). Please provide me any solutions for it.
I have tried the following cases(a and b), but I am not sure why I am not getting the response(like: names) on my controller, so I can show them on my view page ? since I am very new to Play/Scala and Postgresql.
case a. If I give like:
model:
def getTestValuesFromTable() = {
DB.withConnection { implicit connection =>
val selectJson =SQL("select name from test").on().apply().collect {
case Row(id:Long, Some(name:String)) =>
new TestContent(name)
}
//.head
//JsObject(selectJson().map { row => row[Long]("id").toString -> JsString(row[String]("name")) }.toSeq)
}
}
controller:
def getTest = Action {
val response = TestContent.getTestValuesFromTable()
Ok("Done")
//Ok(response)
}
Output is: Done(application is executing fine without any exceptions, of course json data is not coming since I am returning: Done only, so getting output: "Done")
case b. If I do like this: getting error: not enough arguments for method apply: (n: Int)models.Testname in trait LinearSeqOptimized. Unspecified value parameter n. I really not sure how can I get my response for it ?
controller:
def getTest = Action {
val response = TestContent.getTestValuesFromTable()
// Ok("Done")
Ok(response)
}
model:
def getTestValuesFromTable(): JsValue = {
DB.withConnection { implicit connection =>
val selectJson = SQL("select * from test")
JsObject(selectJson().map { row => row[Long]("id").toString -> JsString(row[String]("name")) }.toSeq)
// val selectJson =SQL("select name from test").on().apply().collect {
// case Row(id:Long, Some(name:String)) =>
// new TestContent(name)
// }
//.head
JsObject(selectJson().map { row => row[Long]("id").toString -> JsString(row[String]("name")) }.toSeq)//not enough arguments for method apply: (n: Int)models.Testname in trait LinearSeqOptimized. Unspecified value parameter n.
}
}
Please let me know how to get my response ?
getJsonValuesFromTable method return nothing (Unit). To fix it change definition of this method to
def getJsonValuesFromTable(testContent: TestContent) = {
or explicitly setting type:
def getJsonValuesFromTable(testContent: TestContent): Unit = {
Also as a next step to let client know that you are returning json, you should set content type:
Ok(Json.obj(response)).as("application/json")

Play scala - confusing about the result type of Action.async

I'm little bit confusing about the expected result of Action.async. Here the use case : from the frontend, I receive a JSON to validate (a Foo), I send this data calling an another web service and I extract and validate the received JSON (Bar case class) which I want to validate too. The problem is when I return a result, I have the following error :
type mismatch;
found : Object
required: scala.concurrent.Future[play.api.mvc.Result]
Here my code :
case class Foo(id : String)
case class Bar(id : String)
def create() = {
Action.async(parse.json) { request =>
val sessionTokenOpt : Option[String] = request.headers.get("sessionToken")
val sessionToken : String = "Bearer " + (sessionTokenOpt match {
case None => throw new NoSessionTokenFound
case Some(session) => session
})
val user = ""
val structureId : Option[String] = request.headers.get("structureId")
if (sessionToken.isEmpty) {
Future.successful(BadRequest("no token"))
} else {
val url = config.getString("createURL").getOrElse("")
request.body.validate[Foo].map {
f =>
Logger.debug("sessionToken = " + sessionToken)
Logger.debug(f.toString)
val data = Json.toJson(f)
val holder = WS.url(url)
val complexHolder =
holder.withHeaders(("Content-type","application/json"),("Authorization",(sessionToken)))
Logger.debug("url = " + url)
Logger.debug(complexHolder.headers.toString)
Logger.debug((Json.prettyPrint(data)))
val futureResponse = complexHolder.put(data)
futureResponse.map { response =>
if(response.status == 200) {
response.json.validate[Bar].map {
b =>
Future.successful(Ok(Json.toJson(b)))
}.recoverTotal { e : JsError =>
Future.successful(BadRequest("The JSON in the body is not valid."))
}
} else {
Logger.debug("status from apex " + response.status)
Future.successful(BadRequest("alo"))
}
}
Await.result(futureResponse,5.seconds)
}.recoverTotal { e : JsError =>
Future.successful(BadRequest("The JSON in the body is not valid."))
}
}
}
}
What is wrong in my function ?
Firstly, this is doing nothing:
futureResponse.map { response =>
if(response.status == 200) {
response.json.validate[Bar].map {
b =>
Future.successful(Ok(Json.toJson(b)))
}.recoverTotal { e : JsError =>
Future.successful(BadRequest("The JSON in the body is not valid."))
}
} else {
Logger.debug("status from apex " + response.status)
Future.successful(BadRequest("alo"))
}
}
Because you're not capturing or assigning the result of it to anything. It's equivalent to doing this:
val foo = "foo"
foo + " bar"
println(foo)
The foo + " bar" statement there is pointless, it achieves nothing.
Now to debug type inference problems, what you need to do is assign results to things, and annotate with the types you're expecting. So, assign the result of the map to something first:
val newFuture = futureResponse.map {
...
}
Now, what is the type of newFuture? The answer is actually Future[Future[Result]], because you're using map, and then returning a future from inside that. If you want to return a future inside your map function, then you have to use flatMap instead, this flattens the Future[Future[Result]] to Future[Result]. But actually in your case, you don't need that you can use map, and just get rid of all those Future.successful calls, because you're not actually doing anything in that map function that needs to return a future.
And then get rid of that await as others have said - using await means blocking, which negates the point of using futures in the first place.
Anyway, this should compile:
def create() = {
Action.async(parse.json) { request =>
val sessionTokenOpt : Option[String] = request.headers.get("sessionToken")
val sessionToken : String = "Bearer " + (sessionTokenOpt match {
case None => throw new NoSessionTokenFound
case Some(session) => session
})
val user = ""
val structureId : Option[String] = request.headers.get("structureId")
if (sessionToken.isEmpty) {
Future.successful(BadRequest("no token"))
} else {
val url = config.getString("createURL").getOrElse("")
request.body.validate[Foo].map {
f =>
Logger.debug("sessionToken = " + sessionToken)
Logger.debug(f.toString)
val data = Json.toJson(f)
val holder = WS.url(url)
val complexHolder =
holder.withHeaders(("Content-type","application/json"),("Authorization",(sessionToken)))
Logger.debug("url = " + url)
Logger.debug(complexHolder.headers.toString)
Logger.debug((Json.prettyPrint(data)))
val futureResponse = complexHolder.put(data)
futureResponse.map { response =>
if(response.status == 200) {
response.json.validate[Bar].map {
b =>
Ok(Json.toJson(b))
}.recoverTotal { e : JsError =>
BadRequest("The JSON in the body is not valid.")
}
} else {
Logger.debug("status from apex " + response.status)
BadRequest("alo")
}
}
}.recoverTotal { e : JsError =>
Future.successful(BadRequest("The JSON in the body is not valid."))
}
}
}
}
Do not Await.result(futureResponse, 5 seconds). Just return the futureResponse as is. The Action.async can deal with it (in fact, it wants to deal with it, it requires you to return a Future).
Note that in your various other codepaths (else, recoverTotal) you are already doing that.
If you use Action.async you don't need to await for result. So try to return future as is, without Await.result

Scala Play 2: action composition and BodyParser

I'm using action composition for authentication and to avoid passing common parameters in each action. My question is how can I combine it the BodyParser parse.json, like in the method below?
def setUser() = Action(parse.json) {
implicit request =>
val id = (request.body \ "id").as[String]
val username = (request.body \ "username").as[String]
val previousURI = request.session.get("previousURI") match {
case Some(x) => x
case None => "/"
}
Ok(previousURI).withSession(
"id" -> id,
"username" -> username
)
}
The controller that uses the above method uses the 'Auth' trait:
case class User(id: Option[String], username: Option[String])
case class Context(user: User, request: Request[AnyContent])
extends WrappedRequest(request)
trait Auth {
def CheckLogin(f: Context => Result) = {
Action { request =>
request.session.get("username").map { token =>
// username? yes continue...
val id = request.session.get("id")
val username = request.session.get("username")
f(Context(new User(id, username), request))
}.getOrElse {
// no redirect to login
Results.Redirect(routes.Application.login).withSession(
"previousURI" -> request.uri
)
}
}
}
}
If I try:
def myMethod() = CheckLogin(parse.json) { ...
I get:
type mismatch; found : play.api.mvc.BodyParser[play.api.libs.json.JsValue] required: controllers.Context => play.api.mvc.Result
thanks!
Answering my own question, still not sure if this is the most elegant but it works:
def CheckLogin(bp: BodyParser[AnyContent] = parse.anyContent)(f: Context => Result) = {
Action(bp) { request =>
// ...
CheckLogin now has a BodyParser that defaults to parse.anyContent so it can accept other BodyParsers as well like parse.json