{"action":"opened","issue":{"url":"https://api.github.com/repos/http4s/rho/issues/268","repository_url":"https://api.github.com/repos/http4s/rho","labels_url":"https://api.github.com/repos/http4s/rho/issues/268/labels{/name}","comments_url":"https://api.github.com/repos/http4s/rho/issues/268/comments","events_url":"https://api.github.com/repos/http4s/rho/issues/268/events","html_url":"https://github.com/http4s/rho/issues/268","id":379731629,"node_id":"MDU6SXNzdWUzNzk3MzE2Mjk=","number":268,"title":"Apperance of a sum type affects models of other unrelated types.","user":{"login":"RafalSumislawski","id":9465998,"node_id":"MDQ6VXNlcjk0NjU5OTg=","avatar_url":"https://avatars0.githubusercontent.com/u/9465998?v=4","gravatar_id":"","url":"https://api.github.com/users/RafalSumislawski","html_url":"https://github.com/RafalSumislawski","followers_url":"https://api.github.com/users/RafalSumislawski/followers","following_url":"https://api.github.com/users/RafalSumislawski/following{/other_user}","gists_url":"https://api.github.com/users/RafalSumislawski/gists{/gist_id}","starred_url":"https://api.github.com/users/RafalSumislawski/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/RafalSumislawski/subscriptions","organizations_url":"https://api.github.com/users/RafalSumislawski/orgs","repos_url":"https://api.github.com/users/RafalSumislawski/repos","events_url":"https://api.github.com/users/RafalSumislawski/events{/privacy}","received_events_url":"https://api.github.com/users/RafalSumislawski/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2018-11-12T11:19:49Z","updated_at":"2018-11-12T11:19:49Z","closed_at":null,"author_association":"CONTRIBUTOR","body":"Given the following code:\r\n```\r\npackage rho.test\r\n\r\nimport java.util.concurrent.TimeUnit\r\n\r\nimport cats.effect.IO\r\nimport io.circe.generic.semiauto.deriveEncoder\r\nimport io.circe.{Encoder, Printer}\r\nimport org.http4s._\r\nimport org.http4s.circe.CirceInstances\r\nimport org.http4s.rho.RhoService\r\nimport org.http4s.rho.bits._\r\nimport org.http4s.rho.swagger.SwaggerSupport\r\nimport org.http4s.server.blaze.BlazeBuilder\r\n\r\nimport scala.util.Random\r\n\r\nobject RhoTest2 extends App {\r\n\r\n  case class Baz(a: String)\r\n  case class Baz1(a: String)\r\n  sealed trait FooBar\r\n  case class Foo(f: String) extends FooBar\r\n  case class Bar(b: String) extends FooBar\r\n\r\n  implicit val bazEncoder: Encoder[Baz] = deriveEncoder\r\n  implicit val baz1Encoder: Encoder[Baz1] = deriveEncoder\r\n  implicit val fooBarEncoder: Encoder[FooBar] = deriveEncoder\r\n  implicit val fooEncoder: Encoder[Foo] = deriveEncoder\r\n  implicit val barEncoder: Encoder[Bar] = deriveEncoder\r\n\r\n  val mainService = new RhoService[IO] {\r\n\r\n    private val circeInstances = CirceInstances.withPrinter(Printer.spaces2)\r\n    private implicit def defaultJsonEncoder[A](implicit encoder: Encoder[A]): EntityEncoder[IO, A] =\r\n      circeInstances.jsonEncoderOf[IO, A](stringEncoder, implicitly, encoder)\r\n\r\n    private implicit def ioMatcher[R](implicit r: ResultMatcher[IO, R]): ResultMatcher[IO, IO[R]] = ResultMatcher.fMatcher\r\n\r\n    GET / \"baz\" |>> { () => Ok(Baz(\"baz\")) }\r\n    GET / \"baz1\" |>> { () => Ok(Baz1(\"baz\")) }\r\n    GET / \"foobar\" |>> { () => Ok((if (Random.nextBoolean()) Foo(\"foo\") else Bar(\"bar\")): FooBar) }\r\n  }\r\n\r\n  val http = BlazeBuilder[IO]\r\n    .bindHttp(55555, \"localhost\")\r\n    .mountService(mainService.toService(SwaggerSupport[IO].createRhoMiddleware()), \"/\")\r\n    .start.unsafeRunSync()\r\n\r\n  TimeUnit.SECONDS.sleep(100000)\r\n}\r\n```\r\none would expect that the models of `Baz` and `Baz1` should differ just in name and have no connection to the `FooBar`. Rho generates the following swagger.json:\r\n```\r\n{\r\n  \"swagger\" : \"2.0\",\r\n  \"info\" : {\r\n    \"version\" : \"1.0.0\",\r\n    \"title\" : \"My API\"\r\n  },\r\n  \"paths\" : {\r\n    \"/baz\" : {\r\n      \"get\" : {\r\n        \"tags\" : [ \"baz\" ],\r\n        \"operationId\" : \"getBaz\",\r\n        \"produces\" : [ \"application/json\" ],\r\n        \"responses\" : {\r\n          \"200\" : {\r\n            \"description\" : \"OK\",\r\n            \"schema\" : {\r\n              \"$ref\" : \"#/definitions/Baz\"\r\n            }\r\n          }\r\n        }\r\n      }\r\n    },\r\n    \"/baz1\" : {\r\n      \"get\" : {\r\n        \"tags\" : [ \"baz1\" ],\r\n        \"operationId\" : \"getBaz1\",\r\n        \"produces\" : [ \"application/json\" ],\r\n        \"responses\" : {\r\n          \"200\" : {\r\n            \"description\" : \"OK\",\r\n            \"schema\" : {\r\n              \"$ref\" : \"#/definitions/Baz1\"\r\n            }\r\n          }\r\n        }\r\n      }\r\n    },\r\n    \"/foobar\" : {\r\n      \"get\" : {\r\n        \"tags\" : [ \"foobar\" ],\r\n        \"operationId\" : \"getFoobar\",\r\n        \"produces\" : [ \"application/json\" ],\r\n        \"responses\" : {\r\n          \"200\" : {\r\n            \"description\" : \"OK\",\r\n            \"schema\" : {\r\n              \"$ref\" : \"#/definitions/FooBar\"\r\n            }\r\n          }\r\n        }\r\n      }\r\n    }\r\n  },\r\n  \"definitions\" : {\r\n    \"Baz1\" : {\r\n      \"type\" : \"object\",\r\n      \"required\" : [ \"a\" ],\r\n      \"properties\" : {\r\n        \"a\" : {\r\n          \"type\" : \"string\"\r\n        }\r\n      },\r\n      \"description\" : \"Baz1\"\r\n    },\r\n    \"Bar\" : {\r\n      \"allOf\" : [ {\r\n        \"$ref\" : \"#/definitions/FooBar\"\r\n      }, {\r\n        \"type\" : \"object\",\r\n        \"required\" : [ \"b\" ],\r\n        \"properties\" : {\r\n          \"b\" : {\r\n            \"type\" : \"string\"\r\n          }\r\n        },\r\n        \"description\" : \"Bar\"\r\n      } ],\r\n      \"description\" : \"Bar\"\r\n    },\r\n    \"FooBar\" : {\r\n      \"type\" : \"object\",\r\n      \"required\" : [ \"type\" ],\r\n      \"discriminator\" : \"type\",\r\n      \"properties\" : {\r\n        \"type\" : {\r\n          \"type\" : \"string\",\r\n          \"enum\" : [ \"Bar\", \"Foo\" ]\r\n        }\r\n      },\r\n      \"description\" : \"FooBar\"\r\n    },\r\n    \"Foo\" : {\r\n      \"allOf\" : [ {\r\n        \"$ref\" : \"#/definitions/FooBar\"\r\n      }, {\r\n        \"type\" : \"object\",\r\n        \"required\" : [ \"f\" ],\r\n        \"properties\" : {\r\n          \"f\" : {\r\n            \"type\" : \"string\"\r\n          }\r\n        },\r\n        \"description\" : \"Foo\"\r\n      } ],\r\n      \"description\" : \"Foo\"\r\n    },\r\n    \"Baz\" : {\r\n      \"allOf\" : [ {\r\n        \"$ref\" : \"#/definitions/FooBar\"\r\n      }, {\r\n        \"type\" : \"object\",\r\n        \"required\" : [ \"a\" ],\r\n        \"properties\" : {\r\n          \"a\" : {\r\n            \"type\" : \"string\"\r\n          }\r\n        },\r\n        \"description\" : \"Baz\"\r\n      } ],\r\n      \"description\" : \"Baz\"\r\n    }\r\n  }\r\n}\r\n```\r\n\r\nThe interesting part is the model of Baz:\r\n```\r\n\"Baz\" : {\r\n      \"allOf\" : [ {\r\n        \"$ref\" : \"#/definitions/FooBar\"\r\n      }, {\r\n        \"type\" : \"object\",\r\n        \"required\" : [ \"a\" ],\r\n        \"properties\" : {\r\n          \"a\" : {\r\n            \"type\" : \"string\"\r\n          }\r\n        },\r\n        \"description\" : \"Baz\"\r\n      } ],\r\n      \"description\" : \"Baz\"\r\n    }\r\n```\r\nThe model refers to `FooBar` as if `Baz` was a subtype of the `FooBar` sealed trait. But it isn't. \r\n\r\nI've tracked the issue to the line [92 in TypeBuilder](https://github.com/http4s/rho/blob/d05998fdbd2f15df0b18bdd7f30437db02c766f0/swagger/src/main/scala/org/http4s/rho/swagger/TypeBuilder.scala#L92 ), where `composedModel` is applied to all models known so far instead of just subclasses of the sum type being processed. `Baz1` remained unaffected because it happened to be processed after `FooBar`. The code reproducing this issue may be unreliable as it depends on the order of traversal of a `Set[ResultInfo]`. \r\n\r\nScala version: 2.12.7\r\nRho version: 0.18.0\r\n"}}
{"actor":{"display_login":"RafalSumislawski"}}