[Scala][sttp] various bug fixes (#12254)

* avoid using deprecated enum naming

* map anytype to any for sttp

* circe codecs and enum fixes

* regenerated the samples
This commit is contained in:
Miklos Szots 2022-05-04 18:40:58 +02:00 committed by GitHub
parent 1e48c95d48
commit bee9c79e5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 105 additions and 22 deletions

View File

@ -113,6 +113,10 @@ public class ScalaSttpClientCodegen extends AbstractScalaCodegen implements Code
apiTemplateFiles.put("api.mustache", ".scala"); apiTemplateFiles.put("api.mustache", ".scala");
embeddedTemplateDir = templateDir = "scala-sttp"; embeddedTemplateDir = templateDir = "scala-sttp";
String jsonLibrary = JSON_LIBRARY_PROPERTY.getValue(additionalProperties);
String jsonValueClass = jsonLibrary == "circe" ? "io.circe.Json" : "org.json4s.JValue";
additionalProperties.put(CodegenConstants.GROUP_ID, groupId); additionalProperties.put(CodegenConstants.GROUP_ID, groupId);
additionalProperties.put(CodegenConstants.ARTIFACT_ID, artifactId); additionalProperties.put(CodegenConstants.ARTIFACT_ID, artifactId);
additionalProperties.put(CodegenConstants.ARTIFACT_VERSION, artifactVersion); additionalProperties.put(CodegenConstants.ARTIFACT_VERSION, artifactVersion);
@ -148,6 +152,7 @@ public class ScalaSttpClientCodegen extends AbstractScalaCodegen implements Code
typeMapping.put("number", "Double"); typeMapping.put("number", "Double");
typeMapping.put("decimal", "BigDecimal"); typeMapping.put("decimal", "BigDecimal");
typeMapping.put("ByteArray", "Array[Byte]"); typeMapping.put("ByteArray", "Array[Byte]");
typeMapping.put("AnyType", jsonValueClass);
instantiationTypes.put("array", "ListBuffer"); instantiationTypes.put("array", "ListBuffer");
instantiationTypes.put("map", "Map"); instantiationTypes.put("map", "Map");
@ -170,6 +175,7 @@ public class ScalaSttpClientCodegen extends AbstractScalaCodegen implements Code
supportingFiles.add(new SupportingFile("build.sbt.mustache", "", "build.sbt")); supportingFiles.add(new SupportingFile("build.sbt.mustache", "", "build.sbt"));
final String invokerFolder = (sourceFolder + File.separator + invokerPackage).replace(".", File.separator); final String invokerFolder = (sourceFolder + File.separator + invokerPackage).replace(".", File.separator);
supportingFiles.add(new SupportingFile("jsonSupport.mustache", invokerFolder, "JsonSupport.scala")); supportingFiles.add(new SupportingFile("jsonSupport.mustache", invokerFolder, "JsonSupport.scala"));
supportingFiles.add(new SupportingFile("additionalTypeSerializers.mustache", invokerFolder, "AdditionalTypeSerializers.scala"));
supportingFiles.add(new SupportingFile("project/build.properties.mustache", "project", "build.properties")); supportingFiles.add(new SupportingFile("project/build.properties.mustache", "project", "build.properties"));
supportingFiles.add(new SupportingFile("dateSerializers.mustache", invokerFolder, "DateSerializers.scala")); supportingFiles.add(new SupportingFile("dateSerializers.mustache", invokerFolder, "DateSerializers.scala"));
} }

View File

