guardrail-dev / guardrail

Principled code generation from OpenAPI specifications
https://guardrail.dev
MIT License
526 stars 133 forks source link

Multiple date query params generate ambiguous implicit values #290

Closed kkreuning closed 2 years ago

kkreuning commented 5 years ago

When I run the http4s server code generator with the following (minimal) specification

openapi: "3.0.0"
 info:
   title: Generator Error Sample
   version: 1.0.0
 paths:
   /events:
     parameters:
       - $ref: '#/components/parameters/FromParam'
       - $ref: '#/components/parameters/ToParam'
     get:
       operationId: getEvents
       responses:
         200:
           description: Requested events
 components:
   parameters:
     FromParam:
       name: from
       in: query
       schema:
         type: string
         format: date
     ToParam:
       name: to
       in: query
       schema:
         type: string
         format: date

It generates the following code:

Http4sDsl[F] {
  implicit val toQueryParamDecoder: QueryParamDecoder[java.time.LocalDate] = (value: QueryParameterValue) => Json.fromString(value.value).as[java.time.LocalDate].leftMap(t => ParseFailure("Query decoding failed", t.getMessage)).toValidatedNel
  implicit val fromQueryParamDecoder: QueryParamDecoder[java.time.LocalDate] = (value: QueryParameterValue) => Json.fromString(value.value).as[java.time.LocalDate].leftMap(t => ParseFailure("Query decoding failed", t.getMessage)).toValidatedNel
  object GetEventsToMatcher extends OptionalQueryParamDecoderMatcher[java.time.LocalDate]("to")
  object GetEventsFromMatcher extends OptionalQueryParamDecoderMatcher[java.time.LocalDate]("from")
  def routes(handler: Handler[F]): HttpRoutes[F] = HttpRoutes.of {
    {
      case req @ GET -> Root / "events" :? GetEventsFromMatcher(from) +& GetEventsToMatcher(to) =>
        handler.getEvents(GetEventsResponse)(from, to) flatMap {
          case GetEventsResponse.Ok =>
            Ok()
        }
    }
  }
}

When I compile, I get the following error:

[error] /.../Routes.scala:40:42: ambiguous implicit values:
[error]  both value fromQueryParamDecoder in class Resource of type => org.http4s.QueryParamDecoder[java.time.LocalDate]
[error]  and value toQueryParamDecoder in class Resource of type => org.http4s.QueryParamDecoder[java.time.LocalDate]
[error]  match expected type org.http4s.QueryParamDecoder[java.time.LocalDate]
[error]   object GetEventsFromMatcher extends OptionalQueryParamDecoderMatcher[java.time.LocalDate]("from")
[error]                                          ^
[error] /.../Routes.scala:42:40: ambiguous implicit values:
[error]  both value fromQueryParamDecoder in class Resource of type => org.http4s.QueryParamDecoder[java.time.LocalDate]
[error]  and value toQueryParamDecoder in class Resource of type => org.http4s.QueryParamDecoder[java.time.LocalDate]
[error]  match expected type org.http4s.QueryParamDecoder[java.time.LocalDate]
[error]   object GetOccasionsToMatcher extends OptionalQueryParamDecoderMatcher[java.time.LocalDate]("to")

The two generated implicits are prefixed with the parameter but have the same type signature and body. If we prefix the implicit ParamDecoder with the param's type instead of the param's name and deduplicate those this problem is solved.

I suspect this problem also surfaces with types other than date but I haven't tested this.

blast-hardcheese commented 2 years ago

Didn't realize this was already resolved, closing.