Skip to content
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

FDN-3186 Update scala3 union support for primitive types #708

Merged
merged 1 commit into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
356 changes: 356 additions & 0 deletions lib/src/test/resources/example-union-types-play-29-scala-3.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,356 @@
/**
* Generated by API Builder - https://www.apibuilder.io
* Service version: 0.3.46
*/
package io.apibuilder.example.union.types.v0.models {

sealed trait Baz extends _root_.scala.Product with _root_.scala.Serializable

/**
* Provides future compatibility in clients - in the future, when a type is added
* to the union Baz, it will need to be handled in the client code. This
* implementation will deserialize these future types as an instance of this class.
*
* @param description Information about the type that we received that is undefined in this version of
* the client.
*/

final case class BazUndefinedType(
description: String
) extends Baz

/**
* Wrapper class to support the union types containing the datatype[integer]
*/

final case class BazInt(
value: Int
) extends Baz

}

package io.apibuilder.example.union.types.v0.models {

package object json {
import play.api.libs.json.__
import play.api.libs.json.JsString
import play.api.libs.json.Writes
import play.api.libs.functional.syntax._
import io.apibuilder.example.union.types.v0.models.json._

private[v0] implicit val jsonReadsUUID: play.api.libs.json.Reads[_root_.java.util.UUID] = __.read[String].map { str =>
_root_.java.util.UUID.fromString(str)
}

private[v0] implicit val jsonWritesUUID: play.api.libs.json.Writes[_root_.java.util.UUID] = (x: _root_.java.util.UUID) => play.api.libs.json.JsString(x.toString)

private[v0] implicit val jsonReadsJodaDateTime: play.api.libs.json.Reads[_root_.org.joda.time.DateTime] = __.read[String].map { str =>
_root_.org.joda.time.format.ISODateTimeFormat.dateTimeParser.parseDateTime(str)
}

private[v0] implicit val jsonWritesJodaDateTime: play.api.libs.json.Writes[_root_.org.joda.time.DateTime] = (x: _root_.org.joda.time.DateTime) => {
play.api.libs.json.JsString(_root_.org.joda.time.format.ISODateTimeFormat.dateTime.print(x))
}

private[v0] implicit val jsonReadsJodaLocalDate: play.api.libs.json.Reads[_root_.org.joda.time.LocalDate] = __.read[String].map { str =>
_root_.org.joda.time.format.ISODateTimeFormat.dateTimeParser.parseLocalDate(str)
}

private[v0] implicit val jsonWritesJodaLocalDate: play.api.libs.json.Writes[_root_.org.joda.time.LocalDate] = (x: _root_.org.joda.time.LocalDate) => {
play.api.libs.json.JsString(_root_.org.joda.time.format.ISODateTimeFormat.date.print(x))
}

implicit def jsonReadsApidocExampleUnionTypesPrimitivesBazInt: play.api.libs.json.Reads[io.apibuilder.example.union.types.v0.models.BazInt] = {
(__ \ "value").read[Int].map { x => BazInt(value = x) }
}

implicit def jsonReadsApidocExampleUnionTypesPrimitivesBaz[T <: io.apibuilder.example.union.types.v0.models.Baz]: play.api.libs.json.Reads[T] = (json: play.api.libs.json.JsValue) => {
Seq(
(__ \ "integer").read(jsonReadsApidocExampleUnionTypesPrimitivesBazInt).reads(json).map(_.asInstanceOf[T])
).view.find(_.isSuccess).getOrElse {
play.api.libs.json.JsSuccess(BazUndefinedType(json.toString).asInstanceOf[T])
}
}



implicit def jsonReadsApidocExampleUnionTypesPrimitivesBazSeq[T <: io.apibuilder.example.union.types.v0.models.Baz]: play.api.libs.json.Reads[Seq[T]] = {
case a: play.api.libs.json.JsArray => {
val all: Seq[play.api.libs.json.JsResult[io.apibuilder.example.union.types.v0.models.Baz]] = a.value.map(jsonReadsApidocExampleUnionTypesPrimitivesBaz.reads).toSeq

all.collect { case e: play.api.libs.json.JsError => e }.toList match {
case Nil => play.api.libs.json.JsSuccess(all.collect { case play.api.libs.json.JsSuccess(v, _) => v.asInstanceOf[T] })
case errors => play.api.libs.json.JsError(play.api.libs.json.JsError.merge(errors.flatMap(_.errors), Nil))
}
}
case other => play.api.libs.json.JsError(s"Expected array but found [" + other.getClass.getName + "]")
}

def jsObjectBaz(obj: io.apibuilder.example.union.types.v0.models.Baz): play.api.libs.json.JsObject = {
obj match {
case x: io.apibuilder.example.union.types.v0.models.BazInt => play.api.libs.json.Json.obj("integer" -> play.api.libs.json.Json.obj("value" -> play.api.libs.json.JsNumber(x.value)))
case x: io.apibuilder.example.union.types.v0.models.BazUndefinedType => {
scala.util.Try {
// If we received a JSON object - echo it back. This is a workaround for a bug in
// serialization for unions w/out discriminators where they sometimes have the
// type wrapper and sometimes do not
play.api.libs.json.Json.parse(x.description).asInstanceOf[play.api.libs.json.JsObject]
} match {
case scala.util.Success(o) => o
case scala.util.Failure(_) => sys.error("The type[io.apibuilder.example.union.types.v0.models.BazUndefinedType] should never be serialized")
}
}
}
}

implicit def jsonWritesApidocExampleUnionTypesPrimitivesBaz[T <: io.apibuilder.example.union.types.v0.models.Baz]: play.api.libs.json.Writes[T] = {
(obj: io.apibuilder.example.union.types.v0.models.Baz) => {
io.apibuilder.example.union.types.v0.models.json.jsObjectBaz(obj)
}
}
}
}