@ -0,0 +1,45 @@
package {{invokerPackage}}
import java.net.{ URI, URISyntaxException }
{{#json4s}}
object AdditionalTypeSerializers {
import org.json4s.{Serializer, CustomSerializer, JNull, MappingException}
import org.json4s.JsonAST.JString
case object URISerializer extends CustomSerializer[URI]( _ => ( {
case JString(s) =>
try new URI(s)
catch {
case _: URISyntaxException =>
throw new MappingException("String could not be parsed as a URI reference, it violates RFC 2396.")
case _: NullPointerException =>
throw new MappingException("String is null.")
}
case JNull => null
}, {
case uri: URI =>
JString(uri.toString())
}))
def all: Seq[Serializer[_]] = Seq[Serializer[_]]() :+ URISerializer
}
{{/json4s}}
{{#circe}}
trait AdditionalTypeSerializers {
import io.circe._
implicit final lazy val URIDecoder: Decoder[URI] = Decoder.decodeString.emap(string =>
try Right(new URI(string))
catch {
case _: URISyntaxException =>
Left("String could not be parsed as a URI reference, it violates RFC 2396.")
case _: NullPointerException =>
Left("String is null.")
}
)
implicit final lazy val URIEncoder: Encoder[URI] = new Encoder[URI] {
final def apply(a: URI): Json = Json.fromString(a.toString)
}
}
{{/circe}}

View File

@ -1,9 +1,8 @@
package {{invokerPackage}} package {{invokerPackage}}
{{#java8}} {{#java8}}
import java.time.{LocalDate, LocalDateTime, OffsetDateTime, ZoneId} import java.time.{LocalDate, OffsetDateTime}
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import scala.util.Try
{{/java8}} {{/java8}}
{{#joda}} {{#joda}}
import org.joda.time.DateTime import org.joda.time.DateTime
@ -15,6 +14,9 @@ object DateSerializers {
import org.json4s.{Serializer, CustomSerializer, JNull} import org.json4s.{Serializer, CustomSerializer, JNull}
import org.json4s.JsonAST.JString import org.json4s.JsonAST.JString
{{#java8}} {{#java8}}
import scala.util.Try
import java.time.{LocalDateTime, ZoneId}
case object DateTimeSerializer extends CustomSerializer[OffsetDateTime](_ => ( { case object DateTimeSerializer extends CustomSerializer[OffsetDateTime](_ => ( {
case JString(s) => case JString(s) =>
Try(OffsetDateTime.parse(s, DateTimeFormatter.ISO_OFFSET_DATE_TIME)) orElse Try(OffsetDateTime.parse(s, DateTimeFormatter.ISO_OFFSET_DATE_TIME)) orElse

View File

@ -10,23 +10,23 @@ import sttp.client3.json4s.SttpJson4sApi
import scala.reflect.ClassTag import scala.reflect.ClassTag
object JsonSupport extends SttpJson4sApi { object JsonSupport extends SttpJson4sApi {
def enumSerializers: Seq[Serializer[_]] = Seq[Serializer[_]](){{#models}}{{#model}}{{#hasEnums}}{{#vars}}{{#isEnum}} :+ def enumSerializers: Seq[Serializer[_]] = Seq[Serializer[_]](){{#models}}{{#model}}{{#isEnum}} :+
new EnumNameSerializer({{classname}}Enums.{{datatypeWithEnum}}){{/isEnum}}{{/vars}}{{/hasEnums}}{{/model}}{{/models}} new EnumNameSerializer({{classname}}){{/isEnum}}{{/model}}{{/models}}
private class EnumNameSerializer[E <: Enumeration: ClassTag](enum: E) extends Serializer[E#Value] { private class EnumNameSerializer[E <: Enumeration: ClassTag](enumeration: E) extends Serializer[E#Value] {
import JsonDSL._ import JsonDSL._
val EnumerationClass: Class[E#Value] = classOf[E#Value] val EnumerationClass: Class[E#Value] = classOf[E#Value]
def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), E#Value] = { def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), E#Value] = {
case (t @ TypeInfo(EnumerationClass, _), json) if isValid(json) => case (t @ TypeInfo(EnumerationClass, _), json) if isValid(json) =>
json match { json match {
case JString(value) => enum.withName(value) case JString(value) => enumeration.withName(value)
case value => throw new MappingException(s"Can't convert $value to $EnumerationClass") case value => throw new MappingException(s"Can't convert $value to $EnumerationClass")
} }
} }
private[this] def isValid(json: JValue) = json match { private[this] def isValid(json: JValue) = json match {
case JString(value) if enum.values.exists(_.toString == value) => true case JString(value) if enumeration.values.exists(_.toString == value) => true
case _ => false case _ => false
} }
@ -35,7 +35,7 @@ object JsonSupport extends SttpJson4sApi {
} }
} }
implicit val format: Formats = DefaultFormats ++ enumSerializers ++ DateSerializers.all implicit val format: Formats = DefaultFormats ++ enumSerializers ++ DateSerializers.all ++ AdditionalTypeSerializers.all
implicit val serialization: org.json4s.Serialization = org.json4s.jackson.Serialization implicit val serialization: org.json4s.Serialization = org.json4s.jackson.Serialization
} }
{{/json4s}} {{/json4s}}
@ -44,10 +44,15 @@ import io.circe.{Decoder, Encoder}
import io.circe.generic.AutoDerivation import io.circe.generic.AutoDerivation
import sttp.client3.circe.SttpCirceApi import sttp.client3.circe.SttpCirceApi
object JsonSupport extends SttpCirceApi with AutoDerivation with DateSerializers { object JsonSupport extends SttpCirceApi with AutoDerivation with DateSerializers with AdditionalTypeSerializers {
{{#models}}{{#model}}{{#hasEnums}}{{#vars}}{{#isEnum}}
implicit val {{classname}}{{datatypeWithEnum}}Decoder: Decoder[{{classname}}Enums.{{datatypeWithEnum}}] = Decoder.decodeEnumeration({{classname}}Enums.{{datatypeWithEnum}}) {{#models}}
implicit val {{classname}}{{datatypeWithEnum}}Encoder: Encoder[{{classname}}Enums.{{datatypeWithEnum}}] = Encoder.encodeEnumeration({{classname}}Enums.{{datatypeWithEnum}}) {{#model}}
{{/isEnum}}{{/vars}}{{/hasEnums}}{{/model}}{{/models}} {{#isEnum}}
implicit val {{classname}}Decoder: Decoder[{{classname}}.{{classname}}] = Decoder.decodeEnumeration({{classname}})
implicit val {{classname}}Encoder: Encoder[{{classname}}.{{classname}}] = Encoder.encodeEnumeration({{classname}})
{{/isEnum}}
{{/model}}
{{/models}}
} }
{{/circe}} {{/circe}}

View File

@ -4,6 +4,7 @@ project/build.properties
src/main/scala/org/openapitools/client/api/PetApi.scala src/main/scala/org/openapitools/client/api/PetApi.scala
src/main/scala/org/openapitools/client/api/StoreApi.scala src/main/scala/org/openapitools/client/api/StoreApi.scala
src/main/scala/org/openapitools/client/api/UserApi.scala src/main/scala/org/openapitools/client/api/UserApi.scala
src/main/scala/org/openapitools/client/core/AdditionalTypeSerializers.scala
src/main/scala/org/openapitools/client/core/DateSerializers.scala src/main/scala/org/openapitools/client/core/DateSerializers.scala
src/main/scala/org/openapitools/client/core/JsonSupport.scala src/main/scala/org/openapitools/client/core/JsonSupport.scala
src/main/scala/org/openapitools/client/model/ApiResponse.scala src/main/scala/org/openapitools/client/model/ApiResponse.scala

View File

@ -0,0 +1,24 @@
package org.openapitools.client.core
import java.net.{ URI, URISyntaxException }
object AdditionalTypeSerializers {
import org.json4s.{Serializer, CustomSerializer, JNull, MappingException}
import org.json4s.JsonAST.JString
case object URISerializer extends CustomSerializer[URI]( _ => ( {
case JString(s) =>
try new URI(s)
catch {
case _: URISyntaxException =>
throw new MappingException("String could not be parsed as a URI reference, it violates RFC 2396.")
case _: NullPointerException =>
throw new MappingException("String is null.")
}
case JNull => null
}, {
case uri: URI =>
JString(uri.toString())
}))
def all: Seq[Serializer[_]] = Seq[Serializer[_]]() :+ URISerializer
}

View File

@ -1,12 +1,14 @@
package org.openapitools.client.core package org.openapitools.client.core
import java.time.{LocalDate, LocalDateTime, OffsetDateTime, ZoneId} import java.time.{LocalDate, OffsetDateTime}
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import scala.util.Try
object DateSerializers { object DateSerializers {
import org.json4s.{Serializer, CustomSerializer, JNull} import org.json4s.{Serializer, CustomSerializer, JNull}
import org.json4s.JsonAST.JString import org.json4s.JsonAST.JString
import scala.util.Try
import java.time.{LocalDateTime, ZoneId}
case object DateTimeSerializer extends CustomSerializer[OffsetDateTime](_ => ( { case object DateTimeSerializer extends CustomSerializer[OffsetDateTime](_ => ( {
case JString(s) => case JString(s) =>
Try(OffsetDateTime.parse(s, DateTimeFormatter.ISO_OFFSET_DATE_TIME)) orElse Try(OffsetDateTime.parse(s, DateTimeFormatter.ISO_OFFSET_DATE_TIME)) orElse

View File

@ -17,24 +17,22 @@ import sttp.client3.json4s.SttpJson4sApi
import scala.reflect.ClassTag import scala.reflect.ClassTag
object JsonSupport extends SttpJson4sApi { object JsonSupport extends SttpJson4sApi {
def enumSerializers: Seq[Serializer[_]] = Seq[Serializer[_]]() :+ def enumSerializers: Seq[Serializer[_]] = Seq[Serializer[_]]()
new EnumNameSerializer(OrderEnums.Status) :+
new EnumNameSerializer(PetEnums.Status)
private class EnumNameSerializer[E <: Enumeration: ClassTag](enum: E) extends Serializer[E#Value] { private class EnumNameSerializer[E <: Enumeration: ClassTag](enumeration: E) extends Serializer[E#Value] {
import JsonDSL._ import JsonDSL._
val EnumerationClass: Class[E#Value] = classOf[E#Value] val EnumerationClass: Class[E#Value] = classOf[E#Value]
def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), E#Value] = { def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), E#Value] = {
case (t @ TypeInfo(EnumerationClass, _), json) if isValid(json) => case (t @ TypeInfo(EnumerationClass, _), json) if isValid(json) =>
json match { json match {
case JString(value) => enum.withName(value) case JString(value) => enumeration.withName(value)
case value => throw new MappingException(s"Can't convert $value to $EnumerationClass") case value => throw new MappingException(s"Can't convert $value to $EnumerationClass")
} }
} }
private[this] def isValid(json: JValue) = json match { private[this] def isValid(json: JValue) = json match {
case JString(value) if enum.values.exists(_.toString == value) => true case JString(value) if enumeration.values.exists(_.toString == value) => true
case _ => false case _ => false
} }
@ -43,6 +41,6 @@ object JsonSupport extends SttpJson4sApi {
} }
} }
implicit val format: Formats = DefaultFormats ++ enumSerializers ++ DateSerializers.all implicit val format: Formats = DefaultFormats ++ enumSerializers ++ DateSerializers.all ++ AdditionalTypeSerializers.all
implicit val serialization: org.json4s.Serialization = org.json4s.jackson.Serialization implicit val serialization: org.json4s.Serialization = org.json4s.jackson.Serialization
} }