cannot be applied to (play.api.mvc.AnyContent) - scala

when i try to convert my content of request body to json this happens..
code:
def addItem() = Action { implicit request =>
val content:AnyContent = request.body
val res = Json.parse(content)
Ok("done")
}
sbt compile msg:
**overloaded method parse with alternatives:
[error] (input: Array[Byte])play.api.libs.json.JsValue <and>
[error] (input: java.io.InputStream)play.api.libs.json.JsValue <and>
[error] (input: String)play.api.libs.json.JsValue
[error] cannot be applied to (play.api.mvc.AnyContent)
[error] val res = Json.parse(content)**
i want to know why i can't convert my content as json ?

AnyContent provides a helper method asJson:
ef addItem() = Action { implicit request =>
val content: AnyContent = request.body
val res: Option[JsValue] = content.asJson
Ok("done")
}
Or, you can use Play's body parser directly:
def addItem() = Action(parse.json) { implicit request: Request[JsValue] =>
val content: JsValue = request.body
// ...
Ok("done")
}
It will give a HTTP 400 automatically if content is not JSON.
See https://www.playframework.com/documentation/2.8.x/ScalaBodyParsers

Related

Type mismatch in recursive function using Bitbucket API and Playframework

I created a generic function to get from Bitbucket-API (It gives you the data in pages if there is to much data and the next page URL is under "next" in the response).
def getAggregatedDataWithNext[T](url: String)(implicit reader: Reads[T]): Future[Seq[T]]= {
val featureResponse = ws.url(url).withAuth(AuthUserName, AuthPassword, WSAuthScheme.BASIC).addQueryStringParameters("pagelen" -> "100").get()
featureResponse.map(response => {
val resJson = response.json
val tSequence = (resJson \ "values").as[Seq[T]]
(resJson \ "next").asOpt[String] match {
case None => tSequence
case Some(nextUrl) => getAggregatedDataWithNext(nextUrl).flatMap(tSequence ++ _)
}
})
}
I get an error:
type mismatch;
found : Seq[T]
required: scala.concurrent.Future[?]
And IntelliJ gives me this:

Define generic decoder in scala using circe

