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

codegen: support enum query params #3602

Merged

Conversation

hughsimpson
Copy link
Contributor

@hughsimpson hughsimpson commented Mar 13, 2024

Support for enum query params in codegen. I've gone for case-insensitive decoding here, but arguably case-sensitive would be the better option; it should be very easy to change or make configurable later on.

@hughsimpson hughsimpson changed the title support enum query params codegen: support enum query params (scala 2 only) Mar 13, 2024
@hughsimpson
Copy link
Contributor Author

I think the failure was due to an unrelated flaky test rather than an issue with the code. Would someone mind kicking the test?

@kciesielski
Copy link
Member

Sure, tests kicked and now all pass. I'll do a review soon.

@hughsimpson hughsimpson force-pushed the enum_query_param_support_scala_2 branch from 6709a66 to d1bbdd7 Compare March 14, 2024 10:02
@kciesielski
Copy link
Member

I managed to hack a working solution for Scala 3:

package sttp.tapir.generated

object TapirGeneratedEndpoints {

  import sttp.tapir._
  import sttp.tapir.model._
  import sttp.tapir.json.circe._
  import sttp.tapir.generic.auto._
  import io.circe.generic.auto._
  import scala.compiletime.{erasedValue, summonInline}
  import scala.deriving.Mirror
  import enumextensions.EnumMirror

  def enumMap[E: EnumMirror]: Map[String, E] =
    Map.from(                   
      for e <- EnumMirror[E].values yield e.name.toUpperCase -> e
    )

  def makeQueryCodecForEnum3[T: EnumMirror]: sttp.tapir.Codec[List[String], T, sttp.tapir.CodecFormat.TextPlain] =
    sttp.tapir.Codec
      .listHead[String, String, sttp.tapir.CodecFormat.TextPlain]
      .mapDecode(s =>
        // Case-insensitive mapping
        scala.util
          .Try(enumMap[T](using EnumMirror[T])(s.toUpperCase)) // TODO a nicer error on the stack trace instead of java.util.NoSuchElementException: key not found
          .fold(sttp.tapir.DecodeResult.Error(s, _), sttp.tapir.DecodeResult.Value(_))
      )(_.name)

  enum MyEnumKc derives org.latestbit.circe.adt.codec.JsonTaggedAdt.PureCodec, EnumMirror {
    case paperback, hardback
  }

  object MyEnumKc {
    given codecTapir: sttp.tapir.Codec[List[String], MyEnumKc, sttp.tapir.CodecFormat.TextPlain] =
      makeQueryCodecForEnum3[MyEnumKc]
  }

  import cats.effect.IO
  lazy val postQueryTest =
    endpoint.post
      .in(("queryTest"))
      .in(query[MyEnumKc]("test"))
      .out(statusCode(sttp.model.StatusCode.NoContent))
      .serverLogic[IO] { (test: MyEnumKc) =>
        // Some server logic
        IO.println(s"Received: $test").map(Right(_))

      }
        

  lazy val generatedEndpoints = List(postQueryTest)

}

It requires an additional dependency on "io.github.bishabosha" %% "enum-extensions" % "0.1.1", but I think it's a safe and stable library without risky transitive deps. Note that the enum needs a derives EnumMirror for this to work.

What do you think @hughsimpson?

@hughsimpson
Copy link
Contributor Author

Nice! I was a bit uncomfortable about not having scala 3 support. Let's include that in this pr

@hughsimpson
Copy link
Contributor Author

Pushed up. Shame we can't directly check the scala 3 support in the test 😪 but it looks right to me...

@kciesielski kciesielski changed the title codegen: support enum query params (scala 2 only) codegen: support enum query params Mar 14, 2024
@kciesielski
Copy link
Member

Thanks! I checked Scala 3 in my side project and it works, I also added a small improvement to the error message. Could you update the description to reflect Scala 3 support? Feel free to merge after that (if you have permissions, I'm not sure about that :)

@hughsimpson
Copy link
Contributor Author

I don't have permissions to merge, sadly, and the test makes some rather bold assertions about the precise code generated for scala 3, so I think that will need updating anyway. End of my day now but I'll push up a fix first thing tomorrow morning

@hughsimpson
Copy link
Contributor Author

Pushed a fix for the test; also updated the scala 2 decoding to provide the same nicer error message as for scala 3

@hughsimpson
Copy link
Contributor Author

Thanks for your help with this, @kciesielski! I'm hoping to be able to open a pr with support for jsoniter later on today once I've tidied up a bit, and it builds off this branch (because it turns out that tracking which positions the models are used in is very useful for that 😄)

@kciesielski
Copy link
Member

Thank you for these great contributions! Keep'em coming 😅

@kciesielski kciesielski merged commit 633810b into softwaremill:master Mar 15, 2024
23 checks passed
@hughsimpson hughsimpson deleted the enum_query_param_support_scala_2 branch March 15, 2024 09:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants