Skip to content

Fix schema types seriallization #65

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

Merged
merged 12 commits into from
Jul 24, 2025
Merged
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ The library has springdoc-openapi as a provided dependency,
thus users of the library have to include that dependency in their projects:
- for springdoc-openapi 1.x versions `1.6.7` up to `1.7.0` (including) of
`"org.springdoc" % "springdoc-openapi-webmvc-core"` are supported
- for springdoc-openapi 2.x versions `2.0.0` up to `2.3.0` (including) of
- for springdoc-openapi 2.x versions `2.0.0` up to `2.8.9` (including) of
`"org.springdoc" % "springdoc-openapi-starter-webmvc-api"` are supported

### Add library dependency to SBT/Maven
Expand Down
2 changes: 1 addition & 1 deletion examples/springdoc-openapi-scala-2/simple/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ lazy val root = (project in file("."))
.settings(
libraryDependencies ++= Seq(
"za.co.absa" %% "springdoc-openapi-scala-2" % `springdoc-openapi-scala-2-version`,
"org.springdoc" % "springdoc-openapi-starter-webmvc-api" % "2.3.0",
"org.springdoc" % "springdoc-openapi-starter-webmvc-api" % "2.8.9",
"org.springframework.boot" % "spring-boot-starter-web" % "3.2.0",
"org.playframework" %% "play-json" % "3.0.1"
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
package za.co.absa.springdocopenapiscala

import io.swagger.v3.oas.models.Components
import io.swagger.v3.oas.models.media.{Discriminator, Schema}
import io.swagger.v3.oas.models.media.{ArraySchema, BooleanSchema, Discriminator, IntegerSchema, NumberSchema, ObjectSchema, Schema, StringSchema, UUIDSchema}

import java.time.{Instant, LocalDate, LocalDateTime, LocalTime, ZonedDateTime}
import java.util.UUID
Expand All @@ -27,6 +27,7 @@ import scala.reflect.runtime.universe._
import OpenAPIModelRegistration._

import java.sql.Timestamp
import scala.collection.Seq

class OpenAPIModelRegistration(
components: Components,
Expand Down Expand Up @@ -69,7 +70,7 @@ class OpenAPIModelRegistration(
handleType(tpe)
}

private case class OpenAPISimpleType(tpe: String, format: Option[String] = None)
private case class OpenAPISimpleType(schema: Schema[_], format: Option[String] = None, _type: Option[String] = None)

@tailrec
private def handleType(tpe: Type): Schema[_] = {
Expand All @@ -96,8 +97,7 @@ class OpenAPIModelRegistration(

private def handleCaseType(tpe: Type): Schema[_] = {
val name = tpe.typeSymbol.name.toString.trim
val schema = new Schema
schema.setType("object")
val schema = new ObjectSchema
val fields = tpe.decls.collect {
case field: TermSymbol if field.isVal => field
}
Expand All @@ -114,17 +114,15 @@ class OpenAPIModelRegistration(

private def handleMap(keyType: Type, valueType: Type): Schema[_] = keyType match {
case _ if keyType <:< typeOf[String] =>
val schema = new Schema
schema.setType("object")
val schema = new ObjectSchema
schema.setAdditionalProperties(handleType(valueType))
schema
case _ => throw new IllegalArgumentException("In OpenAPI 3.0.x Map must have String key type.")
}

private def handleSeqLike(tpe: Type): Schema[_] = {
val schema = new Schema
val schema = new ArraySchema
val innerSchema = handleType(tpe.typeArgs.head)
schema.setType("array")
schema.setItems(innerSchema)
schema
}
Expand All @@ -139,8 +137,7 @@ class OpenAPIModelRegistration(
val enumValues = parentObjectType.members.filter(isSymbolEnumerationValue)
val enumValuesAsStrings = enumValues.map(_.name.toString.trim)

val schema = new Schema[String]
schema.setType("string")
val schema = new StringSchema
schema.setEnum(enumValuesAsStrings.toList.asJava)
schema
}
Expand Down Expand Up @@ -224,33 +221,39 @@ class OpenAPIModelRegistration(
}

private def handleSimpleType(tpe: Type): Schema[_] = {
val schema = new Schema
val OpenAPISimpleType(terminalTpe, format) = getOpenAPISimpleType(tpe)
schema.setType(terminalTpe)
format.foreach(f => schema.setFormat(f))
schema
val simpleType = getOpenAPISimpleType(tpe)
simpleType.format.foreach(simpleType.schema.setFormat)
simpleType._type.foreach(simpleType.schema.setType)
simpleType.schema
}

private def getOpenAPISimpleType(tpe: Type): OpenAPISimpleType = tpe.dealias match {
case t if t =:= typeOf[Byte] => OpenAPISimpleType("integer", Some("int32"))
case t if t =:= typeOf[Short] => OpenAPISimpleType("integer", Some("int32"))
case t if t =:= typeOf[Int] => OpenAPISimpleType("integer", Some("int32"))
case t if t =:= typeOf[Long] => OpenAPISimpleType("integer", Some("int64"))
case t if t =:= typeOf[Float] => OpenAPISimpleType("number", Some("float"))
case t if t =:= typeOf[Double] => OpenAPISimpleType("number", Some("double"))
case t if t =:= typeOf[Char] => OpenAPISimpleType("string")
case t if t =:= typeOf[String] => OpenAPISimpleType("string")
case t if t =:= typeOf[UUID] => OpenAPISimpleType("string", Some("uuid"))
case t if t =:= typeOf[Boolean] => OpenAPISimpleType("boolean")
case t if t =:= typeOf[Unit] => OpenAPISimpleType("null")
case t if t =:= typeOf[ZonedDateTime] => OpenAPISimpleType("string", Some("date-time"))
case t if t =:= typeOf[Instant] => OpenAPISimpleType("string", Some("date-time"))
case t if t =:= typeOf[LocalDateTime] => OpenAPISimpleType("string", Some("date-time"))
case t if t =:= typeOf[LocalDate] => OpenAPISimpleType("string", Some("date"))
case t if t =:= typeOf[LocalTime] => OpenAPISimpleType("string", Some("time"))
case t if t =:= typeOf[Timestamp] => OpenAPISimpleType("string", Some("date-time"))
case t if t =:= typeOf[BigDecimal] => OpenAPISimpleType("number")
case t if t =:= typeOf[BigInt] => OpenAPISimpleType("integer")
case t if t =:= typeOf[Byte] || t =:= typeOf[Short] || t =:= typeOf[Int] =>
OpenAPISimpleType(new IntegerSchema())
case t if t =:= typeOf[Long] =>
OpenAPISimpleType(new IntegerSchema(), Some("int64"))
case t if t =:= typeOf[Float] =>
OpenAPISimpleType(new NumberSchema(), Some("float"))
case t if t =:= typeOf[Double] =>
OpenAPISimpleType(new NumberSchema(), Some("double"))
case t if t =:= typeOf[Char] || t =:= typeOf[String] =>
OpenAPISimpleType(new StringSchema())
case t if t =:= typeOf[UUID] =>
OpenAPISimpleType(new UUIDSchema())
case t if t =:= typeOf[Boolean] =>
OpenAPISimpleType(new BooleanSchema())
case t if t =:= typeOf[Unit] =>
OpenAPISimpleType(new Schema, None, Some("null"))
case t if t =:= typeOf[ZonedDateTime] || t =:= typeOf[Instant] || t =:= typeOf[LocalDateTime] || t =:= typeOf[Timestamp] =>
OpenAPISimpleType(new StringSchema(), Some("date-time"))
case t if t =:= typeOf[LocalDate] =>
OpenAPISimpleType(new StringSchema(), Some("date"))
case t if t =:= typeOf[LocalTime] =>
OpenAPISimpleType(new StringSchema(), Some("time"))
case t if t =:= typeOf[BigDecimal] =>
OpenAPISimpleType(new NumberSchema())
case t if t =:= typeOf[BigInt] =>
OpenAPISimpleType(new IntegerSchema(), Some(null))
}

private def registerAsReference(name: String, schema: Schema[_]): Schema[_] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
package za.co.absa.springdocopenapiscala

import io.swagger.v3.oas.models.Components
import io.swagger.v3.oas.models.media.Schema
import io.swagger.v3.oas.models.media.{IntegerSchema, Schema}
import org.scalatest
import org.scalatest.flatspec.AnyFlatSpec

Expand Down Expand Up @@ -281,7 +281,7 @@ class OpenAPIModelRegistrationSpec extends AnyFlatSpec {
assertAdditionalPropertiesAreAsExpected(
actualSchemas,
"Maps.a",
(new Schema).`type`("integer").format("int32")
new IntegerSchema()
)
assertTypeAndFormatAreAsExpected(actualSchemas, "Maps.b", "object")
assertAdditionalPropertiesAreAsExpected(
Expand Down
2 changes: 1 addition & 1 deletion project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ object Dependencies {

def springdocOpenapi(majorVersion: Int): String = majorVersion match {
case 1 => "[1.6.7,1.7.0]"
case 2 => "[2.0.0,2.3.0]"
case 2 => "[2.0.0,2.8.9]"
}
}

Expand Down