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

Multiple routes with difference Accept headers results in swagger.json with incomplete produces #278

Open
balthz opened this issue Dec 29, 2018 · 2 comments

Comments

@balthz
Copy link

balthz commented Dec 29, 2018

(I'm filing this per discussion with @zarthross on Gitter.)

How to reproduce

import cats.effect.{ExitCode, IO, IOApp, Sync}
import io.circe.generic.auto._
import org.http4s.headers._
import org.http4s.rho.swagger
import org.http4s.server.blaze.BlazeServerBuilder
import org.http4s.syntax.kleisli.http4sKleisliResponseSyntax
import org.http4s.{EntityEncoder, MediaType}
import org.http4s.rho.RhoRoutes

case class SampleResponse(msg: String)

class Routes[F[_]: Sync] extends RhoRoutes[F] {
  def accept(mediaType: MediaType) =
    existsAnd(Accept) { (hdr: Accept) 
      hdr.values.exists(_.mediaRange.satisfiedBy(mediaType))
    }

  {
    implicit val entityEncoder: EntityEncoder[F, SampleResponse] = org.http4s.circe.CirceEntityEncoder.circeEntityEncoder[F, SampleResponse]
    GET / "text-or-json" >>> accept(MediaType.application.json) |>> { ()  Ok(SampleResponse("some JSON")) }
  }
  {
    implicit val encoder: EntityEncoder[F, SampleResponse] = EntityEncoder.stringEncoder[F].contramap(_.toString)
    GET / "text-or-json" >>> accept(MediaType.text.plain)       |>> { ()  Ok(SampleResponse("some text")) }
  }
}

object Bug extends IOApp {
  override def run(args: List[String]): IO[ExitCode] = {
    val httpApp = new Routes[IO]()
      .toRoutes(swagger.syntax.io.createRhoMiddleware())
        .orNotFound

    BlazeServerBuilder[IO]
      .bindHttp(8081, "localhost")
      .withHttpApp(httpApp)
      .resource
      .use(server  IO.never)
  }
}

Once this is running, I did a

curl -v http://localhost:8081/swagger.json

and got:

  "paths" : {
    "/text-or-json" : {
      "get" : {
        "tags" : [ "text-or-json" ],
        "operationId" : "getText-or-json-Accept",
        "produces" : [ "text/plain" ],
        "parameters" : [ {
          "name" : "Accept",
          "in" : "header",
          "required" : true,
          "type" : "string"
        } ],
        "responses" : {
          "200" : {
            "description" : "OK",
            "schema" : {
              "$ref" : "#/definitions/SampleResponse"
            }
          }
        },
        "deprecated" : false
      }
    }
  }

I'd have expected to see also "application/json" in the "produces" array.

Note

The actual routes work correctly:

$ http -v GET :8081/text-or-json Accept:text/plain
…
SampleResponse(some text)

$  http -v GET :8081/text-or-json Accept:application/json
{
   "msg": "some JSON"
}
@Igosuki
Copy link
Contributor

Igosuki commented Jun 19, 2019

Having this problem as well, I get consumes : application/*, which is what the swagger js client sends to http4s and so gets rejected with unprocessable entity since it expects application/json...

The culprit is :

  implicit val http4sShowForMediaRange: Show[MediaRange] =
    Show.show(s => s"${s.mainType}/*${MediaRange.extensionsToString(s)}")

@Igosuki
Copy link
Contributor

Igosuki commented Jun 19, 2019

Plus it's not possible to setup a default consumes that gets appended everywhere, doing so in swagger syntax doesn't work

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

No branches or pull requests

3 participants