I've been through the docs and many questions on SO, still can't figure out what I'm doing wrong.
I'm starting with a vanilla akka http g8 template, but I want to use a trait rather than a case class to represent the objects which are coming in POST requests.
When using case classes everything is fine in my app, but when I try to implement traits it fails with:
[error] CollectionRoutes.scala:38: could not find implicit value for parameter um: akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller[collector.CollectorConfig]
[error] entity(as[CollectorConfig]) { config =>
[error] ^
[error] CollectionRoutes.scala:55: could not find implicit value for parameter um: akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller[collector.CollectorStep]
[error] entity(as[CollectorStep]) { collstep =>
[error] ^
[error] CollectionRoutes.scala:77: could not find implicit value for parameter um: akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller[collector.CollectorConfig]
[error] entity(as[CollectorConfig]) { config =>
[error] ^
[error] three errors found
This is the trait I use to define the json formats:
trait JsonSupport extends DefaultJsonProtocol {
import spray.json._
implicit val collectionTypeConverter = new EnumJsonConverter(CollectionType)
implicit val actionPerformedJsonFormat = jsonFormat1(ActionPerformed)
implicit val restCollectorStepFormat = jsonFormat3(RestCollectorStep)
implicit object collectorStepFormat extends RootJsonFormat[CollectorStep] {
def write(a: CollectorStep) = a match {
case p: RestCollectorStep => p.toJson
}
def read(value: JsValue) =
value.asJsObject.fields("collectionType") match {
case JsString("Rest") => value.convertTo[RestCollectorStep]
}
}
implicit val restCollectorConfigFormat = jsonFormat5(RestCollectorConfig)
implicit object collectorConfigFormat extends RootJsonFormat[CollectorConfig] {
def write(a: CollectorConfig) = a match {
case p: RestCollectorConfig => p.toJson
}
def read(value: JsValue) =
value.asJsObject.fields("collectionType") match {
case JsString("Rest") => value.convertTo[RestCollectorConfig]
}
}
}
This is the trait which defines my routes:
trait CollectionRoutes extends JsonSupport {
import spray.json._
implicit def system: ActorSystem
lazy val log = Logging(system, classOf[CollectionRoutes])
def collectorRegistryActor: ActorRef
implicit lazy val timeout = Timeout(5.seconds) // usually we'd obtain the timeout from the system's configuration
val settings = CorsSettings.defaultSettings.withAllowGenericHttpRequests(true)
lazy val collectionRoutes: Route = cors(settings) {
pathPrefix("createcollector") {
concat(
pathEnd {
concat(
post {
entity(as[CollectorConfig]) { config =>
val collectorCreated: Future[CollectorRegistryActor.ActionPerformed] =
(collectorRegistryActor ? MakeCollector(config)).mapTo[CollectorRegistryActor.ActionPerformed]
onSuccess(collectorCreated) { created =>
complete(created.description)
}
}
}
)
}
)
} ~
pathPrefix("reststep") {
concat(
pathEnd {
concat(
post {
entity(as[CollectorStep]) { collstep =>
val executionResult: Future[StepActor.StepResponse] = (collectorRegistryActor ? ExecuteStep(collstep)).mapTo[StepActor.StepResponse]
onSuccess(executionResult) { res =>
res match {
case StepActor.StepResult(step, response, result) => {
val res = (response :: result :: Nil).toJson.toString
complete(res)
}
case StepActor.StepError(step, error) => complete(error)
}
}
}
}
)
}
)
} ~
pathPrefix("restcollect") {
concat(
pathEnd {
concat(
post {
entity(as[CollectorConfig]) { config =>
val executionResult: Future[CollectorActor.CollectionResult] = (collectorRegistryActor ? ExecuteCollection(config)).mapTo[CollectorActor.CollectionResult]
onSuccess(executionResult) { response =>
complete(response.result)
}
}
}
)
}
)
}
}
}
And this is how that trait is being used.
object CollectionServer extends App with CollectionRoutes {
implicit val system: ActorSystem = ActorSystem("CollectionServer")
implicit val materializer: ActorMaterializer = ActorMaterializer()
val collectorRegistryActor: ActorRef = system.actorOf(CollectorRegistryActor.props, "collectorRegistryActor")
lazy val routes: Route = collectionRoutes
Http().bindAndHandle(routes, "localhost", 9100)
println(s"Server online at http://localhost:9100/")
Await.result(system.whenTerminated, Duration.Inf)
}
I'm baffled because I believe I've set everything up in a very standard way, but it's not picking up the implicits.
Any ideas appreciated - thanks.
Figured this out:
I should have been extending SprayJsonSupport rather than DefaultJsonProtocol
trait JsonSupport extends SprayJsonSupport {