tapir
tapir copied to clipboard
Comma separated values only works on scala 2.13+
Tapir version: 1.9.2
Hi peps!
Context
A very common use case when defining an endpoint is adding an (optional) filter of "categories". One way of doing so is as a list of comma-separated values, like
GET /tapirs?cute=true&breed=malayan,baird
Tapir, after this discussion, provides the handy CommaSeparatedValues.
This work perfectly, and the openapi documentation is generated as one would expect in the case of enumerations (with allowedValues).
The problem
The issue with this is that CommaSeparated is defined as
type CommaSeparated[T] = Delimited[",", T]
Unfortunately, type literals are only supported in Scala 2.13+. Which means, people using previous versions of Scala can't use this feature :(
One possible solution
The most trivial solution is just to hard-code the comma in CommaSeparated, of course
final case class CommaSeparated[T](values: List[T])
/** Creates a codec which handles values delimited using `,`. The implicit `T`-codec is used for handling each individual value.
*
* Upon decoding, the string is split using the delimiter, and then decoded using the `T`-codec. Upon encoding, the values are first
* encoded using the `T`-codec, and then combined using the delimiter.
*
* The codec's schema has the `explode` attribute set to `false`.
*/
implicit def commaSeparated[T](
implicit
codec: Codec[String, T, CodecFormat.TextPlain]
): Codec[String, CommaSeparated[T], CodecFormat.TextPlain] =
Codec.string
.map(_.split(",").toList)(_.mkString(","))
.mapDecode(ls => DecodeResult.sequence(ls.map(codec.decode)).map(_.toList))(_.map(codec.encode))
.schema(codec.schema.asIterable[List].attribute(Schema.Explode.Attribute, Schema.Explode(false)))
.map(CommaSeparated[T](_))(_.values)
implicit def schemaForCommaSeparated[D <: String, T](
implicit
tSchema: Schema[T]
): Schema[CommaSeparated[T]] =
tSchema.asIterable[List].map(l => Some(CommaSeparated[T](l)))(_.values).attribute(Explode.Attribute, Explode(false))
The disadvantage is that we'd not be reusing the exact same code we use for Delimited.
What to do next?
I personally see a few options
- Do nothing code-wise, but specify that the feature is only available in Scala 2.13 in the documentation
- Duplicate the code so that
CommaSeparatedis available for everyone andDelimitedonly for Scala 2.13+ - Remove
Delimitedaltogether and only keepCommaSeparated
The third option is quite extreme and causes a breaking change of course. On the other hand, I don't see a real use case for anything other than comma (though clearly you do, so perhaps it's just inexperience on my part), and by making this more generic we're sacrificing the functionality for an interesting portion of Scala users (according to this 2022 survery, about 40% of Scala users still use 2.12 in some projects). Do we have any kind of metric about how needed this functionality is?
Thanks for reading!
I'm aware that this is a Scala 2.13/3-only feature, and this was a conscious decision (Scala 2.12 users are about 9% of tapir's downloads).
Unfortunately breaking changes are not possible - we could only do that when bumping the major version of tapir-core. I think unless there's demand (please vote on this issue if it affects you!), the best course of action would be to define a comma-separated codec locally (probably copying much of the existing code, but hard-coding ,).
I'm aware that this is a Scala 2.13/3-only feature, and this was a conscious decision (Scala 2.12 users are about 9% of tapir's downloads).
Unfortunately breaking changes are not possible - we could only do that when bumping the major version of
tapir-core. I think unless there's demand (please vote on this issue if it affects you!), the best course of action would be to define a comma-separated codec locally (probably copying much of the existing code, but hard-coding,).
Yep, that's what I did indeed.
Thanks for the quick answer, then unless this really gets traction I guess it is what it is. What do you think of adding a quick note in the docs ? I could open a PR later today if you think it's a good idea
Ah yes sure, updating the docs is always good :)
thanks!