Skip to content

Commit f12580b

Browse files
mmevprovinzkraut
andauthored
feat(openapi): add option to exclude parameter from schema (#4177)
* feat(openapi): added option to exclude parameter from schema (#4173) * feat(openapi): fixed condition in test * Update litestar/params.py Co-authored-by: Janek Nouvertné <provinzkraut@posteo.de> * Update litestar/params.py Co-authored-by: Janek Nouvertné <provinzkraut@posteo.de> * feat(openapi): fixed docstring --------- Co-authored-by: Janek Nouvertné <provinzkraut@posteo.de>
1 parent 0124dbe commit f12580b

4 files changed

Lines changed: 83 additions & 4 deletions

File tree

litestar/_openapi/parameters.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -194,13 +194,15 @@ def create_parameters_for_field_definitions(self, fields: dict[str, FieldDefinit
194194
)
195195

196196
for field_name, field_definition in unique_handler_fields:
197-
if (
198-
isinstance(field_definition.kwarg_definition, DependencyKwarg)
199-
and field_name not in self.dependency_providers
200-
):
197+
kwarg_definition = field_definition.kwarg_definition
198+
if isinstance(kwarg_definition, DependencyKwarg) and field_name not in self.dependency_providers:
201199
# never document explicit dependencies
202200
continue
203201

202+
if isinstance(kwarg_definition, ParameterKwarg) and not kwarg_definition.include_in_schema:
203+
# exclude parameters that are marked as not included in the schema
204+
continue
205+
204206
if provider := self.dependency_providers.get(field_name):
205207
self.create_parameters_for_field_definitions(fields=provider.parsed_fn_signature.parameters)
206208
else:

litestar/params.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,11 @@ class KwargDefinition:
124124
Use as the key for the reference when creating a component for this type
125125
.. versionadded:: 2.12.0
126126
"""
127+
include_in_schema: bool = True
128+
"""
129+
A boolean flag dictating whether this parameter should be included in the schema.
130+
.. versionadded:: 2.17.0
131+
"""
127132

128133
@property
129134
def is_constrained(self) -> bool:
@@ -201,6 +206,7 @@ def Parameter(
201206
title: str | None = None,
202207
schema_extra: dict[str, Any] | None = None,
203208
schema_component_key: str | None = None,
209+
include_in_schema: bool = True,
204210
) -> Any:
205211
"""Create an extended parameter kwarg definition.
206212
@@ -247,6 +253,7 @@ def Parameter(
247253
.. versionadded:: 2.8.0
248254
schema_component_key: Use this as the key for the reference when creating a component for this type
249255
.. versionadded:: 2.12.0
256+
include_in_schema: A boolean flag dictating whether this parameter should be included in the schema.
250257
"""
251258
return ParameterKwarg(
252259
annotation=annotation,
@@ -273,6 +280,7 @@ def Parameter(
273280
pattern=pattern,
274281
schema_extra=schema_extra,
275282
schema_component_key=schema_component_key,
283+
include_in_schema=include_in_schema,
276284
)
277285

278286

tests/unit/test_openapi/test_parameters.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,3 +475,33 @@ def handler(
475475
"allowEmptyValue": False,
476476
"allowReserved": False,
477477
}
478+
479+
480+
def test_not_included_in_schema_parameter() -> None:
481+
@get("/handler")
482+
async def handler(param: Annotated[str, Parameter(include_in_schema=False)]) -> None:
483+
pass
484+
485+
with create_test_client(handler) as client:
486+
response = client.get("/schema/openapi.json")
487+
488+
response_json = response.json()
489+
handler_schema = response_json["paths"]["/handler"]["get"]
490+
assert "parameters" not in handler_schema
491+
492+
493+
def test_two_parameters_but_one_not_included_in_schema() -> None:
494+
@get("/handler")
495+
def handler(param1: str, param2: str = Parameter(include_in_schema=False)) -> None:
496+
pass
497+
498+
with create_test_client(handler) as client:
499+
response = client.get("/schema/openapi.json")
500+
501+
response_json = response.json()
502+
handler_schema = response_json["paths"]["/handler"]["get"]
503+
assert "parameters" in handler_schema
504+
505+
parameter_names = {param["name"] for param in handler_schema["parameters"]}
506+
assert "param1" in parameter_names
507+
assert "param2" not in parameter_names

tests/unit/test_params.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,3 +294,42 @@ def test_optional_query_parameter_consistency_with_default_queried_with_other_pa
294294
assert optional_default_client.get("/optional-default", params={"param": "a"}).json() == {"key": None}
295295
assert optional_default_client.get("/optional-annotated-default", params={"abc": "xyz"}).json() == {"key": None}
296296
assert optional_default_client.get("/optional-annotated-default", params={"param": "a"}).json() == {"key": None}
297+
298+
299+
def test_not_included_in_schema_param_as_annotated() -> None:
300+
@get(path="/")
301+
def handler(param: Annotated[str, Parameter(include_in_schema=True)]) -> str:
302+
return param
303+
304+
with create_test_client(handler) as client:
305+
response = client.get("/")
306+
assert response.status_code == HTTP_400_BAD_REQUEST
307+
308+
response = client.get("/?param=a")
309+
assert response.status_code == HTTP_200_OK
310+
assert response.text == "a"
311+
312+
313+
def test_not_included_in_schema_param_as_default() -> None:
314+
@get(path="/")
315+
def handler(param: str = Parameter(include_in_schema=True)) -> str:
316+
return param
317+
318+
with create_test_client(handler) as client:
319+
response = client.get("/")
320+
assert response.status_code == HTTP_400_BAD_REQUEST
321+
322+
response = client.get("/?param=a")
323+
assert response.status_code == HTTP_200_OK
324+
assert response.text == "a"
325+
326+
327+
def test_not_included_in_schema_param_with_default_value() -> None:
328+
@get(path="/")
329+
def handler(param: str = Parameter(default="b", include_in_schema=True)) -> str:
330+
return param
331+
332+
with create_test_client(handler) as client:
333+
response = client.get("/")
334+
assert response.status_code == HTTP_200_OK
335+
assert response.text == "b"

0 commit comments

Comments
 (0)