Skip to content

Commit

Permalink
Merge pull request #3314 from markussammallahti/codegen-any-type-support
Browse files Browse the repository at this point in the history
Codegen any type support
  • Loading branch information
adamw authored Nov 13, 2023
2 parents b3c662a + b3d9b58 commit 9cf1603
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 5 deletions.
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

0 comments on commit 9cf1603

Please sign in to comment.