-
Notifications
You must be signed in to change notification settings - Fork 136
Description
I've been getting errors when generating code for a YAML OpenAPI 3 spec (this one, incidentally). They appear to be happen when one of the enum properties of a component is the same as the component itself. The cause seems to be that attempts to reference the component class are taken to be references to the identically named property object within the class when such an object happens to exist. (This doesn't happen for standard, non-enum strings as no child object is generated.)
The conflict is not case-sensitive, as generated class/object names are automatically camel cased.
I think the suggested fix for #1147 - fully qualifying component class names within generated code - would probably fix this as well.
I've narrowed this down into a minimal failing case - I get the following error when attempting to generate code for the specification below it by running sbt compile on a project using sbt-guardrail 1.0.0-M1.
Error
[info] compiling 1 Scala source to C:\...\untitled\target\scala-2.13\classes ...
[error] C:\...\untitled\target\scala-2.13\src_managed\main\nhclosures\definitions\Test.scala:13:129: value test is not a member of nhclosures.definitions.Test.Test
[error] _root_.io.circe.Encoder.AsObject.instance[Test](a => _root_.io.circe.JsonObject.fromIterable(_root_.scala.Vector(("test", a.test.asJson))))
[error] ^
[error] C:\...\untitled\target\scala-2.13\src_managed\main\nhclosures\definitions\Test.scala:15:219: type Test is not a member of object nhclosures.definitions.Test.Test
[error] implicit val decodeTest: _root_.io.circe.Decoder[Test] = new _root_.io.circe.Decoder[Test] { final def apply(c: _root_.io.circe.HCursor): _root_.io.circe.Decoder.Result[Test] = for (v0 <- c.downField("test").as[Test.Test]) yield Test(v0) }
[error] ^
[error] C:\...\untitled\target\scala-2.13\src_managed\main\nhclosures\definitions\Test.scala:15:236: nhclosures.definitions.Test.Test.type does not take parameters
[error] implicit val decodeTest: _root_.io.circe.Decoder[Test] = new _root_.io.circe.Decoder[Test] { final def apply(c: _root_.io.circe.HCursor): _root_.io.circe.Decoder.Result[Test] = for (v0 <- c.downField("test").as[Test.Test]) yield Test(v0) }
[error] ^
[error] C:\...\untitled\target\scala-2.13\src_managed\main\nhclosures\definitions\Test.scala:10:28: type Test is not a member of object nhclosures.definitions.Test.Test
[error] case class Test(test: Test.Test)
Spec used
openapi: 3.0.1
info:
title: Minimal Error Case
version: "1.0"
servers:
- url: https://example.com
paths:
/test:
get:
operationId: Test
responses:
'200':
description: A test
content:
application/json:
schema:
$ref: '#/components/schemas/Test'
components:
schemas:
Test:
title: Test
required:
- test
type: object
properties:
test:
title: test
enum:
- optionA
- optionB
type: string
Code generated (Test.scala)
/*
* This file was generated by guardrail (https://github.com/guardrail-dev/guardrail).
* Modifications will be overwritten; instead edit the OpenAPI/Swagger spec file.
*/
package nhclosures.definitions
import cats.syntax.either._
import io.circe.syntax._
import cats.instances.all._
import _root_.nhclosures.Implicits._
case class Test(test: Test.Test)
object Test {
implicit val encodeTest: _root_.io.circe.Encoder.AsObject[Test] = {
_root_.io.circe.Encoder.AsObject.instance[Test](a => _root_.io.circe.JsonObject.fromIterable(_root_.scala.Vector(("test", a.test.asJson))))
}
implicit val decodeTest: _root_.io.circe.Decoder[Test] = new _root_.io.circe.Decoder[Test] { final def apply(c: _root_.io.circe.HCursor): _root_.io.circe.Decoder.Result[Test] = for (v0 <- c.downField("test").as[Test.Test]) yield Test(v0) }
sealed abstract class Test(val value: String) extends _root_.scala.Product with _root_.scala.Serializable { override def toString: String = value.toString }
object Test {
object members {
case object OptionA extends Test("optionA")
case object OptionB extends Test("optionB")
}
val OptionA: Test = members.OptionA
val OptionB: Test = members.OptionB
val values = _root_.scala.Vector(OptionA, OptionB)
implicit val encodeTest: _root_.io.circe.Encoder[Test] = _root_.io.circe.Encoder[String].contramap(_.value)
implicit val decodeTest: _root_.io.circe.Decoder[Test] = _root_.io.circe.Decoder[String].emap(value => from(value).toRight(s"$value not a member of Test"))
implicit val showTest: Show[Test] = Show[String].contramap[Test](_.value)
def from(value: String): _root_.scala.Option[Test] = values.find(_.value == value)
implicit val order: cats.Order[Test] = cats.Order.by[Test, Int](values.indexOf)
}
}