Skip to content

Commit

Permalink
fix: "name" and "in" should not be included in openapi headers (#3417)
Browse files Browse the repository at this point in the history
Exclude the "name" and "in" fields from openapi schema generated for headers.

Add ``BaseSchemaObject._iter_fields()``  method that allows schema types to define the fields that should be included in their openapi schema representation and override that method for ``OpenAPIHeader``.

Closes #3416
  • Loading branch information
peterschutt authored Apr 23, 2024
1 parent a31d1c6 commit c372633
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 8 deletions.
11 changes: 9 additions & 2 deletions litestar/openapi/spec/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

from dataclasses import asdict, dataclass, fields, is_dataclass
from enum import Enum
from typing import Any
from typing import TYPE_CHECKING, Any

if TYPE_CHECKING:
from collections.abc import Iterator
from dataclasses import Field

__all__ = ("BaseSchemaObject",)

Expand Down Expand Up @@ -34,13 +38,16 @@ def _normalize_value(value: Any) -> Any:
class BaseSchemaObject:
"""Base class for schema spec objects"""

def _iter_fields(self) -> Iterator[Field[Any]]:
yield from fields(self)

def to_schema(self) -> dict[str, Any]:
"""Transform the spec dataclass object into a string keyed dictionary. This method traverses all nested values
recursively.
"""
result: dict[str, Any] = {}

for field in fields(self):
for field in self._iter_fields():
value = _normalize_value(getattr(self, field.name, None))

if value is not None:
Expand Down
10 changes: 8 additions & 2 deletions litestar/openapi/spec/header.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from __future__ import annotations

from dataclasses import dataclass
from typing import TYPE_CHECKING, Any, Literal
from dataclasses import Field, dataclass
from typing import TYPE_CHECKING, Any, Iterator, Literal

from typing_extensions import override

from litestar.openapi.spec.base import BaseSchemaObject

Expand Down Expand Up @@ -119,3 +121,7 @@ class OpenAPIHeader(BaseSchemaObject):
The key is the media type and the value describes it. The map MUST only contain one entry.
"""

@override
def _iter_fields(self) -> Iterator[Field[Any]]:
yield from (f for f in super()._iter_fields() if f.name not in {"name", "param_in"})
Empty file.
29 changes: 29 additions & 0 deletions tests/e2e/test_openapi/test_spec_headers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from litestar import Litestar, Request, get
from litestar.datastructures import ResponseHeader


@get("/")
async def hello_world1(request: Request) -> None:
request.logger.info("inside request")
return


app1 = Litestar(
route_handlers=[hello_world1],
response_headers=[ResponseHeader(name="X-Version", value="ABCD", description="Test")],
)


def test_included_header_fields() -> None:
# https://github.com/litestar-org/litestar/issues/3416

assert app1.openapi_schema.to_schema()["paths"]["/"]["get"]["responses"]["200"]["headers"] == {
"X-Version": {
"allowEmptyValue": False,
"allowReserved": False,
"deprecated": False,
"description": "Test",
"required": False,
"schema": {"type": "string"},
}
}
4 changes: 0 additions & 4 deletions tests/unit/test_openapi/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,12 @@ def test_merged_components_correct() -> None:
"examples": {"example-one": {"summary": "an example"}},
"headers": {
"one": {
"name": "",
"in": "header",
"required": False,
"deprecated": False,
"allowEmptyValue": False,
"allowReserved": False,
},
"two": {
"name": "",
"in": "header",
"required": False,
"deprecated": False,
"allowEmptyValue": False,
Expand Down

0 comments on commit c372633

Please sign in to comment.