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

1# Copyright © Michal Čihař <michal@weblate.org> 

2# 

3# SPDX-License-Identifier: GPL-3.0-or-later 

4 

5from __future__ import annotations 

6 

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 

15 

16from .middleware import ( 

17 RATELIMIT_LIMIT_HEADER, 

18 RATELIMIT_REMAINING_HEADER, 

19 RATELIMIT_RESET_HEADER, 

20) 

21 

22 

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 ) 

38 

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"] 

43 

44 return parameter 

45 

46 

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 ) 

67 

68 

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) 

84 

85 

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) 

91 

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 

97 

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 ) 

110 

111 result["components"] = generator.registry.build( 

112 spectacular_settings.APPEND_COMPONENTS 

113 ) 

114 

115 return result