Coverage for an_website / endpoints / endpoints.py: 96.000%
25 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-24 17:35 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-24 17:35 +0000
1# This program is free software: you can redistribute it and/or modify
2# it under the terms of the GNU Affero General Public License as
3# published by the Free Software Foundation, either version 3 of the
4# License, or (at your option) any later version.
5#
6# This program is distributed in the hope that it will be useful,
7# but WITHOUT ANY WARRANTY; without even the implied warranty of
8# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9# GNU Affero General Public License for more details.
10#
11# You should have received a copy of the GNU Affero General Public License
12# along with this program. If not, see <https://www.gnu.org/licenses/>.
14"""Show a list of all API endpoints."""
16from typing import ClassVar, cast
18import orjson as json
20from .. import ORJSON_OPTIONS
21from ..utils.request_handler import APIRequestHandler, HTMLRequestHandler
22from ..utils.utils import ModuleInfo, Permission, name_to_id
25def get_module_info() -> ModuleInfo:
26 """Create and return the ModuleInfo for this module."""
27 return ModuleInfo(
28 handlers=(
29 ("/endpunkte", Endpoints),
30 ("/api/endpunkte", EndpointsAPI),
31 ),
32 name="API-Endpunkte",
33 description="Alle API-Endpunkte unserer Webseite",
34 path="/endpunkte",
35 aliases={
36 "/endpoints": "/endpunkte",
37 "/api": "/api/endpunkte",
38 "/api/endpoints": "/api/endpunkte",
39 },
40 keywords=("API", "Endpoints", "Endpunkte"),
41 )
44class Endpoints(HTMLRequestHandler):
45 """Endpoint page request handler."""
47 async def get(self, *, head: bool = False) -> None:
48 """Handle a GET request."""
49 if head:
50 return
51 return await self.render(
52 "pages/endpoints.html",
53 endpoints=self.get_endpoints(),
54 name_to_id=name_to_id,
55 )
57 def get_endpoints(
58 self,
59 ) -> list[dict[str, str | list[dict[str, str | list[str]]]]]:
60 """Get a list of all API endpoints and return it."""
61 endpoints: list[dict[str, str | list[dict[str, str | list[str]]]]] = []
62 for module_info in self.settings["MODULE_INFOS"]:
63 api_paths: list[dict[str, str | list[str]]] = [
64 {
65 "path": handler[0],
66 "methods": methods,
67 "content_types": list(handler[1].POSSIBLE_CONTENT_TYPES),
68 }
69 for handler in module_info.handlers
70 if handler[0].startswith("/api/")
71 if issubclass(handler[1], APIRequestHandler)
72 if (
73 methods := [
74 method.upper()
75 for method in handler[1].get_allowed_methods()
76 if (
77 perm := getattr(
78 getattr(handler[1], method.lower()),
79 "required_perms",
80 None,
81 )
82 )
83 is None
84 or self.is_authorized(cast(Permission, perm))
85 ]
86 )
87 if methods != ["OPTIONS"]
88 ]
89 if len(api_paths) > 0:
90 endpoints.append(
91 {
92 "name": module_info.name,
93 "description": module_info.description,
94 "endpoints": api_paths,
95 }
96 )
97 return endpoints
100class EndpointsAPI(APIRequestHandler, Endpoints):
101 """Show a list of all API endpoints."""
103 POSSIBLE_CONTENT_TYPES: ClassVar[tuple[str, ...]] = (
104 APIRequestHandler.POSSIBLE_CONTENT_TYPES + ("application/x-ndjson",)
105 )
107 async def get(self, *, head: bool = False) -> None:
108 """Handle a GET request."""
109 if self.content_type == "application/x-ndjson":
110 await self.finish(
111 b"\n".join(
112 json.dumps(endpoint, option=ORJSON_OPTIONS)
113 for endpoint in self.get_endpoints()
114 )
115 )
116 else:
117 await self.finish(self.dump(self.get_endpoints()))