Skip to content

Commit 961c2e4

Browse files
JennyLeahyJenny Leahy
andauthored
[scala][client]: scala-http4s: minor improvement (enum companion method, remove implicit, error handling) (#19901)
* [scala][http4s][client]: add enum method; remove implicit * improve error handling * more enum enhancement * remove unused * update samle * avoid breaking change --------- Co-authored-by: Jenny Leahy <jennyleahy@JENNYLEAHY.localdomain>
1 parent 3a36882 commit 961c2e4

19 files changed

+112
-100
lines changed

modules/openapi-generator/src/main/resources/scala-http4s/api.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class {{classname}}EndpointsImpl[F[*]: Concurrent](
4848
formParameters = {{^hasFormParams}}None,{{/hasFormParams}}{{#hasFormParams}}formParameters,{{/hasFormParams}}
4949
queryParameters = {{^hasQueryParams}}Nil,{{/hasQueryParams}}{{#hasQueryParams}}queryParameters,{{/hasQueryParams}}
5050
requestHeaders = requestHeaders,
51-
auth = {{#authMethods}}Some(auth){{/authMethods}}{{^authMethods}}None{{/authMethods}}) {
51+
auth = {{#authMethods.0}}Some(auth){{/authMethods.0}}{{^authMethods}}None{{/authMethods}}) {
5252
{{>responseState}}
5353
}
5454
}

modules/openapi-generator/src/main/resources/scala-http4s/baseClient.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ abstract class BaseClient[F[*]: Concurrent](
3333
queryParameters: Seq[(String, Any)] = Nil,
3434
requestHeaders: Seq[(String, String)] = Nil,
3535
auth: Option[_Authorization] = None
36-
)(handler: Response[F] => F[U])(implicit encoder: Encoder[T]): F[U] = {
36+
)(handler: Response[F] => F[U])(using Encoder[T]): F[U] = {
3737
3838
val m = Method.fromString(method) match {
3939
case Right(m) => m
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{{#dataType}}case r if r.status.code == {{code}} => parseJson[F, {{dataType}}]("{{dataType}}", r).flatMap(res => Concurrent[F].raiseError(_FailedRequest(r.status.code, res.asJson.noSpaces))){{/dataType}}{{^dataType}}case r if r.status.code == {{code}} => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)){{/dataType}}
1+
{{#dataType}}case r if r.status.code == {{code}} => parseJson[F, {{dataType}}]("{{dataType}}", r).flatMap(res => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason, Some(res.asJson)))){{/dataType}}{{^dataType}}case r if r.status.code == {{code}} => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)){{/dataType}}

modules/openapi-generator/src/main/resources/scala-http4s/failedRequest.mustache

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,33 @@
11
{{>licenseInfo}}
22
package {{modelPackage}}
33

4-
import io.circe.*
5-
import io.circe.Decoder.*
6-
import io.circe.Encoder.*
4+
import io.circe.{Decoder, Encoder, Json}
75
import io.circe.syntax.*
86

9-
case class _FailedRequest(code: Int, message: String) extends Exception(s"Server return status code: $code; message: $message")
7+
case class _FailedRequest(code: Int, message: String, body: Option[Json] = None)
8+
extends Exception(s"Server returned status $code; message: $message; body: ${body.map(_.noSpaces).getOrElse("")}")
109

1110
object _FailedRequest {
1211
1312
given encoderFailedRequest: Encoder[_FailedRequest] = Encoder.instance { t =>
1413
Json.fromFields{
1514
Seq(
16-
"code" -> t.code.asJson,
17-
"message" -> t.message.asJson
18-
)
15+
Some("code" -> t.code.asJson),
16+
Some("message" -> t.message.asJson),
17+
t.body.map(x => "body" -> x)
18+
).flatten
1919
}
2020
}
2121

2222
given decodeFailedRequest: Decoder[_FailedRequest] = Decoder.instance { c =>
2323
for {
2424
code <- c.downField("code").as[Int]
2525
message <- c.downField("message").as[String]
26+
body <- c.downField("body").as[Option[Json]]
2627
} yield _FailedRequest(
2728
code = code,
28-
message = message
29+
message = message,
30+
body = body
2931
)
3032
}
3133

modules/openapi-generator/src/main/resources/scala-http4s/jsonSupports.mustache

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ import {{modelPackage}}.*
1010

1111
object JsonSupports {
1212
13-
implicit def circeJsonEncoder[F[*]: Concurrent, A](implicit encoder: Encoder[A]): EntityEncoder[F, A] =
13+
implicit def circeJsonEncoder[F[*]: Concurrent, A](using Encoder[A]): EntityEncoder[F, A] =
1414
http4sCirce.jsonEncoderOf[F, A]
15-
implicit def circeJsonDecoder[F[*]: Concurrent, A](implicit decoder: Decoder[A]): EntityDecoder[F, A] =
15+
implicit def circeJsonDecoder[F[*]: Concurrent, A](using Decoder[A]): EntityDecoder[F, A] =
1616
http4sCirce.jsonOf[F, A]
1717
1818
def parseJson[F[*]: Concurrent, T](
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{{#allParams}}{{paramName}}: {{#required}}{{dataType}}{{/required}}{{^required}}Option[{{dataType}}]{{/required}}{{^defaultValue}}{{^required}} = None{{/required}}{{/defaultValue}}{{^-last}}, {{/-last}}{{/allParams}}{{#authMethods.0}}{{#isApiKey}})(implicit auth: _Authorization.ApiKey{{/isApiKey}}{{#isBasic}}{{#isBasicBasic}})(implicit auth: _Authorization.Basic{{/isBasicBasic}}{{#isBasicBearer}})(implicit auth: _Authorization.Bearer{{/isBasicBearer}}{{/isBasic}}{{/authMethods.0}}
1+
{{#allParams}}{{paramName}}: {{#required}}{{dataType}}{{/required}}{{^required}}Option[{{dataType}}]{{/required}}{{^defaultValue}}{{^required}} = None{{/required}}{{/defaultValue}}{{^-last}}, {{/-last}}{{/allParams}}{{#authMethods.0}}{{#isApiKey}})(using auth: _Authorization.ApiKey{{/isApiKey}}{{#isBasic}}{{#isBasicBasic}})(using auth: _Authorization.Basic{{/isBasicBasic}}{{#isBasicBearer}})(using auth: _Authorization.Bearer{{/isBasicBearer}}{{/isBasic}}{{/authMethods.0}}

modules/openapi-generator/src/main/resources/scala-http4s/model.mustache

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,14 @@ enum {{classname}}(val value: String) {
2626
}
2727

2828
object {{classname}} {
29-
given decoder{{classname}}: Decoder[{{classname}}] =
30-
Decoder.decodeString.map(str => {{classname}}.values.find(_.value == str)
31-
.getOrElse(throw java.lang.IllegalArgumentException(s"{{classname}} enum case not found: $str"))
32-
)
3329
34-
given encoder{{classname}}: Encoder[{{classname}}] =
35-
Encoder.encodeString.contramap[{{classname}}](_.value)
30+
def withValueOpt(value: String): Option[{{classname}}] = {{classname}}.values.find(_.value == value)
31+
def withValue(value: String): {{classname}} =
32+
withValueOpt(value).getOrElse(throw java.lang.IllegalArgumentException(s"{{classname}} enum case not found: $value"))
33+
34+
given decoder{{classname}}: Decoder[{{classname}}] = Decoder.decodeString.map(withValue)
35+
given encoder{{classname}}: Encoder[{{classname}}] = Encoder.encodeString.contramap[{{classname}}](_.value)
36+
3637
}
3738
{{/isEnum}}
3839
{{^isEnum}}
Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,34 @@
11
{{>licenseInfo}}
22
package {{packageName}}
33

4-
import io.circe.{Decoder, Encoder}
4+
import io.circe.{Decoder, Encoder, Json}
5+
import java.time.{Instant, LocalDate, OffsetDateTime}
6+
import java.util.UUID
57

68
package object models {
79
8-
given decodeUUID: Decoder[_root_.java.util.UUID] =
9-
Decoder.decodeString.map(str => _root_.java.util.UUID.fromString(str))
10+
given decodeUUID: Decoder[UUID] =
11+
Decoder.decodeString.map(str => UUID.fromString(str))
1012
11-
given encodeUUID: Encoder[_root_.java.util.UUID] =
12-
Encoder.encodeString.contramap[_root_.java.util.UUID](uuid => uuid.toString)
13+
given encodeUUID: Encoder[UUID] =
14+
Encoder.encodeString.contramap[UUID](uuid => uuid.toString)
1315
14-
given decodeInstant: Decoder[_root_.java.time.Instant] =
15-
Decoder.decodeString.map(str => _root_.java.time.OffsetDateTime.parse(str).toInstant)
16+
given decodeInstant: Decoder[Instant] =
17+
Decoder.decodeString.map(str => OffsetDateTime.parse(str).toInstant)
1618
17-
given encodeInstant: Encoder[_root_.java.time.Instant] =
18-
Encoder.encodeString.contramap[_root_.java.time.Instant](_.toString)
19+
given encodeInstant: Encoder[Instant] =
20+
Encoder.encodeString.contramap[Instant](_.toString)
1921
20-
given decodeLocalDate: Decoder[_root_.java.time.LocalDate] =
21-
Decoder.decodeString.map(str => _root_.java.time.LocalDate.parse(str))
22+
given decodeLocalDate: Decoder[LocalDate] =
23+
Decoder.decodeString.map(str => LocalDate.parse(str))
2224
23-
given encodeLocalDate: Encoder[_root_.java.time.LocalDate] =
24-
Encoder.encodeString.contramap[_root_.java.time.LocalDate](_.toString)
25+
given encodeLocalDate: Encoder[LocalDate] =
26+
Encoder.encodeString.contramap[LocalDate](_.toString)
2527
26-
given decodeJson: Decoder[io.circe.Json] =
27-
Decoder.decodeString.map(str => io.circe.Json.fromString(str))
28+
given decodeJson: Decoder[Json] =
29+
Decoder.decodeString.map(str => Json.fromString(str))
2830
29-
given encodeJson: Encoder[io.circe.Json] =
30-
Encoder.encodeString.contramap[io.circe.Json](_.toString)
31+
given encodeJson: Encoder[Json] =
32+
Encoder.encodeString.contramap[Json](_.toString)
3133
3234
}

modules/openapi-generator/src/main/resources/scala-http4s/responseState.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
{{>successResponsePart}}{{/is2xx}}{{^is2xx}}{{#is3xx}}
33
{{>successResponsePart}}{{/is3xx}}{{^is3xx}}{{^isDefault}}
44
{{>errorResponsePart}}{{/isDefault}}{{#isDefault}}
5-
{{#dataType}}case r => parseJson[F, {{dataType}}]("{{dataType}}", r).flatMap(res => Concurrent[F].raiseError(_FailedRequest(r.status.code, res.asJson.noSpaces))){{/dataType}}{{^dataType}}case r => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)){{/dataType}}{{/isDefault}}{{/is3xx}}{{/is2xx}}{{/responses}}{{/hasOnlyDefaultResponse}}
5+
{{#dataType}}case r => parseJson[F, {{dataType}}]("{{dataType}}", r).flatMap(res => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason, Some(res.asJson)))){{/dataType}}{{^dataType}}case r => Concurrent[F].raiseError(_FailedRequest(r.status.code, r.status.reason)){{/dataType}}{{/isDefault}}{{/is3xx}}{{/is2xx}}{{/responses}}{{/hasOnlyDefaultResponse}}

samples/client/petstore/scala-http4s/src/main/scala/org/openapitools/client/apis/BaseClient.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ abstract class BaseClient[F[*]: Concurrent](
4242
queryParameters: Seq[(String, Any)] = Nil,
4343
requestHeaders: Seq[(String, String)] = Nil,
4444
auth: Option[_Authorization] = None
45-
)(handler: Response[F] => F[U])(implicit encoder: Encoder[T]): F[U] = {
45+
)(handler: Response[F] => F[U])(using Encoder[T]): F[U] = {
4646

4747
val m = Method.fromString(method) match {
4848
case Right(m) => m

0 commit comments

Comments
 (0)