package io.apibuilder.example.union.types.v0 {

object Bindables {

import play.api.mvc.{PathBindable, QueryStringBindable}

// import models directly for backwards compatibility with prior versions of the generator
import Core._

object Core {
implicit def pathBindableDateTimeIso8601(implicit stringBinder: QueryStringBindable[String]): PathBindable[_root_.org.joda.time.DateTime] = ApibuilderPathBindable(ApibuilderTypes.dateTimeIso8601)
implicit def queryStringBindableDateTimeIso8601(implicit stringBinder: QueryStringBindable[String]): QueryStringBindable[_root_.org.joda.time.DateTime] = ApibuilderQueryStringBindable(ApibuilderTypes.dateTimeIso8601)

implicit def pathBindableDateIso8601(implicit stringBinder: QueryStringBindable[String]): PathBindable[_root_.org.joda.time.LocalDate] = ApibuilderPathBindable(ApibuilderTypes.dateIso8601)
implicit def queryStringBindableDateIso8601(implicit stringBinder: QueryStringBindable[String]): QueryStringBindable[_root_.org.joda.time.LocalDate] = ApibuilderQueryStringBindable(ApibuilderTypes.dateIso8601)
}

trait ApibuilderTypeConverter[T] {

def convert(value: String): T

def convert(value: T): String

def example: T

def validValues: Seq[T] = Nil

def errorMessage(key: String, value: String, ex: java.lang.Exception): String = {
val base = s"Invalid value '$value' for parameter '$key'. "
validValues.toList match {
case Nil => base + "Ex: " + convert(example)
case values => base + ". Valid values are: " + values.mkString("'", "', '", "'")
}
}
}

object ApibuilderTypes {
val dateTimeIso8601: ApibuilderTypeConverter[_root_.org.joda.time.DateTime] = new ApibuilderTypeConverter[_root_.org.joda.time.DateTime] {
override def convert(value: String): _root_.org.joda.time.DateTime = _root_.org.joda.time.format.ISODateTimeFormat.dateTimeParser.parseDateTime(value)
override def convert(value: _root_.org.joda.time.DateTime): String = _root_.org.joda.time.format.ISODateTimeFormat.dateTime.print(value)
override def example: _root_.org.joda.time.DateTime = _root_.org.joda.time.DateTime.now
}

val dateIso8601: ApibuilderTypeConverter[_root_.org.joda.time.LocalDate] = new ApibuilderTypeConverter[_root_.org.joda.time.LocalDate] {
override def convert(value: String): _root_.org.joda.time.LocalDate = _root_.org.joda.time.format.ISODateTimeFormat.dateTimeParser.parseLocalDate(value)
override def convert(value: _root_.org.joda.time.LocalDate): String = _root_.org.joda.time.format.ISODateTimeFormat.date.print(value)
override def example: _root_.org.joda.time.LocalDate = _root_.org.joda.time.LocalDate.now
}
}

final case class ApibuilderQueryStringBindable[T](
converters: ApibuilderTypeConverter[T]
) extends QueryStringBindable[T] {

override def bind(key: String, params: Map[String, Seq[String]]): _root_.scala.Option[_root_.scala.Either[String, T]] = {
params.getOrElse(key, Nil).headOption.map { v =>
try {
Right(
converters.convert(v)
)
} catch {
case ex: java.lang.Exception => Left(
converters.errorMessage(key, v, ex)
)
}
}
}

override def unbind(key: String, value: T): String = {
s"$key=${converters.convert(value)}"
}
}

final case class ApibuilderPathBindable[T](
converters: ApibuilderTypeConverter[T]
) extends PathBindable[T] {

override def bind(key: String, value: String): _root_.scala.Either[String, T] = {
try {
Right(
converters.convert(value)
)
} catch {
case ex: java.lang.Exception => Left(
converters.errorMessage(key, value, ex)
)
}
}

override def unbind(key: String, value: T): String = {
converters.convert(value)
}
}

}

}


