From 34754a53a094ee3e503bf78cdd7a4600c7960487 Mon Sep 17 00:00:00 2001 From: Markus Sammallahti Date: Sun, 12 Nov 2023 23:28:40 +0200 Subject: [PATCH 1/2] Add any type parsing --- .../openapi/models/OpenapiSchemaType.scala | 21 ++++++++++--- .../codegen/models/SchemaParserSpec.scala | 31 +++++++++++++++++++ 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/openapi-codegen/core/src/main/scala/sttp/tapir/codegen/openapi/models/OpenapiSchemaType.scala b/openapi-codegen/core/src/main/scala/sttp/tapir/codegen/openapi/models/OpenapiSchemaType.scala index 6653f96de2..427b3252a3 100644 --- a/openapi-codegen/core/src/main/scala/sttp/tapir/codegen/openapi/models/OpenapiSchemaType.scala +++ b/openapi-codegen/core/src/main/scala/sttp/tapir/codegen/openapi/models/OpenapiSchemaType.scala @@ -83,6 +83,10 @@ object OpenapiSchemaType { val nullable = false } + case class OpenapiSchemaAny( + nullable: Boolean + ) extends OpenapiSchemaSimpleType + case class OpenapiSchemaConstantString( value: String ) extends OpenapiSchemaType { @@ -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]] @@ -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, @@ -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 _) } diff --git a/openapi-codegen/core/src/test/scala/sttp/tapir/codegen/models/SchemaParserSpec.scala b/openapi-codegen/core/src/test/scala/sttp/tapir/codegen/models/SchemaParserSpec.scala index 45fe396afb..36c0ed6cb7 100644 --- a/openapi-codegen/core/src/test/scala/sttp/tapir/codegen/models/SchemaParserSpec.scala +++ b/openapi-codegen/core/src/test/scala/sttp/tapir/codegen/models/SchemaParserSpec.scala @@ -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, @@ -27,6 +28,7 @@ class SchemaParserSpec extends AnyFlatSpec with Matchers with Checkers { val yaml = """ |schemas: | User: + | type: object | properties: | id: | type: integer @@ -61,6 +63,7 @@ class SchemaParserSpec extends AnyFlatSpec with Matchers with Checkers { val yaml = """ |schemas: | User: + | type: object | properties: | attributes: | type: object @@ -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 = """ From b3d9b58a78b790a90f170e7893c321a58cca1a4f Mon Sep 17 00:00:00 2001 From: Markus Sammallahti Date: Sun, 12 Nov 2023 23:49:49 +0200 Subject: [PATCH 2/2] Add support for any type --- .../sttp/tapir/codegen/BasicGenerator.scala | 3 +++ .../codegen/ClassDefinitionGeneratorSpec.scala | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/openapi-codegen/core/src/main/scala/sttp/tapir/codegen/BasicGenerator.scala b/openapi-codegen/core/src/main/scala/sttp/tapir/codegen/BasicGenerator.scala index 1b9515f887..5b387bcd1f 100644 --- a/openapi-codegen/core/src/main/scala/sttp/tapir/codegen/BasicGenerator.scala +++ b/openapi-codegen/core/src/main/scala/sttp/tapir/codegen/BasicGenerator.scala @@ -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, @@ -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") diff --git a/openapi-codegen/core/src/test/scala/sttp/tapir/codegen/ClassDefinitionGeneratorSpec.scala b/openapi-codegen/core/src/test/scala/sttp/tapir/codegen/ClassDefinitionGeneratorSpec.scala index 9686096361..27c37eea95 100644 --- a/openapi-codegen/core/src/test/scala/sttp/tapir/codegen/ClassDefinitionGeneratorSpec.scala +++ b/openapi-codegen/core/src/test/scala/sttp/tapir/codegen/ClassDefinitionGeneratorSpec.scala @@ -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, @@ -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( "",