Coverage for app/venv/lib/python3.14/site-packages/weblate/api/spectacular.py: 82%

20 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# SPDX-FileCopyrightText: 2025 Javier Pérez <jdbp@protonmail.com> 

3# 

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

5 

6from __future__ import annotations 

7 

8import warnings 

9from typing import Any 

10 

11from django.utils.functional import lazy 

12from django.utils.translation import gettext_lazy 

13 

14 

15def get_doc_url_wrapper(page: str, anchor: str = "") -> str: 

16 """ 

17 Wrap get_doc_url to delay get_doc_url import. 

18 

19 It cannot be imported directly, because get_spectacular_settings is used 

20 from settings and get_doc_url needs settings to determine if it should hide t 

21 he version info. 

22 """ 

23 from weblate.utils.docs import get_doc_url 

24 

25 return get_doc_url(page, anchor) 

26 

27 

28def get_spectacular_settings( 

29 installed_apps: list[str], site_url: str, site_title: str 

30) -> dict[str, Any]: 

31 settings = { 

32 # Use redoc from sidecar 

33 # TODO: Should bundle it internally 

34 "REDOC_DIST": "SIDECAR", 

35 "REDOC_UI_SETTINGS": { 

36 "theme": { 

37 "typography": { 

38 "fontFamily": '"Source Sans 3", sans-serif', 

39 "headings": { 

40 "fontFamily": '"Source Sans 3", sans-serif', 

41 }, 

42 "code": { 

43 "fontFamily": '"Source Code Pro", monospace', 

44 }, 

45 }, 

46 "logo": { 

47 "maxWidth": "150px", 

48 "maxHeight": "50vh", 

49 "margin": "auto", 

50 }, 

51 }, 

52 }, 

53 "SWAGGER_UI_DIST": "SIDECAR", 

54 "DEFAULT_GENERATOR_CLASS": "weblate.api.generators.WeblateSchemaGenerator", 

55 # OpenAPI Specification version: 'webhooks' field is supported from 3.1.0 

56 "OAS_VERSION": "3.1.1", 

57 "SERVERS": [ 

58 {"url": site_url.rstrip("/"), "description": site_title}, 

59 ], 

60 "SERVE_URLCONF": "weblate.urls", 

61 "TITLE": gettext_lazy("Weblate's REST API"), 

62 "LICENSE": { 

63 "name": "GNU General Public License v3 or later", 

64 "url": lazy(get_doc_url_wrapper, str)("contributing/license"), 

65 }, 

66 "DESCRIPTION": """ 

67The API is accessible on the ``/api/`` URL and it is based on [Django REST framework](https://www.django-rest-framework.org/). 

68 

69The OpenAPI specification is available as feature preview, feedback welcome! 

70 

71## Authorization 

72 

73<!-- Redoc-Inject: <security-definitions> --> 

74 

75 

76 """, 

77 "EXTENSIONS_INFO": { 

78 "x-logo": { 

79 "url": "/static/weblate.svg", 

80 } 

81 }, 

82 # Do not use API versioning 

83 "VERSION": None, 

84 # Flatten enum definitions 

85 "ENUM_NAME_OVERRIDES": { 

86 "ColorEnum": "weblate.utils.colors.ColorChoices.choices", 

87 "ValidationErrorEnum": "drf_standardized_errors.openapi_serializers.ValidationErrorEnum.choices", 

88 "ClientErrorEnum": "drf_standardized_errors.openapi_serializers.ClientErrorEnum.choices", 

89 "ServerErrorEnum": "drf_standardized_errors.openapi_serializers.ServerErrorEnum.choices", 

90 "ErrorCode401Enum": "drf_standardized_errors.openapi_serializers.ErrorCode401Enum.choices", 

91 "ErrorCode403Enum": "drf_standardized_errors.openapi_serializers.ErrorCode403Enum.choices", 

92 "ErrorCode404Enum": "drf_standardized_errors.openapi_serializers.ErrorCode404Enum.choices", 

93 "ErrorCode405Enum": "drf_standardized_errors.openapi_serializers.ErrorCode405Enum.choices", 

94 "ErrorCode406Enum": "drf_standardized_errors.openapi_serializers.ErrorCode406Enum.choices", 

95 "ErrorCode415Enum": "drf_standardized_errors.openapi_serializers.ErrorCode415Enum.choices", 

96 "ErrorCode429Enum": "drf_standardized_errors.openapi_serializers.ErrorCode429Enum.choices", 

97 "ErrorCode500Enum": "drf_standardized_errors.openapi_serializers.ErrorCode500Enum.choices", 

98 "ErrorCode423Enum": "weblate.api.serializers.ErrorCode423Enum.choices", 

99 }, 

100 "POSTPROCESSING_HOOKS": [ 

101 "drf_standardized_errors.openapi_hooks.postprocess_schema_enums", 

102 "weblate.api.docs.add_middleware_headers", 

103 ], 

104 "EXTERNAL_DOCS": { 

105 "url": lazy(get_doc_url_wrapper, str)("index"), 

106 "description": "Official Weblate documentation", 

107 }, 

108 "TAGS": [ 

109 { 

110 "name": "root", 

111 "description": "The API root entry point.", 

112 }, 

113 { 

114 "name": "users", 

115 "description": "Added in version 4.0.", 

116 }, 

117 { 

118 "name": "groups", 

119 "description": "Added in version 4.0.", 

120 }, 

121 { 

122 "name": "roles", 

123 }, 

124 { 

125 "name": "languages", 

126 }, 

127 { 

128 "name": "projects", 

129 }, 

130 { 

131 "name": "components", 

132 }, 

133 { 

134 "name": "translations", 

135 }, 

136 { 

137 "name": "memory", 

138 "description": "Added in version 4.14.", 

139 }, 

140 { 

141 "name": "units", 

142 "description": "A unit is a single piece of a translation which pairs a source string with a corresponding translated string and also contains some related metadata. The term is derived from the Translate Toolkit and XLIFF.", 

143 }, 

144 { 

145 "name": "changes", 

146 }, 

147 { 

148 "name": "screenshots", 

149 }, 

150 { 

151 "name": "addons", 

152 "description": "Added in version 4.4.1.", 

153 }, 

154 { 

155 "name": "component-lists", 

156 "description": "Added in version 4.0.", 

157 }, 

158 { 

159 "name": "glossary", 

160 "description": "**Changed in version 4.5:** Glossaries are now stored as regular components, translations and strings, please use respective API instead.", 

161 }, 

162 { 

163 "name": "tasks", 

164 "description": "Added in version 4.4.\n\nListing of the tasks is currently not available.", 

165 }, 

166 { 

167 "name": "statistics", 

168 "description": "Many endpoints support displaying statistics for their objects.", 

169 }, 

170 { 

171 "name": "metrics", 

172 }, 

173 { 

174 "name": "search", 

175 "description": "Added in version 4.18.", 

176 }, 

177 { 

178 "name": "categories", 

179 }, 

180 { 

181 "name": "hooks", 

182 "description": """Notification hooks allow external applications to notify Weblate that the VCS repository has been updated.""" 

183 """\n\nYou can use repository endpoints for projects, components and translations to update individual repositories.""", 

184 }, 

185 { 

186 "name": "exports", 

187 "description": "Weblate provides various exports to allow you to further process the data.", 

188 }, 

189 { 

190 "name": "rssFeeds", 

191 "description": "Changes in translations are exported in RSS feeds.", 

192 }, 

193 { 

194 "name": "webhooks", 

195 "description": "Notifications sent by Weblate.", 

196 }, 

197 ], 

198 "WEBHOOKS": ["weblate.addons.webhooks.change_event_webhook"], 

199 } 