package io.apibuilder.example.union.types.v0 {

object Constants {

val Namespace = "io.apibuilder.example.union.types.v0"
val UserAgent = "apibuilder-play_2x_client-unknown"
val Version = "0.3.46"
val VersionMajor = 0

}

class Client(
ws: play.api.libs.ws.WSClient,
val baseUrl: String,
auth: scala.Option[io.apibuilder.example.union.types.v0.Authorization] = None,
defaultHeaders: Seq[(String, String)] = Nil
) extends interfaces.Client {
import io.apibuilder.example.union.types.v0.models.json._

private val logger = play.api.Logger("io.apibuilder.example.union.types.v0.Client")

logger.info(s"Initializing io.apibuilder.example.union.types.v0.Client for url $baseUrl")





def _requestHolder(path: String): play.api.libs.ws.WSRequest = {

val holder = ws.url(baseUrl + path).addHttpHeaders(
"User-Agent" -> Constants.UserAgent,
"X-Apidoc-Version" -> Constants.Version,
"X-Apidoc-Version-Major" -> Constants.VersionMajor.toString
).addHttpHeaders(defaultHeaders*)
auth.fold(holder) {
case Authorization.Basic(username, password) => {
holder.withAuth(username, password.getOrElse(""), play.api.libs.ws.WSAuthScheme.BASIC)
}
}
}

def _logRequest(method: String, req: play.api.libs.ws.WSRequest): play.api.libs.ws.WSRequest = {
val queryComponents = for {
(name, values) <- req.queryString
value <- values
} yield s"$name=$value"
val url = s"${req.url}${queryComponents.mkString("?", "&", "")}"
auth.fold(logger.info(s"curl -X $method '$url'")) { _ =>
logger.info(s"curl -X $method -u '[REDACTED]:' '$url'")
}
req
}

def _executeRequest(
method: String,
path: String,
queryParameters: Seq[(String, String)] = Nil,
requestHeaders: Seq[(String, String)] = Nil,
body: Option[play.api.libs.json.JsValue] = None
): scala.concurrent.Future[play.api.libs.ws.WSResponse] = {
import play.api.libs.ws.WSBodyWritables.writeableOf_JsValue

method.toUpperCase match {
case "GET" => {
_logRequest("GET", _requestHolder(path).addHttpHeaders(requestHeaders*).addQueryStringParameters(queryParameters*)).get()
}
case "POST" => {
_logRequest("POST", _requestHolder(path).addHttpHeaders(_withJsonContentType(requestHeaders)*).addQueryStringParameters(queryParameters*)).post(body.getOrElse(play.api.libs.json.Json.obj()))
}
case "PUT" => {
_logRequest("PUT", _requestHolder(path).addHttpHeaders(_withJsonContentType(requestHeaders)*).addQueryStringParameters(queryParameters*)).put(body.getOrElse(play.api.libs.json.Json.obj()))
}
case "PATCH" => {
_logRequest("PATCH", _requestHolder(path).addHttpHeaders(requestHeaders*).addQueryStringParameters(queryParameters*)).patch(body.getOrElse(play.api.libs.json.Json.obj()))
}
case "DELETE" => {
_logRequest("DELETE", _requestHolder(path).addHttpHeaders(requestHeaders*).addQueryStringParameters(queryParameters*)).delete()
}
case "HEAD" => {
_logRequest("HEAD", _requestHolder(path).addHttpHeaders(requestHeaders*).addQueryStringParameters(queryParameters*)).head()
}
case "OPTIONS" => {
_logRequest("OPTIONS", _requestHolder(path).addHttpHeaders(requestHeaders*).addQueryStringParameters(queryParameters*)).options()
}
case _ => {
_logRequest(method, _requestHolder(path).addHttpHeaders(requestHeaders*).addQueryStringParameters(queryParameters*))
sys.error("Unsupported method[%s]".format(method))
}
}
}

/**
* Adds a Content-Type: application/json header unless the specified requestHeaders
* already contain a Content-Type header
*/
def _withJsonContentType(headers: Seq[(String, String)]): Seq[(String, String)] = {
headers.find { _._1.toUpperCase == "CONTENT-TYPE" } match {
case None => headers ++ Seq("Content-Type" -> "application/json; charset=UTF-8")
case Some(_) => headers
}
}

}

object Client {

def parseJson[T](
className: String,
r: play.api.libs.ws.WSResponse,
f: (play.api.libs.json.JsValue => play.api.libs.json.JsResult[T])
): T = {
f(play.api.libs.json.Json.parse(r.body)) match {
case play.api.libs.json.JsSuccess(x, _) => x
case play.api.libs.json.JsError(errors) => {
throw io.apibuilder.example.union.types.v0.errors.FailedRequest(r.status, s"Invalid json for class[" + className + "]: " + errors.mkString(" "))
}
}
}

}

sealed trait Authorization extends _root_.scala.Product with _root_.scala.Serializable
object Authorization {
final case class Basic(username: String, password: Option[String] = None) extends Authorization
}

package interfaces {

trait Client {
def baseUrl: String

}

}



package errors {

final case class FailedRequest(responseCode: Int, message: String, requestUri: Option[_root_.java.net.URI] = None) extends _root_.java.lang.Exception(s"HTTP $responseCode: $message")

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"apidoc": {
"version": "0.11.17"
},
"name": "apidoc-example-union-types-primitives",
"organization": {
"key": "bryzek"
},
"application": {
"key": "apidoc-example-union-types-primitives"
},
"namespace": "io.apibuilder.example.union.types.v0",
"version": "0.3.46",
"info": {},
"headers": [],
"imports": [],
"enums": [],
"unions": [
{
"name": "baz",
"plural": "bazes",
"types": [
{
"type": "integer",
"attributes": []
}
],
"attributes": []
}
],
"models": [],
"resources": [],
"attributes": []
}
Loading
Loading