Spray.io case class extraction error - scala

I'm learning spray.io, and I'm stuck in a problem.. I'm trying to test the case class extraction of several GET parameters. My code is inspired by the examples from the documentation page :
package com.example
import akka.actor.Actor
import spray.routing._
import spray.http._
import MediaTypes._
class ServiceActor extends Actor
with ServiceHello {
def actorRefFactory = context
def receive = runRoute(testRoute)
trait ServiceHello extends HttpService with Controls {
case class Color(keyword: String, sort_order: Int, sort_key: String)
val route =
path("test") {
parameters('keyword.as[String], 'sort_order.as[Int], 'sort_key.as[String]).as(Color) { color =>
handleTestRoute(color) // route working with the Color instance
}
}
}
This code should be ok, but when I try to run it, I got the following errors :
Cannot resolve symbol as (on top of "as(Color)" )
Missing parameter type: color (on top of "{ color =>")
I understand these errors, but i do not understand why they come...
I'm running Scala 2.10.3
Am I doing something wrong ?

Thats working for me (scala 2.10.4, spray 1.3.1):
import akka.actor.Actor
import spray.routing._
import spray.http._
import MediaTypes._
class ServiceActor extends Actor with ServiceHello {
def actorRefFactory = context
def receive = runRoute(testRoute)
}
trait ServiceHello extends HttpService {
case class Color(keyword: String, sort_order: Int, sort_key: String)
val testRoute =
path("test") {
parameters('keyword.as[String], 'sort_order.as[Int], 'sort_key.as[String]).as(Color) { color =>
//handleTestRoute(color) // route working with the Color instance
complete {
<h1>test route</h1>
}
}
}
}
Don't know what Controls is, so I just commented it.

Related

Cannot understand Scala method definition

This is some code from Alvin Alexander's minimal Scala Websocket server.
package controllers
import play.api.mvc._
import play.api.libs.streams.ActorFlow
import javax.inject.Inject
import akka.actor.ActorSystem
import akka.stream.Materializer
import models.SimpleWebSocketActor
import play.api.libs.json._
class WebSocketsController #Inject() (cc: ControllerComponents)(implicit system: ActorSystem)
extends AbstractController(cc)
{
def index = Action { implicit request: Request[AnyContent] =>
logger.info("index page was called")
Ok(views.html.index())
}
}
What I'm curious about is the method definition for index. The notation is different to the normal method definition I've seen in Scala. Could someone explain what is going on here, and possibly point to some docs on this syntax? I haven't been able to find anything.
This definition is a combination of several syntax features of Scala but in the end there's nothing that special about it.
It's equivalent to something like this:
def index = {
def handleRequest(request: Request[AnyContent]) = {
implicit req = request
logger.info("index page was called")
Ok(views.html.index())
}
Action.apply(handleRequest) // Replaced by Action(...), then Action { ... }
}

Struggling with generic(s) repository methods

I have a Language model, table and repository. So far this works:
package repositories
import javax.inject.Inject
import Helper
import model.{Language, LanguageModel}
import play.api.Logger
import play.api.cache.SyncCacheApi
import slick.jdbc.JdbcProfile
import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success}
class LanguageRepository #Inject()(cache: SyncCacheApi, jdbcProfile: JdbcProfile, implicit val executionContext: ExecutionContext)
{
private val model = new LanguageModel(jdbcProfile)
import jdbcProfile.api._
def all(userName: String): Future[Seq[Language]] =
{
cache.get[Future[Seq[Language]]](buildCacheKey(userName)) match
{
case Some(x) => {Logger.info("[LanguageRepository](all) Found something in cache"); x}
case None => {
Logger.info("[LanguageRepository](all) Nothing useful to be found in cache, calling database now")
val result = retrieve(userName)
result.onComplete{
case Success(value) => if(!value.isEmpty) cache.set(buildCacheKey(userName), result)
case Failure(e) => ()
}
result
}
}
}
private def retrieve(userName: String): Future[Seq[Language]] =
{
// TODO extract this to a repositoryTrait and implement fallbacks etc
val db = Database.forURL(Helper.getDbUrl(), driver = Helper.getDbDriver())
db.run(model.all.result)
}
private def buildCacheKey(userName: String): String = s"$userName.languages"
}
Now I am struggling with the today past me left current me.
So I created this trait and wanted to let it be extended by LanguageRepository to get rid of that generic retrieve method that should be the same for all repositories/models. But sadly no luck so far:
trait Repository
{
type Entity
val model: Base
val profile: JdbcProfile
import profile.api._
protected def retrieve(userName: String): Future[Seq[Entity]] =
{
val db = Database.forURL(Helper.getDbUrl(), driver = Helper.getDbDriver())
db.run(model.all.result)
}
}
This is base:
trait Base
{
val dbProfile: JdbcProfile
import dbProfile.api._
type Entity
type EntityTable <: Table[Entity]
lazy val all = TableQuery[EntityTable]
}
Here I get one error >> class type required but Base.this.EntityTable found
class LanguageModel(databaseProfile: JdbcProfile) extends Base
{
override val dbProfile: JdbcProfile = databaseProfile
import dbProfile.api._
...
override type EntityTable = LanguageTable
}
Repository itself is not compiling either, because the types don't match. There are multiple problems and I am not sure where to start to solve them.
Your base table definition won't work like that. You need class types, maybe you should generics instead. Also, instead of creating multiple abstractions, start with only one abstraction and evolve from there. Try something along these lines:
class Repository[A, B <: Table[A]](t: TableQuery[B]) {
val model = t
//protected def retrieve ..
}
class LanguageModel(databaseProfile: JdbcProfile) extends Repository[Language, LanguageTable](TableQuery[LanguageTable]) {
//...
}
Get everything to compile first, then start adding the abstraction one class at a time.