200 if "weblate.legal" in installed_apps: 200 ↛ 201line 200 didn't jump to line 201 because the condition on line 200 was never true

201 settings["TOS"] = "/legal/terms/" 

202 

203 return settings 

204 

205 

206def get_drf_standardized_errors_settings() -> dict[str, Any]: 

207 return { 

208 "ALLOWED_ERROR_STATUS_CODES": [ 

209 "400", 

210 "401", 

211 "403", 

212 "404", 

213 "405", 

214 "406", 

215 "415", 

216 "423", # Added by Weblate 

217 "429", 

218 "500", 

219 ], 

220 "ERROR_SCHEMAS": { 

221 "423": "weblate.api.serializers.ErrorResponse423Serializer", 

222 }, 

223 "EXCEPTION_HANDLER_CLASS": "weblate.api.views.WeblateExceptionHandler", 

224 } 

225 

226 

227# spellchecker:off 

228def get_drf_standardized_errors_sertings() -> dict[str, Any]: 

229 # TODO: Remove in Weblate 5.14 

230 warnings.warn( 

231 "The get_drf_standardized_errors_sertings was renamed to get_drf_standardized_errors_settings", 

232 DeprecationWarning, 

233 stacklevel=1, 

234 ) 

235 return get_drf_standardized_errors_settings() 

236 

237 

238# spellchecker:on 

239 

240 

241def get_drf_settings( 

242 *, require_login: bool, anon_throttle: str, user_throttle: str 

243) -> dict[str, Any]: 

244 return { 

245 # Use Django's standard `django.contrib.auth` permissions, 

246 # or allow read-only access for unauthenticated users. 

247 "DEFAULT_PERMISSION_CLASSES": [ 

248 # Require authentication for login required sites 

249 "rest_framework.permissions.IsAuthenticated" 

250 if require_login 

251 else "rest_framework.permissions.IsAuthenticatedOrReadOnly" 

252 ], 

253 "DEFAULT_AUTHENTICATION_CLASSES": ( 

254 "rest_framework.authentication.TokenAuthentication", 

255 "weblate.api.authentication.BearerAuthentication", 

256 "rest_framework.authentication.SessionAuthentication", 

257 ), 

258 "DEFAULT_THROTTLE_CLASSES": ( 

259 "weblate.api.throttling.UserRateThrottle", 

260 "weblate.api.throttling.AnonRateThrottle", 

261 ), 

262 "DEFAULT_THROTTLE_RATES": { 

263 "anon": anon_throttle, 

264 "user": user_throttle, 

265 }, 

266 "DEFAULT_RENDERER_CLASSES": [ 

267 "rest_framework.renderers.JSONRenderer", 

268 "rest_framework.renderers.BrowsableAPIRenderer", 

269 "weblate.api.renderers.AutoCSVRenderer", 

270 ], 

271 "DEFAULT_PAGINATION_CLASS": "weblate.api.pagination.StandardPagination", 

272 "PAGE_SIZE": 50, 

273 "VIEW_DESCRIPTION_FUNCTION": "weblate.api.views.get_view_description", 

274 "EXCEPTION_HANDLER": "drf_standardized_errors.handler.exception_handler", 

275 "UNAUTHENTICATED_USER": "weblate.auth.models.get_anonymous", 

276 "DEFAULT_SCHEMA_CLASS": "weblate.api.generators.WeblateAutoSchema", 

277 }