DefaultMarshallers missing with scala and spray-routing - scala

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 ?

Related

What is the correct way to register generic type to spray-json-support in akka-http Scala?

So, I have this class:
case class Something[T](data: Option[T] = None)
And i register it like the instruction said in https://github.com/spray/spray-json and in https://doc.akka.io/docs/akka-http/current/common/json-support.html. Like this:
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
import spray.json.DefaultJsonProtocol
trait InternalJsonFormat extends SprayJsonSupport with DefaultJsonProtocol {
import spray.json._
implicit def somethingFormat[A :JsonFormat] = jsonFormat1(Something.apply[A])
}
And last i used complete from akka http directive. Like this:
import akka.http.scaladsl.server.Directives._
object ResponseIt extends InternalJsonFormat {
def apply[T](rawData: T) = {
val theResponse = Something(data = Some(rawData))
complete(theResponse)
}
}
And then i get an error in complete(theResponse). It said
Type mismatch, expected: ToResponseMarshallable, actual: Something[T]
===========================================================
I have try to edit the last code for debugging purpose, like this:
object ResponseIt extends InternalJsonFormat {
import spray.json._
def apply[T](rawData: T) = {
val theResponse = Something(data = Some(rawData))
val trying = theResponse.toJson
complete(theResponse)
}
}
and get new error in val trying = theResponse.toJson. like this:
No implicits found for parameter writer: JsonWriter[Something[T]]
So, i really confused what is wrong in my code?. Is there any correct way to use the spray json support in akka http?
Thanks in advance
You see, there is no evidence for existence of JsonFormat for your T here:
def apply[T](rawData: T) = {
// ^--- here
val theResponse = Something(data = Some(rawData))
val trying = theResponse.toJson
complete(theResponse)
}
One can rewrite this method to provide JsonFormat for generic T:
def apply[T](rawData: T)(implicit formatter: JsonFormat[T])

Returning a JSON array in Akka Http

I have an Akka HTTP server with routing defined like this:
case class FooResults(results: Seq[Tuple2[String, Tuple2[Double, Double]]])
object MainApp extends App with JsonSupport {
...
lazy val routes: Route =
pathPrefix("foo") {
pathEnd {
get {
entity(as[String]) { str =>
val results =
(fooActor ? Foo(str)).mapTo[FooResults]
complete(results)
}
}
}
}
...
And in the class I have injected the implicit json support:
trait JsonSupport extends SprayJsonSupport {
import DefaultJsonProtocol._
implicit val userFormat = jsonFormat1(FooResults)
}
Somehow sbt still reports with
Type mismatch - FooResults with ToResponseMashallable
Anyone had similar problems? Thanks!
I figured out myself. It was because there're two SprayJsonSupport classes in my project:
import spray.httpx.SprayJsonSupport
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
Now obviously the latter is the correct one. Guess along the way since both Scala and Akka are evolving (fast), sometimes it becomes confusing with the namespaces and classes.

Illegal inheritance, superclass X not a subclass of the superclass Y of the mixin trait Z - Scala

I am trying to execute a akka-http which is a scala program. My KISS code is as follows:-
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.{HttpRequest, HttpResponse}
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.directives.BasicDirectives
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.Flow
import com.typesafe.config.ConfigFactory
object MyBoot02 extends SprayCanBoot02 with RestInterface02 with App {
}
abstract class SprayCanBoot02 {
val config = ConfigFactory.load()
val host = config.getString("http.host")
val port = config.getInt("http.port")
implicit val system = ActorSystem("My-ActorSystem")
implicit val executionContext = system.dispatcher
implicit val materializer = ActorMaterializer()
//implicit val timeout = Timeout(10 seconds)
implicit val routes: Flow[HttpRequest, HttpResponse, Any]
Http().bindAndHandle(routes, host, port) map {
binding => println(s"The binding local address is ${binding.localAddress}")
}
}
trait RestInterface02 extends AncileDemoGateway02 with Resource02 {
implicit val routes = questionroutes
val buildMetadataConfig = "this is a build metadata route"
}
trait Resource02 extends QuestionResource02
trait QuestionResource02 {
val questionroutes = {
path("hi") {
get {
complete("questionairre created")
}
}
}
}
class AncileDemoGateway02 {
println("Whatever")
}
The error that I get is because of how I am wiring stuff when trying to execute MyBoot02. The error is as follows:
Error:(58, 41) illegal inheritance; superclass SprayCanBoot is not a
subclass of the superclass AncileDemoGateway of the mixin trait
RestInterface object MyBoot extends SprayCanBoot with RestInterface
with App
Why does the error state 'SprayCanBoot is not a subclass of the superclass AncileDemoGateway'. In my code SprayCanBoot and AncileDemoGateway are 2 separate entities then why such an error?
Thanks
Unfortunately, for some mysterious reason, that's probably inherited from the wonderful design of Java, you cannot extend more than one class either directly or indirectly.
It is possible to mix in as many traits as you want, but you can only have one superclass in the hierarchy (ok, technically, you can have more than one, but they all must be extending each other - that's why your error message is complaining about SprayBoot not being a subclass).
In your case, you are extending SprayCanBoot02, which is a class, and also RestInterface02, that extends AncileDemoGateway02, which is also a class. So MyBoot02 is trying to extend two different classes at once, which is illegal.
Making AncileDemoGateway02 a trait should fix the error.
In general, a trait inheriting from a class is commonly used to limit the classes a trait can be mixed into.
In your case, the MyBoot02 class can’t extend the RestInterface02 trait, because MyBoot02 and RestInterface02 don’t share the same superclass.

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.

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.