Skip to content

Commit

Permalink
Fix hidden schema visible in OpenAPI generated yml (#3820)
Browse files Browse the repository at this point in the history
  • Loading branch information
micossow authored Jun 5, 2024
1 parent cb5c2fe commit e2f61df
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import sttp.model.Method
import sttp.apispec.{Schema => ASchema, SchemaType => ASchemaType}
import sttp.apispec.openapi._
import sttp.tapir._
import sttp.tapir.EndpointIO.OneOfBody
import sttp.tapir.docs.apispec.DocsExtensionAttribute.{RichEndpointIOInfo, RichEndpointInfo}
import sttp.tapir.docs.apispec.schema.TSchemaToASchema
import sttp.tapir.docs.apispec.{DocsExtensions, SecurityRequirementsForEndpoints, SecuritySchemes, namedPathComponents}
Expand All @@ -23,7 +24,7 @@ private[openapi] class EndpointToOpenAPIPaths(
def pathItem(e: AnyEndpoint): (String, PathItem) = {
import Method._

val inputs = e.asVectorOfBasicInputs(includeAuth = false)
val inputs = filterOutHiddenInputs(e.asVectorOfBasicInputs(includeAuth = false))
val pathComponents = namedPathComponents(inputs)
val method = e.method.getOrElse(Method.GET)

Expand All @@ -44,6 +45,16 @@ private[openapi] class EndpointToOpenAPIPaths(
(e.showPathTemplate(showQueryParam = None, includeAuth = false, showNoPathAs = "/", showPathsAs = None), pathItem)
}

private def filterOutHiddenInputs(inputs: Vector[EndpointInput.Basic[_]]) = inputs.collect {
// EndpointInput.Basic is either OneOfBody or Atom
case OneOfBody(variants, mapping) =>
OneOfBody(
variants.filterNot(_.codec.schema.hidden),
mapping
)
case a: EndpointInput.Atom[_] if !a.codec.schema.hidden => a
}

private def endpointToOperation(defaultId: String, e: AnyEndpoint, inputs: Vector[EndpointInput.Basic[_]]): Operation = {
val parameters = operationParameters(inputs).distinct.toList
val body: Vector[ReferenceOr[RequestBody]] = operationInputBody(inputs)
Expand Down Expand Up @@ -99,11 +110,11 @@ private[openapi] class EndpointToOpenAPIPaths(

private def operationParameters(inputs: Vector[EndpointInput.Basic[_]]) = {
inputs.collect {
case q: EndpointInput.Query[_] if !q.codec.schema.hidden => enrich(q, queryToParameter(q))
case p: EndpointInput.PathCapture[_] if !p.codec.schema.hidden => enrich(p, pathCaptureToParameter(p))
case h: EndpointIO.Header[_] if !h.codec.schema.hidden => enrich(h, headerToParameter(h))
case c: EndpointInput.Cookie[_] if !c.codec.schema.hidden => enrich(c, cookieToParameter(c))
case f: EndpointIO.FixedHeader[_] if !f.codec.schema.hidden => enrich(f, fixedHeaderToParameter(f))
case q: EndpointInput.Query[_] => enrich(q, queryToParameter(q))
case p: EndpointInput.PathCapture[_] => enrich(p, pathCaptureToParameter(p))
case h: EndpointIO.Header[_] => enrich(h, headerToParameter(h))
case c: EndpointInput.Cookie[_] => enrich(c, cookieToParameter(c))
case f: EndpointIO.FixedHeader[_] => enrich(f, fixedHeaderToParameter(f))
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
openapi: 3.1.0
info:
title: test
version: '1.0'
paths:
/:
post:
operationId: postRoot
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Fruit'
required: true
responses:
'200':
description: ''
content:
text/plain:
schema:
type: string
'400':
description: Invalid value
content:
text/plain:
schema:
type: string
components:
schemas:
Fruit:
title: Fruit
type: object
required:
- f
properties:
f:
type: string
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
openapi: 3.1.0
info:
title: Hide security body
version: '1.0'
paths:
/api/echo:
post:
operationId: postApiEcho
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/FruitAmount'
required: true
responses:
'200':
description: ''
headers:
Set-Cookie:
required: false
schema:
type: array
items:
type: string
'400':
description: 'Invalid value for: body'
content:
text/plain:
schema:
type: string
components:
schemas:
FruitAmount:
title: FruitAmount
type: object
required:
- fruit
- amount
properties:
fruit:
type: string
amount:
type: integer
format: int32
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import sttp.apispec.openapi.Info
import sttp.apispec.openapi.circe.yaml._
import sttp.tapir.tests.OneOfBody.{
in_one_of_json_text_range_out_string,
in_one_of_json_xml_hidden_out_string,
in_one_of_json_xml_text_out_string,
in_string_out_one_of_json_xml_text
}
Expand Down Expand Up @@ -43,4 +44,15 @@ class VerifyYamlOneOfBodyTest extends AnyFunSuite with Matchers {

actualYamlNoIndent shouldBe expectedYaml
}

test("should respect hidden body") {
val expectedYaml = load("oneOfBody/expected_in_json_xml_hidden.yml")

val e = in_one_of_json_xml_hidden_out_string

val actualYaml = OpenAPIDocsInterpreter().toOpenAPI(e, Info("test", "1.0")).toYaml
val actualYamlNoIndent = noIndentation(actualYaml)

actualYamlNoIndent shouldBe expectedYaml
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,14 @@ import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers
import sttp.apispec.openapi.Info
import sttp.apispec.openapi.circe.yaml._
import sttp.tapir.json.circe.jsonBody
import sttp.tapir.model.UsernamePassword
import sttp.tapir.tests.data.FruitAmount
import sttp.tapir.{auth, endpoint, header, path, stringBody, _}
import sttp.tapir.generic.Derived
import sttp.tapir.generic.auto._
import sttp.tapir.json.circe._
import io.circe.generic.auto._

import scala.collection.immutable.{ListMap, Seq}

Expand Down Expand Up @@ -165,4 +171,19 @@ class VerifyYamlSecurityTest extends AnyFunSuite with Matchers {
actualYamlNoIndent shouldBe expectedYaml
}

test("should respect hidden annotation for security body") {
val e = endpoint.post
.securityIn(byteArrayBody.schema(_.hidden(true)))
.in("api" / "echo")
.in(jsonBody[FruitAmount])
.out(header[List[String]]("Set-Cookie"))

val actualYaml = OpenAPIDocsInterpreter()
.toOpenAPI(e, Info("Hide security body", "1.0"))
.toYaml

val expectedYaml = load("security/expected_hide_security_body.yml")

noIndentation(actualYaml) shouldBe expectedYaml
}
}
9 changes: 9 additions & 0 deletions tests/src/main/scala/sttp/tapir/tests/OneOfBody.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ object OneOfBody {
)
.out(stringBody)

val in_one_of_json_xml_hidden_out_string: PublicEndpoint[Fruit, Unit, String, Any] = endpoint.post
.in(
oneOfBody(
jsonBody[Fruit],
xmlBody[Fruit].schema(_.hidden(true))
)
)
.out(stringBody)

val in_one_of_json_text_range_out_string: PublicEndpoint[Fruit, Unit, String, Any] = endpoint.post
.in(
oneOfBody(
Expand Down

0 comments on commit e2f61df

Please sign in to comment.