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

Codegen any type support #3314

Merged
merged 2 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package sttp.tapir.codegen

import sttp.tapir.codegen.openapi.models.OpenapiModels.OpenapiDocument
import sttp.tapir.codegen.openapi.models.OpenapiSchemaType.{
OpenapiSchemaAny,
OpenapiSchemaBoolean,
OpenapiSchemaDouble,
OpenapiSchemaEnum,
Expand Down Expand Up @@ -66,6 +67,8 @@ object BasicGenerator {
("String", nb)
case OpenapiSchemaBoolean(nb) =>
("Boolean", nb)
case OpenapiSchemaAny(nb) =>
("io.circe.Json", nb)
case OpenapiSchemaRef(t) =>
(t.split('/').last, false)
case x => throw new NotImplementedError(s"Not all simple types supported! Found $x")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ object OpenapiSchemaType {
val nullable = false
}

case class OpenapiSchemaAny(
nullable: Boolean
) extends OpenapiSchemaSimpleType

case class OpenapiSchemaConstantString(
value: String
) extends OpenapiSchemaType {
Expand Down Expand Up @@ -251,10 +255,7 @@ object OpenapiSchemaType {

implicit val OpenapiSchemaObjectDecoder: Decoder[OpenapiSchemaObject] = { (c: HCursor) =>
for {
_ <- c
.downField("type")
.as[Option[String]]
.ensure(DecodingFailure("Given type is not object!", c.history))(v => v.forall(_ == "object"))
_ <- c.downField("type").as[String].ensure(DecodingFailure("Given type is not object!", c.history))(v => v == "object")
f <- c.downField("properties").as[Option[Map[String, OpenapiSchemaType]]]
r <- c.downField("required").as[Option[Seq[String]]]
nb <- c.downField("nullable").as[Option[Boolean]]
Expand Down Expand Up @@ -283,6 +284,15 @@ object OpenapiSchemaType {
}
}

implicit val OpenapiSchemaAnyDecoder: Decoder[OpenapiSchemaAny] = { (c: HCursor) =>
for {
_ <- c.downField("type").as[Option[String]].ensure(DecodingFailure("Type must not be defined!", c.history))(_.isEmpty)
nb <- c.downField("nullable").as[Option[Boolean]]
} yield {
OpenapiSchemaAny(nb.getOrElse(false))
}
}

implicit lazy val OpenapiSchemaTypeDecoder: Decoder[OpenapiSchemaType] =
List[Decoder[OpenapiSchemaType]](
Decoder[OpenapiSchemaEnum].widen,
Expand All @@ -291,6 +301,7 @@ object OpenapiSchemaType {
Decoder[OpenapiSchemaNot].widen,
Decoder[OpenapiSchemaMap].widen,
Decoder[OpenapiSchemaObject].widen,
Decoder[OpenapiSchemaArray].widen
Decoder[OpenapiSchemaArray].widen,
Decoder[OpenapiSchemaAny].widen
).reduceLeft(_ or _)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package sttp.tapir.codegen
import sttp.tapir.codegen.openapi.models.OpenapiComponent
import sttp.tapir.codegen.openapi.models.OpenapiModels.OpenapiDocument
import sttp.tapir.codegen.openapi.models.OpenapiSchemaType.{
OpenapiSchemaAny,
OpenapiSchemaArray,
OpenapiSchemaConstantString,
OpenapiSchemaEnum,
Expand Down Expand Up @@ -108,6 +109,23 @@ class ClassDefinitionGeneratorSpec extends CompileCheckTestBase {
new ClassDefinitionGenerator().classDefs(doc).get shouldCompile ()
}

it should "generate class with any type" in {
val doc = OpenapiDocument(
"",
null,
null,
Some(
OpenapiComponent(
Map(
"Test" -> OpenapiSchemaObject(Map("anyType" -> OpenapiSchemaAny(false)), Seq("anyType"), false)
)
)
)
)

new ClassDefinitionGenerator().classDefs(doc).get shouldCompile ()
}

it should "generate class with inner class" in {
val doc = OpenapiDocument(
"",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package sttp.tapir.codegen.openapi.models

import sttp.tapir.codegen.openapi.models.OpenapiModels.OpenapiResponseContent
import sttp.tapir.codegen.openapi.models.OpenapiSchemaType.{
OpenapiSchemaAny,
OpenapiSchemaArray,
OpenapiSchemaInt,
OpenapiSchemaMap,
Expand All @@ -27,6 +28,7 @@ class SchemaParserSpec extends AnyFlatSpec with Matchers with Checkers {
val yaml = """
|schemas:
| User:
| type: object
| properties:
| id:
| type: integer
Expand Down Expand Up @@ -61,6 +63,7 @@ class SchemaParserSpec extends AnyFlatSpec with Matchers with Checkers {
val yaml = """
|schemas:
| User:
| type: object
| properties:
| attributes:
| type: object
Expand All @@ -87,6 +90,34 @@ class SchemaParserSpec extends AnyFlatSpec with Matchers with Checkers {
)
}

it should "parse any type" in {
val yaml = """
|schemas:
| User:
| type: object
| properties:
| anyValue: {}
| required:
| - anyValue""".stripMargin

val res = parser
.parse(yaml)
.leftMap(err => err: Error)
.flatMap(_.as[OpenapiComponent])

res shouldBe Right(
OpenapiComponent(
Map(
"User" -> OpenapiSchemaObject(
Map("anyValue" -> OpenapiSchemaAny(false)),
Seq("anyValue"),
false
)
)
)
)
}

it should "parse security schemes" in {
val yaml =
"""
Expand Down
Loading