diff --git a/library/src/main/scala/za/co/absa/springdocopenapiscala/OpenAPIModelRegistration.scala b/library/src/main/scala/za/co/absa/springdocopenapiscala/OpenAPIModelRegistration.scala index aded15d..79d0073 100644 --- a/library/src/main/scala/za/co/absa/springdocopenapiscala/OpenAPIModelRegistration.scala +++ b/library/src/main/scala/za/co/absa/springdocopenapiscala/OpenAPIModelRegistration.scala @@ -77,13 +77,13 @@ class OpenAPIModelRegistration( else tpe.dealias match { case t if tpe.typeSymbol.isClass && tpe.typeSymbol.asClass.isCaseClass => handleCaseType(t) - case t if t <:< typeOf[Map[_, _]] => handleMap(t) - case t if t <:< typeOf[Option[_]] => handleType(t.typeArgs.head) - case t if t <:< typeOf[Seq[_]] || t <:< typeOf[Array[_]] => handleSeqLike(t) - case t if t <:< typeOf[Set[_]] => handleSet(t) - case t if t <:< typeOf[Enumeration#Value] => handleEnum(t) - case t if t.typeSymbol.isClass && t.typeSymbol.asClass.isSealed => handleSealedType(t) - case t => handleSimpleType(t) + case t if t <:< typeOf[Map[_, _]] => handleMap(t.typeArgs(0), t.typeArgs(1)) + case t if t <:< typeOf[Option[_]] => handleType(t.typeArgs.head) + case t if t <:< typeOf[Seq[_]] || t <:< typeOf[Array[_]] => handleSeqLike(t) + case t if t <:< typeOf[Set[_]] => handleSet(t) + case t if t <:< typeOf[Enumeration#Value] => handleEnum(t) + case t if t.typeSymbol.isClass && t.typeSymbol.asClass.isSealed => handleSealedType(t) + case t => handleSimpleType(t) } } @@ -112,10 +112,13 @@ class OpenAPIModelRegistration( registerAsReference(name, schema) } - private def handleMap(tpe: Type): Schema[_] = { - val schema = new Schema - schema.setType("object") - schema + private def handleMap(keyType: Type, valueType: Type): Schema[_] = keyType match { + case _ if keyType <:< typeOf[String] => + val schema = new Schema + schema.setType("object") + 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[_] = { diff --git a/library/src/test/scala/za/co/absa/springdocopenapiscala/OpenAPIModelRegistrationSpec.scala b/library/src/test/scala/za/co/absa/springdocopenapiscala/OpenAPIModelRegistrationSpec.scala index 11ff79b..038ca8d 100644 --- a/library/src/test/scala/za/co/absa/springdocopenapiscala/OpenAPIModelRegistrationSpec.scala +++ b/library/src/test/scala/za/co/absa/springdocopenapiscala/OpenAPIModelRegistrationSpec.scala @@ -80,7 +80,12 @@ class OpenAPIModelRegistrationSpec extends AnyFlatSpec { private case class Maps( a: Map[String, Int], - b: Map[Int, Arrays] + b: Map[String, Arrays] + ) + + private case class WrongMaps( + a: Map[Int, Int], + b: Map[Boolean, Arrays] ) private object Things extends Enumeration { @@ -262,7 +267,7 @@ class OpenAPIModelRegistrationSpec extends AnyFlatSpec { assertIsArrayOfExpectedType(actualSchemas, "Arrays.h", "integer") } - it should "make Map an OpenAPI object type" in { + it should "make Map an OpenAPI object type with value schema as additionalProperties" in { val components = new Components val openAPIModelRegistration = new OpenAPIModelRegistration(components) @@ -271,7 +276,26 @@ class OpenAPIModelRegistrationSpec extends AnyFlatSpec { val actualSchemas = components.getSchemas assertTypeAndFormatAreAsExpected(actualSchemas, "Maps.a", "object") + assertAdditionalPropertiesAreAsExpected( + actualSchemas, + "Maps.a", + (new Schema).`type`("integer").format("int32") + ) assertTypeAndFormatAreAsExpected(actualSchemas, "Maps.b", "object") + assertAdditionalPropertiesAreAsExpected( + actualSchemas, + "Maps.b", + (new Schema).$ref("#/components/schemas/Arrays") + ) + } + + it should "throw IllegalArgumentException if Map has key type different than String" in { + val components = new Components + val openAPIModelRegistration = new OpenAPIModelRegistration(components) + + intercept[IllegalArgumentException] { + openAPIModelRegistration.register[WrongMaps]() + } } it should "make simple Enumeration (without renaming) an OpenAPI enum" in { @@ -805,6 +829,16 @@ class OpenAPIModelRegistrationSpec extends AnyFlatSpec { schema => schema.getType === expectedType && Option(schema.getFormat) === expectedFormat ) + private def assertAdditionalPropertiesAreAsExpected( + actualSchemas: java.util.Map[String, Schema[_]], + fieldPath: String, + expectedAdditionalProperties: AnyRef + ): scalatest.Assertion = assertPredicateForPath( + actualSchemas, + fieldPath, + schema => Option(schema.getAdditionalProperties).map(_ === expectedAdditionalProperties).getOrElse(false) + ) + private def assertRefIsAsExpected( actualSchemas: java.util.Map[String, Schema[_]], fieldPath: String,