Coverage for app/venv/lib/python3.14/site-packages/weblate/api/docs.py: 95%
32 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 07:15 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 07:15 +0000
1# Copyright © Michal Čihař <michal@weblate.org>
2#
3# SPDX-License-Identifier: GPL-3.0-or-later
5from __future__ import annotations
7from django.utils.translation import gettext
8from drf_spectacular.plumbing import (
9 ResolvedComponent,
10 build_basic_type,
11 build_parameter_type,
12)
13from drf_spectacular.settings import spectacular_settings
14from drf_spectacular.utils import OpenApiParameter
16from .middleware import (
17 RATELIMIT_LIMIT_HEADER,
18 RATELIMIT_REMAINING_HEADER,
19 RATELIMIT_RESET_HEADER,
20)
23def build_response_header_parameter(
24 name: str,
25 description: str,
26 schema_type: type = str,
27 required: bool = True,
28 **kwargs,
29):
30 parameter = build_parameter_type(
31 name=name,
32 schema=build_basic_type(schema_type),
33 location=OpenApiParameter.HEADER,
34 description=description,
35 required=required,
36 **kwargs,
37 )
39 # following drf_spectacular.openapi.AutoSchema._get_response_headers_for_code, this is
40 # not present in header objects
41 del parameter["in"]
42 del parameter["name"]
44 return parameter
47def build_response_header_component(
48 name: str,
49 description: str,
50 schema_type: type = str,
51 required: bool = True,
52 **kwargs,
53) -> ResolvedComponent:
54 parameter = build_response_header_parameter(
55 name=name,
56 description=description,
57 schema_type=schema_type,
58 required=required,
59 **kwargs,
60 )
61 return ResolvedComponent(
62 name=name,
63 type=ResolvedComponent.HEADER,
64 schema=parameter,
65 object=name,
66 )
69RATELIMIT_LIMIT_COMPONENT = build_response_header_component(
70 name=RATELIMIT_LIMIT_HEADER,
71 schema_type=int,
72 description=gettext("Allowed number of requests to perform"),
73)
74RATELIMIT_REMAINING_COMPONENT = build_response_header_component(
75 name=RATELIMIT_REMAINING_HEADER,
76 schema_type=int,
77 description=gettext("Remaining number of requests to perform"),
78)
79RATELIMIT_RESET_COMPONENT = build_response_header_component(
80 name=RATELIMIT_RESET_HEADER,
81 schema_type=int,
82 description=gettext("Number of seconds until the rate-limit window resets"),
83)
86def add_middleware_headers(result, generator, request, public):
87 """Add headers to responses set by middleware."""
88 generator.registry.register_on_missing(RATELIMIT_LIMIT_COMPONENT)
89 generator.registry.register_on_missing(RATELIMIT_REMAINING_COMPONENT)
90 generator.registry.register_on_missing(RATELIMIT_RESET_COMPONENT)
92 for path in result["paths"].values():
93 for operation in path.values():
94 # the paths object may be extended with custom, non-standard extensions
95 if "responses" not in operation: # pragma: no cover 95 ↛ 96line 95 didn't jump to line 96 because the condition on line 95 was never true
96 continue
98 for code, response in operation["responses"].items():
99 if code != "200":
100 continue
101 # spec: https://swagger.io/specification/#response-object
102 response.setdefault("headers", {})
103 response["headers"].update(
104 {
105 RATELIMIT_LIMIT_HEADER: RATELIMIT_LIMIT_COMPONENT.ref,
106 RATELIMIT_REMAINING_HEADER: RATELIMIT_REMAINING_COMPONENT.ref,
107 RATELIMIT_RESET_HEADER: RATELIMIT_RESET_COMPONENT.ref,
108 }
109 )
111 result["components"] = generator.registry.build(
112 spectacular_settings.APPEND_COMPONENTS
113 )
115 return result