I'm trying to switch my JsonUtils class from Json4s to circe
and i find it hard to solve the generic implementation for the decoder.
my Json4s func look like this:
implicit val formats = DefaultFormats
def extractValueFromJson[T](json: String, key: String)(implicit m: Manifest[T]): T = {
val parsed = parse(json).asInstanceOf[JObject]
val value =(parsed \ key).extract[T]
value
}
example of usage:
extractValueFromJson[String](jsonStr, keyInJson)
and it is working perfectly
now i have tried the same func with circe:
implicit def decodeGeneric[A: Decoder](json: Json): Either[Error, A] = Decoder[A].decodeJson(json)
def extractValueFromJson[A: ClassTag, T](jsonStr: String, key: String)(implicit m: Manifest[T]): T = {
val json: String = jsonStr.asJson.noSpaces
decode[A](json) match {
case Left(error: Error) => throw error
case Right(value) => {
value.getClass.getDeclaredField(key).get(value).asInstanceOf[T]
}
}
}
and i get the following error while compiling:
could not find implicit value for evidence parameter of type io.circe.Decoder[A]
[error] decode[A](json) match {
[error] ^
This is the desired output for a given input
input:
case class Bar(str: String)
val bar = Bar("just a string")
usage:
val test = extractValueFromJson[Bar,String](bar.asJson.noSpaces,"str")
output:
just a string
What am i doing wrong here?
Is there a way to define a generic decoder?
i have read some similar questions here but haven't found a solution that fit my needs
You can do this:
def extractValueFromJson[A](jsonStr: String, key: String)(implicit decoder: Decoder[A]): A =
io.circe.parser.decode(jsonStr)(decoder.at(field = key)) match {
case Right(result) => result
case Left(error) => throw error
}
Which you can use like this:
extractValueFromJson[String](jsonStr = bar.asJson.noSpaces, key = "str")
// res: String = "just a string"

Scala future chaining and Left/Right

I have this piece of code in the PLAY framework
def ws: WebSocket = WebSocket.acceptOrResult[JsValue, JsValue] { rh =>
implicit val req = Request(rh, AnyContentAsEmpty)
silhouette.SecuredRequestHandler { securedRequest =>
Future.successful(HandlerResult(Ok, Some(securedRequest.identity)))
}.map {
case HandlerResult(_, Some(_)) => wsFutureFlow(rh).map { flow => Right(flow) }
case HandlerResult(r, None) => Left(r)
}
// wsFutureFlow(rh).map { flow =>
// Right(flow)
// }.recover {
// case e: Exception =>
// logger.error("Cannot create websocket", e)
// val jsError = Json.obj("error" -> "Cannot create websocket")
// val result = InternalServerError(jsError)
// Left(result)
// }
}
private def wsFutureFlow(request: RequestHeader): Future[Flow[JsValue, JsValue, NotUsed]] = {
// Use guice assisted injection to instantiate and configure the child actor.
implicit val timeout = Timeout(1.second) // the first run in dev can take a while :-(
val future: Future[Any] = userParentActor ? UserParentActor.Create(request.id.toString)
val futureFlow: Future[Flow[JsValue, JsValue, NotUsed]] = future.mapTo[Flow[JsValue, JsValue, NotUsed]]
futureFlow
}
I'm a beginner to scala and essentially what I am trying to do is authenticate the request to the ws endpoint. If it's authenticated, then I give it a Flow[JsValue, JsValue, None] to act as the WebSocket connection otherwise I need to return a Result. The issue I am having is I can't quite figure out how to design the futures correctly. For context, here is the authenticated endpoint example in the documentation https://www.silhouette.rocks/docs/endpoints.
The line that doesn't compile is the below:
case HandlerResult(_, Some(_)) => wsFutureFlow(rh).map { flow => Right(flow) }
The function I'm passing to WebSocket.acceptOrResult[JsValue, JsValue] needs to return a Future[Either[Result, Flow[In, Out, _]]]. As you can see in the line that doesn't compile I'm trying to Right() the Flow, but it's not quite right. The commented section does compile though. Here is the compile error
[error] /home/julian/IdeaProjects/crypto-bloomberg-app/app-admin/src/main/scala/admin/controllers/HomeController.scala:32:62: type mismatch;
[error] found : scala.concurrent.Future[scala.util.Right[Nothing,akka.stream.scaladsl.Flow[play.api.libs.json.JsValue,play.api.libs.json.JsValue,akka.NotUsed]]]
[error] required: Either[play.api.mvc.Result,akka.stream.scaladsl.Flow[play.api.libs.json.JsValue, play.api.libs.json.JsValue, _]]
[error] case HandlerResult(_, Some(_)) => wsFutureFlow(rh).map { flow => Right(flow) }
[error] ^
[error] one error found
[error] (app-admin/compile:compileIncremental) Compilation failed
[error] Total time: 4 s, completed 21-Mar-2018 4:29:04 PM
Thanks
Use flatMap instead of map:
def ws: WebSocket = WebSocket.acceptOrResult[JsValue, JsValue] { rh =>
implicit val req = Request(rh, AnyContentAsEmpty)
silhouette.SecuredRequestHandler { securedRequest =>
Future.successful(HandlerResult(Ok, Some(securedRequest.identity)))
} flatMap {
case HandlerResult(_, Some(_)) => wsFutureFlow(rh).map(Right(_))
case HandlerResult(r, None) => Future.successful(Left(r))
}
}

Akka Http client type mismatch

Can anyone tell me why I'm getting the following error?:
[error] HttpClient.scala:117: type mismatch;
[error] found : akka.stream.scaladsl.Sink[(akka.http.scaladsl.model.StatusCode, String),scala.concurrent.Future[(akka.http.scaladsl.model.StatusCode, String)]]
[error] required: akka.stream.Graph[akka.stream.SinkShape[(akka.http.scaladsl.model.StatusCode, String)],scala.concurrent.Future[akka.http.scaladsl.model.StatusCode]]
[error] source.via(flow).runWith(Sink.head)
[error] ^
Here's the code:
implicit def map2entity: ToEntityMarshaller[Map[String, Any]] = mapMarshaller(MediaTypes.`application/json`)
def mapMarshaller(mediaType: MediaType.WithFixedCharset): ToEntityMarshaller[Map[String, Any]] =
Marshaller.withFixedContentType(mediaType) { m => HttpEntity(mediaType, JSONObject(m).toString()) }
def post(path: String, entity: Map[String, Any]): Future[StatusCode] = {
val uri = Uri(getResourceUri(path))
logger.info(s"httpPost: $uri")
Marshal(entity).to[RequestEntity] flatMap { e =>
val source = Source.single(HttpRequest(
uri = uri.path.toString,
method = HttpMethods.POST,
entity = e))
val flow = getConnection(uri.scheme)(uri.authority.host.address)
.mapAsync(10) { r =>
//(r.status -> Marshal(r.entity).to[String])
Unmarshal(r.entity).to[String].flatMap(s => Future(r.status -> s))
}
source.via(flow).runWith(Sink.head)
}
}
The materialized value of your sink (Future[(StatusCode, String)]) is different from the return type you declared in the function (Future[StatusCode]).
If you post function only needs to return the status code, you can change this call
.flatMap(s => Future(r.status -> s))
To this
.map(_ => r.status)

Slick select using value object (extending AnyVal)

I am using Slick 2.1.0 with Scala 2.10.
I have a value object (EMail) that I map to a VARCHAR column.
object EMail {
import java.util.regex.Pattern
val emailRegex = Pattern.compile("^[A-Z0-9._%+-]+#[A-Z0-9.-]+\\.[A-Z]{2,6}$", Pattern.CASE_INSENSITIVE)
val empty = EMail("x#y.com")
}
case class EMail(value: String) extends AnyVal with MappedTo[String] {
def isValid: Boolean = EMail.emailRegex.matcher(value).find
def validate: EMail = {
assert(isValid)
EMail(value.trim.toLowerCase)
}
override def toString = validate.value
}
The Column definition is:
def email = column[Option[EMail]]("email_address")
This is my attempt at writing a finder:
def findByEmail(email: Option[EMail]): Option[User] =
database.withSession { implicit session: Session =>
queryAll.filter(e => e.email.isDefined && e.email === email).firstOption
}
This is the error message I get:
[error] Users.scala:52: ambiguous implicit values:
[error] both value BooleanCanBeQueryCondition in object CanBeQueryCondition of type => scala.slick.lifted.CanBeQueryCondition[Boolean]
[error] and value BooleanOptionColumnCanBeQueryCondition in object CanBeQueryCondition of type => scala.slick.lifted.CanBeQueryCondition[scala.slick.lifted.Column[Option[Boolean]]]
[error] match expected type scala.slick.lifted.CanBeQueryCondition[Nothing]
[error] queryAll.filter(_.email === email.map(_.value)).firstOption
[error] ^
You don't have to pass an Option[EMail] to your method, you could simply make:
def findByEmail(email: EMail): Option[User] = {
database.withSession { implicit session =>
queryAll.filter(_.email === email).firstOption
}
}
If you really need an Option[EMail]] as the input parameter, you should try the following:
def findByEmail(emailOpt: Option[EMail]): Option[User] = {
emailOpt.flatMap { email =>
database.withSession { implicit session =>
queryAll.filter(_.email === email).firstOption
}
}
}