Skip to content

Strange api transformation behaviour #41

@wookievx

Description

@wookievx

Recently i encountered very strange bug, that was very hard to pinpoint. I will provide example (maybe not minimal but this exact combination causes the failure, in this case UUID as param type and Server.Match element) that should reproduce on version 0.2.0.
I have the following endpoint definition:

val Api = := :> Segment[UUID]("param") :> Server.Match[String]("headers") :> ReqBody[Json, TypeA] :> Put[Json, TypeB]

Now i work with it as always, so i define serialization/deserialization mechanism for request/response, have to define ValueExtractor for UUID and then derive endpoint from it and mount it (using http4s) as backend (i wrote custom integration with the newest version but it is exposing the same types as the standard one):

val endpoint = derive[F](Api).from(func)
val sm = ServerManager(BlazeServerBuilder[F], "0.0.0.0", 8080)
mount(sm, endpoint)

The following code does not compile resulting with compiler error like the following:
could not find implicit value for parameter executor: typedapi.server.EndpointExecutor.Aux[Req,typedapi.shared.SegmentInput :: typedapi.shared.ServerHeaderMatchInput :: shapeless.HNil,String("param") :: String("headers") :: Symbol with shapeless.tag.Tagged[String("body")] with shapeless.labelled.KeyTag[typedapi.dsl.MT.application/json.type,Symbol with shapeless.tag.Tagged[String("body")]] :: shapeless.HNil,java.util.UUID :: scala.collection.immutable.Map[String,String] :: TypeA :: shapeless.HNil,typedapi.shared.PutWithBodyCall,this.Out,F,TypeB,Resp]

I started digging into this error and what i found out is that somehow this.Out is infered to the wrong type by the compiler, or something like that because when i wrote the following custom http4s executor, the same endpoint compiled :

trait SimpleExecutor[R, VIn <: HList, Rout, F[_], FOut, Out] {
  def execute(req: R, eReq: EndpointRequest, endpoint: Endpoint[_, _, VIn, _, Rout, F, FOut]): Either[ExtractionError, Out]
}

//instance very similar to the default http4s executor instance
implicit def noReqBodySimple[VIn <: HList, ROut, F[_], FOut](implicit
    encoder: EntityEncoder[F, FOut],
    ME: Effect[F]
  ): SimpleExecutor[Request[F], VIn, ROut, F, FOut, F[Response[F]]] = ???

//with body instance
implicit def withReqBodySimple[VIn <: HList, ROut <: HList, Bd, F[_], FOut](implicit
    encoder: EntityEncoder[F, FOut],
    ME: Effect[F],
    decoder: EntityDecoder[F, Bd],
    _prepend: Prepend[ROut, Bd :: HNil]
  ): SimpleExecutor[Request[F], VIn, (BodyType[Bd], ROut), F, FOut, F[Response[F]]] = ???

//mounting function, again analogous
def mountHttp4s[VIn <: HList, ROut, M <: MethodType, F[_], FOut](
    server: ServerManager[BlazeServerBuilder[F]],
    endpoint: Endpoint[_, _, VIn, M, ROut, F, FOut]
  )(implicit
    executor: SimpleExecutor[Request[F], VIn, ROut, F, FOut, F[Response[F]]],
    mounting: MountEndpoints.Aux[BlazeServerBuilder[F], Request[F], F[Response[F]], Resource[F, Server[F]]]
  ): Resource[F, Server[F]] = ???

The most important thing i changed is that i erased the information that Rout is equal to VIn and simply casted the value when needed (assuming that it would be equal anyway which only made sense).
Now using mountHttp4s code compiles and runs but what is happening is that Rout is actually a reversal of VIn so i get the following error message when trying to invoke this endpoint:
scala.collection.immutable.Map$EmptyMap$ cannot be cast to java.util.UUID
I checked the original definition of executor for http4s and did not found any part which "reversed" input argument.

I am really puzzled here because the issue seems to be non-existent when using Get method (standard mount works great), it's not only request body issue because for example Delete also fails to compile (and fails at runtime using my special Executor implementation)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions