summaryrefslogtreecommitdiff
path: root/venv/lib/python3.11/site-packages/litestar/_openapi/path_item.py
blob: 74a04ce756acf101e7efdadf25b35e6b68decb57 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
from __future__ import annotations

from inspect import cleandoc
from typing import TYPE_CHECKING

from litestar._openapi.parameters import create_parameters_for_handler
from litestar._openapi.request_body import create_request_body
from litestar._openapi.responses import create_responses_for_handler
from litestar._openapi.utils import SEPARATORS_CLEANUP_PATTERN
from litestar.enums import HttpMethod
from litestar.openapi.spec import Operation, PathItem
from litestar.utils.helpers import unwrap_partial

if TYPE_CHECKING:
    from litestar._openapi.datastructures import OpenAPIContext
    from litestar.handlers.http_handlers import HTTPRouteHandler
    from litestar.routes import HTTPRoute

__all__ = ("create_path_item_for_route",)


class PathItemFactory:
    """Factory for creating a PathItem instance for a given route."""

    def __init__(self, openapi_context: OpenAPIContext, route: HTTPRoute) -> None:
        self.context = openapi_context
        self.route = route
        self._path_item = PathItem()

    def create_path_item(self) -> PathItem:
        """Create a PathItem for the given route parsing all http_methods into Operation Models.

        Returns:
            A PathItem instance.
        """
        for http_method, handler_tuple in self.route.route_handler_map.items():
            route_handler, _ = handler_tuple

            if not route_handler.resolve_include_in_schema():
                continue

            operation = self.create_operation_for_handler_method(route_handler, HttpMethod(http_method))

            setattr(self._path_item, http_method.lower(), operation)

        return self._path_item

    def create_operation_for_handler_method(
        self, route_handler: HTTPRouteHandler, http_method: HttpMethod
    ) -> Operation:
        """Create an Operation instance for a given route handler and http method.

        Args:
            route_handler: A route handler instance.
            http_method: An HttpMethod enum value.

        Returns:
            An Operation instance.
        """
        operation_id = self.create_operation_id(route_handler, http_method)
        parameters = create_parameters_for_handler(self.context, route_handler, self.route.path_parameters)
        signature_fields = route_handler.parsed_fn_signature.parameters

        request_body = None
        if data_field := signature_fields.get("data"):
            request_body = create_request_body(
                self.context, route_handler.handler_id, route_handler.resolve_data_dto(), data_field
            )

        raises_validation_error = bool(data_field or self._path_item.parameters or parameters)
        responses = create_responses_for_handler(
            self.context, route_handler, raises_validation_error=raises_validation_error
        )

        return route_handler.operation_class(
            operation_id=operation_id,
            tags=route_handler.resolve_tags() or None,
            summary=route_handler.summary or SEPARATORS_CLEANUP_PATTERN.sub("", route_handler.handler_name.title()),
            description=self.create_description_for_handler(route_handler),
            deprecated=route_handler.deprecated,
            responses=responses,
            request_body=request_body,
            parameters=parameters or None,  # type: ignore[arg-type]
            security=route_handler.resolve_security() or None,
        )

    def create_operation_id(self, route_handler: HTTPRouteHandler, http_method: HttpMethod) -> str:
        """Create an operation id for a given route handler and http method.

        Adds the operation id to the context's operation id set, where it is checked for uniqueness.

        Args:
            route_handler: A route handler instance.
            http_method: An HttpMethod enum value.

        Returns:
            An operation id string.
        """
        if isinstance(route_handler.operation_id, str):
            operation_id = route_handler.operation_id
        elif callable(route_handler.operation_id):
            operation_id = route_handler.operation_id(route_handler, http_method, self.route.path_components)
        else:
            operation_id = self.context.openapi_config.operation_id_creator(
                route_handler, http_method, self.route.path_components
            )
        self.context.add_operation_id(operation_id)
        return operation_id

    def create_description_for_handler(self, route_handler: HTTPRouteHandler) -> str | None:
        """Produce the operation description for a route handler.

        Args:
            route_handler: A route handler instance.

        Returns:
            An optional description string
        """
        handler_description = route_handler.description
        if handler_description is None and self.context.openapi_config.use_handler_docstrings:
            fn = unwrap_partial(route_handler.fn)
            return cleandoc(fn.__doc__) if fn.__doc__ else None
        return handler_description


def create_path_item_for_route(openapi_context: OpenAPIContext, route: HTTPRoute) -> PathItem:
    """Create a PathItem for the given route parsing all http_methods into Operation Models.

    Args:
        openapi_context: The OpenAPIContext instance.
        route: The route to create a PathItem for.

    Returns:
        A PathItem instance.
    """
    path_item_factory = PathItemFactory(openapi_context, route)
    return path_item_factory.create_path_item()