Skip to content

Add fix for explicit content-type header priority issue#1297

Merged
adamw merged 6 commits intomasterfrom
explicit_content_header_priority_fix
Jun 9, 2021
Merged

Add fix for explicit content-type header priority issue#1297
adamw merged 6 commits intomasterfrom
explicit_content_header_priority_fix

Conversation

@slabiakt
Copy link
Copy Markdown
Contributor

@slabiakt slabiakt commented Jun 7, 2021

Closes #1280

}

test("explicit Content-Type header should have priority over the codec") {
val ep = endpoint.out(stringBody and header("Content-Type", "text/csv"))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think it also might be beneficial to

  1. move this definition to tests/src/main/scala/sttp/tapir/tests/package.json (a reusable endpoint definition)
  2. add a server test (in ServerBasicTests) that when this endpoint is called, we get response with the correct content-type

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.

@adamw point 1. and 2. implemented, please review

.out(stringBody)
.name("Query with default")

val out_overridden_content_type_header: Endpoint[Unit, Unit, String, Any] =
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[minor, naming] this is in other places referred to as a fixed header - instead of overriden

basicRequest.post(baseUri).send(backend).map(_.body shouldBe Right(""))
},
testServer(out_overridden_content_type_header, "Overridden content-type header")((_: Unit) => pureResult("".asRight[Unit])) { (backend, baseUri) =>
basicRequest.get(baseUri).send(backend).map(_.headers.map(h => h.name -> h.value).toSet should contain ("Content-Type" -> "text/csv"))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'd rather run the .contains using a Header instance, as it properly checks headers using case-insentitive header name comparison

RequestBody(
info.description,
codecToMediaType(codec, info.examples),
codecToMediaType(codec, info.examples, Option.empty),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[minor] you can use None

.map(output => outputToResponses(output, ResponsesDefaultKey, None))
.getOrElse(ListMap())

private def extractForcedContentType(outputs: List[EndpointOutput[_]]): Option[String] = outputs match {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

again, I'd call this extractFixedContentType

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

another thing - outputs might be nested, e.g. using mapped pairs or simply pairs. The above will probably break given an output like .out(header("C-T", "...").and(header("another", "...")))

For this purposes, there's a traverseOutputs method which properly checks the entire structure

@slabiakt slabiakt changed the title Add test for explicit content-type header priority issue Add fix for explicit content-type header priority issue Jun 8, 2021
private def extractFixedContentType(outputs: List[EndpointOutput[_]]): Option[String] = {
outputs.flatMap(_.traverseOutputs {
case EndpointIO.FixedHeader(h, _, _) =>
if (h.name.equalsIgnoreCase("Content-Type")) Vector(Option(h.value)) else Vector(None)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

simpler: Vecotr(h.value) & Vector.empty. Then you just need to call .headOption on the result to get the first element (if any)

private def extractFixedContentType(outputs: List[EndpointOutput[_]]): Option[String] = {
outputs.flatMap(_.traverseOutputs {
case EndpointIO.FixedHeader(h, _, _) =>
if (h.name.equalsIgnoreCase("Content-Type")) Vector(h.value) else Vector.empty
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

you can use h.is for a case-insensitive check

basicRequest.post(baseUri).send(backend).map(_.body shouldBe Right(""))
},
testServer(out_fixed_content_type_header, "Fixed content-type header")((_: Unit) => pureResult("".asRight[Unit])) { (backend, baseUri) =>
basicRequest.get(baseUri).send(backend).map(_.headers should contain only Header("Content-Type", "text/csv"))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

the content-length header is also returned, so I think you'll have to drop the only in this test

@adamw adamw merged commit f16b9ba into master Jun 9, 2021
@mergify mergify Bot deleted the explicit_content_header_priority_fix branch June 9, 2021 08:10
@adamw
Copy link
Copy Markdown
Member

adamw commented Jun 9, 2021

Thanks!

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.

[BUG] Explicit header doesn't have priority over the codec in generated OpenAPI.

2 participants