summaryrefslogtreecommitdiff
path: root/venv/lib/python3.11/site-packages/litestar/openapi/config.py
blob: c935693696da6c1e2a6d61cffd71e5432591daef (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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
from __future__ import annotations

from copy import deepcopy
from dataclasses import dataclass, field, fields
from typing import TYPE_CHECKING, Literal

from litestar._openapi.utils import default_operation_id_creator
from litestar.openapi.controller import OpenAPIController
from litestar.openapi.spec import (
    Components,
    Contact,
    ExternalDocumentation,
    Info,
    License,
    OpenAPI,
    PathItem,
    Reference,
    SecurityRequirement,
    Server,
    Tag,
)
from litestar.utils.path import normalize_path

__all__ = ("OpenAPIConfig",)


if TYPE_CHECKING:
    from litestar.types.callable_types import OperationIDCreator


@dataclass
class OpenAPIConfig:
    """Configuration for OpenAPI.

    To enable OpenAPI schema generation and serving, pass an instance of this class to the
    :class:`Litestar <.app.Litestar>` constructor using the ``openapi_config`` kwargs.
    """

    title: str
    """Title of API documentation."""
    version: str
    """API version, e.g. '1.0.0'."""

    create_examples: bool = field(default=False)
    """Generate examples using the polyfactory library."""
    random_seed: int = 10
    """The random seed used when creating the examples to ensure deterministic generation of examples."""
    openapi_controller: type[OpenAPIController] = field(default_factory=lambda: OpenAPIController)
    """Controller for generating OpenAPI routes.

    Must be subclass of :class:`OpenAPIController <litestar.openapi.controller.OpenAPIController>`.
    """
    contact: Contact | None = field(default=None)
    """API contact information, should be an :class:`Contact <litestar.openapi.spec.contact.Contact>` instance."""
    description: str | None = field(default=None)
    """API description."""
    external_docs: ExternalDocumentation | None = field(default=None)
    """Links to external documentation.

    Should be an instance of :class:`ExternalDocumentation <litestar.openapi.spec.external_documentation.ExternalDocumentation>`.
    """
    license: License | None = field(default=None)
    """API Licensing information.

    Should be an instance of :class:`License <litestar.openapi.spec.license.License>`.
    """
    security: list[SecurityRequirement] | None = field(default=None)
    """API Security requirements information.

    Should be an instance of
        :data:`SecurityRequirement <.openapi.spec.SecurityRequirement>`.
    """
    components: Components | list[Components] = field(default_factory=Components)
    """API Components information.

    Should be an instance of :class:`Components <litestar.openapi.spec.components.Components>` or a list thereof.
    """
    servers: list[Server] = field(default_factory=lambda: [Server(url="/")])
    """A list of :class:`Server <litestar.openapi.spec.server.Server>` instances."""
    summary: str | None = field(default=None)
    """A summary text."""
    tags: list[Tag] | None = field(default=None)
    """A list of :class:`Tag <litestar.openapi.spec.tag.Tag>` instances."""
    terms_of_service: str | None = field(default=None)
    """URL to page that contains terms of service."""
    use_handler_docstrings: bool = field(default=False)
    """Draw operation description from route handler docstring if not otherwise provided."""
    webhooks: dict[str, PathItem | Reference] | None = field(default=None)
    """A mapping of key to either :class:`PathItem <litestar.openapi.spec.path_item.PathItem>` or.

    :class:`Reference <litestar.openapi.spec.reference.Reference>` objects.
    """
    root_schema_site: Literal["redoc", "swagger", "elements", "rapidoc"] = "redoc"
    """The static schema generator to use for the "root" path of `/schema/`."""
    enabled_endpoints: set[str] = field(
        default_factory=lambda: {
            "redoc",
            "swagger",
            "elements",
            "rapidoc",
            "openapi.json",
            "openapi.yaml",
            "openapi.yml",
            "oauth2-redirect.html",
        }
    )
    """A set of the enabled documentation sites and schema download endpoints."""
    operation_id_creator: OperationIDCreator = default_operation_id_creator
    """A callable that generates unique operation ids"""
    path: str | None = field(default=None)
    """Base path for the OpenAPI documentation endpoints."""

    def __post_init__(self) -> None:
        if self.path:
            self.path = normalize_path(self.path)
            self.openapi_controller = type("OpenAPIController", (self.openapi_controller,), {"path": self.path})

    def to_openapi_schema(self) -> OpenAPI:
        """Return an ``OpenAPI`` instance from the values stored in ``self``.

        Returns:
            An instance of :class:`OpenAPI <litestar.openapi.spec.open_api.OpenAPI>`.
        """

        if isinstance(self.components, list):
            merged_components = Components()
            for components in self.components:
                for key in (f.name for f in fields(components)):
                    if value := getattr(components, key, None):
                        merged_value_dict = getattr(merged_components, key, {}) or {}
                        merged_value_dict.update(value)
                        setattr(merged_components, key, merged_value_dict)

            self.components = merged_components

        return OpenAPI(
            external_docs=self.external_docs,
            security=self.security,
            components=deepcopy(self.components),  # deepcopy prevents mutation of the config's components
            servers=self.servers,
            tags=self.tags,
            webhooks=self.webhooks,
            info=Info(
                title=self.title,
                version=self.version,
                description=self.description,
                contact=self.contact,
                license=self.license,
                summary=self.summary,
                terms_of_service=self.terms_of_service,
            ),
            paths={},
        )