Getting implicit val from the companion object

I'm playing with Scala Spray. I enjoy working with it but can't figure out one thing.
This code compiles fine:
import spray.http.MediaTypes._
import spray.routing.HttpService
import spray.json.{DefaultJsonProtocol, _}
import spray.httpx.SprayJsonSupport._
trait StatusService extends HttpService {
case class StatusResponse(status: String)
object StatusResponseProtocol extends DefaultJsonProtocol {
implicit val statusResponse = jsonFormat1(StatusResponse)
}
import StatusResponseProtocol._
val statusRoute =
path("status") {
get {
respondWithMediaType(`application/json`) {
complete {
StatusResponse("OK")
}
}
}
}
}
But it doesn't compile (can't find json serializer) when I move case class & protocol to the companion object.
trait StatusService extends HttpService {
import StatusResponseProtocol._
val statusRoute =
path("status") {
get {
respondWithMediaType(`application/json`) {
complete {
StatusResponse("OK")
}
}
}
}
}
object StatusService {
case class StatusResponse(status: String)
object StatusResponseProtocol extends DefaultJsonProtocol {
implicit val statusResponse = jsonFormat1(StatusResponse)
}
}
I do not understand why..
I think the problem might be in your import statement. If you import from a companion object, it should be done like this:
trait StatusService extends HttpService {
import StatusService.StatusResponseProtocol._
The rest of the code doesn't have to be changed.
I think I've had pretty much the same problem. Try replacing:
import spray.httpx.SprayJsonSupport._
with
import spray.json._
It worked for me.
I noticed today (in another context, not Spray) that providing a type for the implicit val in a companion object made it visible.
So, I'm thinking whether this would make it work in your case:
implicit val statusResponse: RootJsonFormat[StatusResponse] = jsonFormat1(StatusResponse)
Note: I'm not sure of the type I added - it may not be what jsonFormat1 returns. Also, the Spray.json documentation does not use types. Anyways, if someone has the time to try this out, I'd be interested to know..

Spray won't convert my case class to json and expect a spray.httpx.marshalling.ToResponseMarshallable

I'm trying to reprocude this or this, but I keep getting an error I am not able to fix...
First of all, here are my dependencies:
compile 'io.spray:spray-can_2.11:1.3.1'
compile 'io.spray:spray-routing_2.11:1.3.1',
compile 'io.spray:spray-json_2.11:1.2.6'
Now what I'm trying to do is:
class WHttpService extends Actor with HttpService with ActorLogging {
implicit def actorRefFactory = context
def receive = runRoute(route)
lazy val route = logRequest(showReq _) {
// Way too much imports but I tried all I could find
import spray.json._
import DefaultJsonProtocol._
import MasterJsonProtocol._
import spray.httpx.SprayJsonSupport._
path("server" / Segment / DoubleNumber / DoubleNumber) { (login, first, second) =>
get {
complete {
Answer(1, "test")
}
}
}
}
private def showReq(req : HttpRequest) = LogEntry(req.uri, InfoLevel)
}
With:
case object MasterJsonProtocol extends DefaultJsonProtocol with SprayJsonSupport {
import spray.json._
case class Answer(code: Int, content: String)
implicit val anwserFormat: JsonFormat[Answer] = jsonFormat2(Answer)
}
Now I get this error:
Error:(42, 19) type mismatch;
found : MasterJsonProtocol.Answer
required: spray.httpx.marshalling.ToResponseMarshallable
Answer(1, "test")
^
I tried a lot of things but can't manage to make it works.
I tried with
Answer(1, "test").toJson
Answer(1, "test").toJson.asJsObject
Finally what I did was
complete {
Answer(1, "test").toJson.compactPrint
}
This works but it is sent to the client as Content-Type: text/plain when I need application/json.
Anyone see what the problem is here?
Edit: I added a sample project on github https://github.com/ydemartino/spray-test
Move your model outside of the json protocol and make it a regular object (not a case object)
case class Answer(code: Int, content: String)
object MasterJsonProtocol extends DefaultJsonProtocol {
implicit val anwserFormat = jsonFormat2(Answer)
}
Edit
Also clean up your imports:
class WHttpService extends Actor with HttpService with ActorLogging {
implicit def actorRefFactory = context
def receive = runRoute(route)
lazy val route = logRequest(showReq _) {
// Way too much imports but I tried all I could find
import MasterJsonProtocol._
import spray.httpx.SprayJsonSupport._
path("server" / Segment / DoubleNumber / DoubleNumber) { (login, first, second) =>
get {
complete {
Answer(1, "test")
}
}
}
}
private def showReq(req : HttpRequest) = LogEntry(req.uri, InfoLevel)
}
I created a pull request to fix your problem: https://github.com/ydemartino/spray-test/pull/1
The json protocol object has to be declared before it can be used implicitly. I'm not wholly sure why the compiler can't figure it out, but moving the object declaration to the top fixed it.
For your actual project make sure to declare packages in each file then use those packages to in the import statements.
In my case the name of the unresolvable implicit format instance conflicted with a local definition, so it got shadowed. The compiler was graciously silent about that. Only discovered that by accident after hours of head-banging.

DefaultMarshallers missing with scala and spray-routing

I'm new to Scala, and trying to write a little REST API.
Here is my route definition :
package com.example
import akka.actor.Actor
import com.example.core.control.CrudController
import spray.routing._
class ServiceActor extends Actor with Service {
def actorRefFactory = context
def receive = runRoute(routes)
}
trait Service extends HttpService {
val crudController = new CrudController()
val routes = {
path("ads" / IntNumber) { id =>
get {
complete(
crudController.getFromElasticSearch(id)
)
}
}
}
}
and here is my controller
package com.example.core.control
import com.example._
import org.elasticsearch.action.search.SearchResponse
import scala.concurrent._
import ExecutionContext.Implicits.global
class CrudController extends elastic4s
{
def getFromElasticSearch (id:Integer) : Future[String] = {
val result: Future[SearchResponse] = get
result onFailure {
case t: Throwable => println("An error has occured: " + t)
}
result map { response =>
response.toString
}
}
}
When I try to run this code, I got the following exception :
Error:(22, 58) could not find implicit value for parameter marshaller: spray.httpx.marshalling.ToResponseMarshaller[scala.concurrent.Future[String]]
crudController.getFromElasticSearch(id)
I understand quite well this error, spray needs an implicit marshaller in order to marshall my Future[String] Object. But I'm a little bit confused because in the documentation we can read
Scala compiler will look for an in-scope implicit Marshaller for your type to do the job of converting your custom object to a representation accepted by the client. spray comes with the following marshallers already defined (as implicit objects in the DefaultMarshallers trait) Source https://github.com/spray/spray/wiki/Marshalling-and-Unmarshalling
The required marshallers in my case belongs to DefaultMarshallers, I should not have to implicit him by myself.. Should I ?