Explicit 'explode: true' in the generated OpenAPI

Hello, to please my workplace’s API gateway, I’m trying to make the OpenAPI generated by Tapir explicitly add explode: true in the spec of a query parameter that takes a list:

I have a custom wrapper class to hold a non-empty set of Ints

final case class Tiles(ids: NonEmptySet[Int])

object Tiles {

  def from(ids: Seq[Int]): Option[Tiles] =
    NonEmptySet.fromSet(SortedSet(ids: _*)).map(Tiles(_))
}

And I’m defining a custom codec

  implicit val tilesCodec: Codec[List[String], Tiles, TextPlain] =
    Codec
      .list(Codec.int.validate(Validator.min(lowerBound).and(Validator.max(upperBound))))
      .schema(_.copy(isOptional = false).attribute(Schema.Explode.Attribute, Schema.Explode(true)))
      .validate(Validator.minSize(1).and(Validator.maxSize(maxTilesPerReq)))
      .mapDecode(ids => DecodeResult.fromOption(Tiles.from(ids)))(_.toList)

But the explode attribute doesn’t seem to be picked up by the OpenAPI derivation, and I need to manually patch it.

Am I missing something?

That’s because true is the default value for the explode attribute in case of query parameters (as per the spec), so it’s only added if the value specified is false.

As a work-around, I think adding a docs extension should work:

query[Tiles]("tiles").docsExtension("exploded", true)

We can change the code linked above as well, if explicitly specifying true would have any uses? (well I guess it would, since you have a problem with it, question is if that’s a rare exception or a rule :slight_smile: )

1 Like

Hi Adam, thanks for the suggestion but it doesn’t seem to work either.
The extension is properly added to the OpenAPI object but the attribute gets lost during serialization there:

Because jsonWithoutExt contains explode: null by default. This behavior would also affect other extension attributes trying to override those of the model specification.

A simple fix would be to swap the deepMerge operands:

extensions
  .flatMap(_.asObject)
  .map(extObject => jsonWithoutExt.deepMerge(extObject))
  .getOrElse(jsonWithoutExt)

But I don’t know if it’s a more desirable behavior. I believe letting EndpointToOpenAPIPaths set the explode attribute explicitly even when redundant might be better.

What do you think? Either way I can open a PR.

Hm I think I tested this on your example and it worked, but maybe it was somehow simplified. I think the overriding behavior you describe is fine, let’s try opening a PR and we’ll see if any tests fail :wink:

Here you go: Allow doc extensions to override model's default attributes by hygt · Pull Request #113 · softwaremill/sttp-apispec · GitHub :slight_smile: