Skip to content

codegen: Fix code generation for some unsupported response combinations - #5592

Merged
adamw merged 4 commits into
softwaremill:masterfrom
hughsimpson:header_response_edge_case
Oct 13, 2026
Merged

adamw merged 4 commits into
softwaremill:masterfrom
hughsimpson:header_response_edge_case

Conversation

@hughsimpson

@hughsimpson hughsimpson commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor
  • Adds a @unchecked annotation on response check for streaming types (since generic param otherwise causes scala compiler to raise a warning which can be hard to silence)
  • Improves the codegen for some optionality-related response cases (non-json or custom-json bodies when another status code has no content)

I was initially trying to improve handling of non-required inputs too, but ran into a whole host of issues, so I've stripped this back to be more focused than it was before... There are almost certainly output cases that this will still not work for as well, but I think this pretty clearly increases the set of supported combinations.

}
case class PostJsonStringJsonBodyBodyOutFull (
override val `application/json`: () => Option[String],
override val `application/json`: () => String,

@hughsimpson hughsimpson Oct 12, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't really have been wrapped before -- the declaration is not for a nullable schema (the None case is on a separate status code with no declared content) so it made no sense to do it (just made it possible to represent invalid forms)

}
case class PostJsonStringJsonBodyBodyOption_String_Out(value: Option[String]) extends PostJsonStringJsonBodyBodyOut{
override def `application/json`: () => Option[String] = () => value
case class PostJsonStringJsonBodyBodyStringOut(value: String) extends PostJsonStringJsonBodyBodyOut{

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto (shouldn't've been wrapped at this level -- is handled by an outer oneOfVariantValueMatcher)

.in(("headers" / "tests"))
.securityIn(auth.oauth2.implicitFlow("https://petstore3.swagger.io/oauth/authorize", None))
.out(oneOf[(Option[String], PutHeadersTestsResponseHeader)](
oneOfVariantValueMatcher(sttp.model.StatusCode(200), stringBody.map(Option(_))(_.orNull).description("Strict text response with a header").and(header[String]("Content-Disposition").description("").map(PutHeadersTestsResponseHeader200(_))(_.`Content-Disposition`))){ case (Some(_: String), (_: PutHeadersTestsResponseHeader200)) => true },

@hughsimpson hughsimpson Oct 12, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generated code didn't compile before, because we were generating the outer oneOfVariantValueMatcher but the inner value didn't have an option type because it was missing the map. That new map should be safe given the matcher.

@hughsimpson
hughsimpson marked this pull request as ready for review October 12, 2026 17:41
val body = if (ContentTypes.isSuffixed(ct)) suffixedBody("sttp.tapir.Codec.string", ct) else "stringJsonBody"
// this doesn't use the wrap helpers to preserve what was going on on requests before it was implemented; may be buggy for those cases.
if (required) MappedContentType(body, "String", None)
else MappedContentType(s"$body.map(Option(_))(_.orNull)", "Option[String]", None)

@hughsimpson hughsimpson Oct 12, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would've messed around with this more but for the fact that I couldn't figure out a good way to handle non-required request bodies in the general case. It'll want looking at again but I think it can wait for now.

Comment left in as a hint...

@adamw

adamw commented Oct 13, 2026

Copy link
Copy Markdown
Member

Thank you - as always 👌

@adamw
adamw merged commit 8f04560 into softwaremill:master Oct 13, 2026
16 checks passed
@hughsimpson
hughsimpson deleted the header_response_edge_case branch October 13, 2026 20:05
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