Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

possible to unmarshal generic subtypes? #203

Open
evbo opened this issue Apr 20, 2018 · 2 comments
Open

possible to unmarshal generic subtypes? #203

evbo opened this issue Apr 20, 2018 · 2 comments

Comments

@evbo
Copy link

evbo commented Apr 20, 2018

Hi,

Is it possible to unmarshall generic types? Here I've reproduced an example that fails to compile due to:

Error:(28, 88) could not find implicit value for parameter um: akka.http.scaladsl.unmarshalling.Unmarshaller[akka.http.scaladsl.model.HttpResponse,Requests.this.SomeResponse[T]]
val unmarshalled: Future[SomeResponse[T]] = response.flatMap(Unmarshal(_).to[SomeResponse[T]])

full REPL reproduction below. See comment:

import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.marshalling.Marshal
import akka.http.scaladsl.model.{HttpMethods, HttpRequest, HttpResponse, RequestEntity}
import akka.http.scaladsl.unmarshalling.Unmarshal
import akka.stream.ActorMaterializer
import de.heikoseeberger.akkahttpcirce.FailFastCirceSupport
import io.circe.generic.auto._

import scala.concurrent.duration._
import scala.concurrent.{Await, Future}


trait Requests extends FailFastCirceSupport {

  implicit val system = ActorSystem("testSystem")
  implicit val ec = system.dispatcher
  implicit val mat = ActorMaterializer()

  case class SomeRequest(query: String)
  case class SomeData[T](queryResponse: T)
  case class SomeResponse[T](data: SomeData[T])
  case class SampleData(hello: String)


  def parseContent[T <: SampleData](query: String) = {
    val response: Future[HttpResponse] = requestContent(SomeRequest(query))

// See Comment:
// This line wont compile unless I replace T with an explicit type like SampleData. 
// Why? Is it be possible to use T somehow?
    val unmarshalled: Future[SomeResponse[T]] = response.flatMap(Unmarshal(_).to[SomeResponse[T]])

    val hello: Future[String] = for {
      root <- unmarshalled
    } yield root.data.queryResponse.hello

    Await.result[String](hello, 30 seconds)
  }

  def requestContent(request: SomeRequest): Future[HttpResponse] = {
    for {
      request <- Marshal(request).to[RequestEntity]
      response <- Http().singleRequest(HttpRequest(
        method = HttpMethods.POST,
        uri = "http://localhost:8000/Some",
        entity = request
      ))
    } yield response
  }
}

object OtherRequests extends Requests {
  val query =
    """
        query GetSomething {
          results: getSomething() {
            hello
          }
        }
      """.replaceAll("//\\d+", "")

  def getSome() = {
    parseContent[SampleData](query)
  }
}

OtherRequests.getSome()
@akauppi
Copy link

akauppi commented Dec 1, 2018

Have you tried moving the FailFastCirceSupport so that it would not affect the place of your problem.

I struggled with similar today:

package test

import akka.http.scaladsl.model.{ContentTypes, StatusCodes}
import akka.http.scaladsl.testkit.ScalatestRouteTest
import de.heikoseeberger.akkahttpcirce.FailFastCirceSupport
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.{FlatSpec, Matchers}
import renamed.data.Fake

class RouteTest extends FlatSpec with Matchers with ScalatestRouteTest /*with FailFastCirceSupport*/ with ScalaFutures {
  import renamed.fake.AppRoute.route
  import io.circe.generic.auto._

  behavior of "GET /ping"

  it should "return 'pong'" in {
    Get("/ping") ~> route ~> check {
      status shouldBe StatusCodes.OK
      contentType shouldBe ContentTypes.`text/plain(UTF-8)`

      entityAs[String] shouldBe "pong"
    }
  }

  behavior of "GET /mock"

  // Note: Keep this away from code needing 'entityAs[String]' - it causes them not to compile.
  //
  import FailFastCirceSupport._

  it should "provide a valid 'Fake' object as JSON" in {
    Get("/mock") ~> route ~> check {
      status shouldBe StatusCodes.OK
      contentType shouldBe ContentTypes.`application/json`

      val o: Fake = entityAs[Fake]    // decode
      info(o.toString)
    }
  }
}

Bringing FailFastCirceSupport in - while using akka.http.scaladsl.testkit.ScalatestRouteTest - seems to shadow the unmarshalling support needed to make EntityAs[String] work.

It's not a big thing, but debugging this kind of things does take time.

@decoursin
Copy link

Agreed with @akauppi this was and remains a pain for me. It would be really nice if there was a better solution than what @akauppi suggests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants