diff --git a/.travis.yml b/.travis.yml index 75e8959..ef4c06e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,12 @@ language: scala jdk: + - oraclejdk7 - oraclejdk8 scala: - - 2.10.4 - - 2.11.4 + - 2.10.6 + - 2.11.8 + - 2.12.1 +matrix: + exclude: + - jdk: oraclejdk7 + scala: 2.12.1 diff --git a/README.md b/README.md index 3ff0db7..3fe3081 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Swagger Scala Module [![Build Status](https://travis-ci.org/swagger-api/swagger-scala-module.svg?branch=develop)](https://travis-ci.org/swagger-api/swagger-scala-module) +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.swagger/swagger-scala-module_2.11/badge.svg?style=plastic)](https://maven-badges.herokuapp.com/maven-central/io.swagger/swagger-scala-module_2.11) The goal of Swaggerâ„¢ is to define a standard, language-agnostic interface to REST APIs which allows both humans and computers to discover and understand the capabilities of the service without access to source code, documentation, or through network traffic inspection. When properly defined via Swagger, a consumer can understand and interact with the remote service with a minimal amount of implementation logic. Similar to what interfaces have done for lower-level programming, Swagger removes the guesswork in calling the service. @@ -18,7 +19,7 @@ This project is compatible with [swagger-core](https://github.com/swagger-api/sw To enable the swagger-scala-module, include the appropriate version in your project: ``` - "io.swagger" %% "swagger-scala-module" % "1.0.0", + "io.swagger" %% "swagger-scala-module" % "1.0.3" ``` Which will include the proper cross-publish version of swagger-scala-module. @@ -42,7 +43,7 @@ The following methods are available to obtain support for Swagger: See the guide on [getting started with swagger](http://swagger.io) to get started with adding swagger to your API. -### To build from source (currently 1.0.0) +### To build from source ``` sbt publishLocal ``` diff --git a/build.sbt b/build.sbt index cb85146..9fb51c8 100644 --- a/build.sbt +++ b/build.sbt @@ -6,7 +6,7 @@ import Defaults._ organization := "io.swagger" -version := "1.0.3" +version := "1.0.4" scalaVersion := "2.11.8" diff --git a/src/main/scala/io/swagger/scala/converter/SwaggerScalaModelConverter.scala b/src/main/scala/io/swagger/scala/converter/SwaggerScalaModelConverter.scala index 48d45a5..609eb2d 100644 --- a/src/main/scala/io/swagger/scala/converter/SwaggerScalaModelConverter.scala +++ b/src/main/scala/io/swagger/scala/converter/SwaggerScalaModelConverter.scala @@ -19,7 +19,7 @@ class SwaggerScalaModelConverter extends ModelConverter { SwaggerScalaModelConverter override - def resolveProperty(`type`: Type, context: ModelConverterContext, + def resolveProperty(`type`: Type, context: ModelConverterContext, annotations: Array[Annotation] , chain: Iterator[ModelConverter]): Property = { val javaType = Json.mapper().constructType(`type`) val cls = javaType.getRawClass @@ -40,17 +40,32 @@ class SwaggerScalaModelConverter extends ModelConverter { val dp = PrimitiveType.DECIMAL.createProperty() dp.setRequired(true) return dp + } else if (cls.isAssignableFrom(classOf[BigInt])) { + val dp = PrimitiveType.INT.createProperty() + dp.setRequired(true) + return dp } } } // Unbox scala options `type` match { - case rt: ReferenceType if isOption(cls) && chain.hasNext => rt.getContentType + case rt: ReferenceType if isOption(cls) => val nextType = rt.getContentType - val nextResolved = chain.next().resolveProperty(nextType, context, annotations, chain) - nextResolved.setRequired(false) - nextResolved + val nextResolved = { + Option(resolveProperty(nextType, context, annotations, chain)) match { + case Some(p) => Some(p) + case None if chain.hasNext() => + Option(chain.next().resolveProperty(nextType, context, annotations, chain)) + case _ => None + } + } + nextResolved match { + case Some(nextResolved) => + nextResolved.setRequired(false) + nextResolved + case None => null + } case t if chain.hasNext => val nextResolved = chain.next().resolveProperty(t, context, annotations, chain) nextResolved.setRequired(true) @@ -64,7 +79,7 @@ class SwaggerScalaModelConverter extends ModelConverter { def resolve(`type`: Type, context: ModelConverterContext, chain: Iterator[ModelConverter]): Model = { val javaType = Json.mapper().constructType(`type`) getEnumerationInstance(javaType.getRawClass) match { - case Some(enumInstance) =>null // ignore scala enums + case Some(enumInstance) => null // ignore scala enums case None => if (chain.hasNext()) { val next = chain.next() diff --git a/src/test/scala/ModelPropertyParserTest.scala b/src/test/scala/ModelPropertyParserTest.scala index d25d66c..258cf1a 100644 --- a/src/test/scala/ModelPropertyParserTest.scala +++ b/src/test/scala/ModelPropertyParserTest.scala @@ -1,4 +1,5 @@ import io.swagger.converter._ +import io.swagger.models.Model import io.swagger.models.properties import io.swagger.models.properties._ import models._ @@ -46,17 +47,51 @@ class ModelPropertyParserTest extends FlatSpec with Matchers { } it should "process Model with Scala BigDecimal as Number" in { - case class TestModel(field: BigDecimal) + case class TestModelWithBigDecimal(field: BigDecimal) val converter = ModelConverters.getInstance() - val schemas = converter.readAll(classOf[TestModel]).asScala.toMap - val model = schemas.values.headOption + val schemas = converter.readAll(classOf[TestModelWithBigDecimal]).asScala.toMap + val model = findModel(schemas, "TestModelWithBigDecimal") model should be ('defined) val modelOpt = model.get.getProperties().get("field") modelOpt shouldBe a [properties.DecimalProperty] modelOpt.getRequired should be (true) } + it should "process Model with Scala BigInt as Number" in { + case class TestModelWithBigInt(field: BigInt) + + val converter = ModelConverters.getInstance() + val schemas = converter.readAll(classOf[TestModelWithBigInt]).asScala.toMap + val model = findModel(schemas, "TestModelWithBigInt") + model should be ('defined) + val modelOpt = model.get.getProperties().get("field") + modelOpt shouldBe a [properties.BaseIntegerProperty] + modelOpt.getRequired should be (true) + } + + it should "process Model with Scala Option BigDecimal" in { + val converter = ModelConverters.getInstance() + val schemas = converter.readAll(classOf[ModelWOptionBigDecimal]).asScala.toMap + val model = schemas.get("ModelWOptionBigDecimal") + model should be ('defined) + val optBigDecimal = model.get.getProperties().get("optBigDecimal") + optBigDecimal should not be (null) + optBigDecimal shouldBe a [properties.DecimalProperty] + optBigDecimal.getRequired should be (false) + } + + it should "process Model with Scala Option BigInt" in { + val converter = ModelConverters.getInstance() + val schemas = converter.readAll(classOf[ModelWOptionBigInt]).asScala.toMap + val model = schemas.get("ModelWOptionBigInt") + model should be ('defined) + val optBigDecimal = model.get.getProperties().get("optBigInt") + optBigDecimal should not be (null) + optBigDecimal shouldBe a [properties.BaseIntegerProperty] + optBigDecimal.getRequired should be (false) + } + it should "process all properties as required barring Option[_] or if overridden in annotation" in { val schemas = ModelConverters .getInstance() @@ -78,4 +113,15 @@ class ModelPropertyParserTest extends FlatSpec with Matchers { val forcedOptional = model.getProperties().get("forcedOptional") forcedOptional.getRequired should be (false) } -} \ No newline at end of file + + def findModel(schemas: Map[String, Model], name: String): Option[Model] = { + schemas.get(name) match { + case Some(m) => Some(m) + case None => + schemas.keys.find { case k => k.startsWith(name) } match { + case Some(key) => schemas.get(key) + case None => schemas.values.headOption + } + } + } +} diff --git a/src/test/scala/models/ModelWOptionBigDecimal.scala b/src/test/scala/models/ModelWOptionBigDecimal.scala new file mode 100644 index 0000000..ef2aa6e --- /dev/null +++ b/src/test/scala/models/ModelWOptionBigDecimal.scala @@ -0,0 +1,7 @@ +package models + +import io.swagger.annotations.ApiModelProperty +import scala.annotation.meta.field + +case class ModelWOptionBigDecimal( + @(ApiModelProperty @field)(value="this is an Option[BigDecimal] attribute") optBigDecimal: Option[BigDecimal]) diff --git a/src/test/scala/models/ModelWOptionBigInt.scala b/src/test/scala/models/ModelWOptionBigInt.scala new file mode 100644 index 0000000..99e4bfc --- /dev/null +++ b/src/test/scala/models/ModelWOptionBigInt.scala @@ -0,0 +1,7 @@ +package models + +import io.swagger.annotations.ApiModelProperty +import scala.annotation.meta.field + +case class ModelWOptionBigInt( + @(ApiModelProperty @field)(value="this is an Option[BigInt] attribute") optBigInt: Option[BigInt])