Coverage for an_website/endpoints/endpoints.py: 96.154%

26 statements  

« prev     ^ index     » next       coverage.py v7.6.4, created at 2024-11-16 19:56 +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/>. 

13 

14"""Show a list of all API endpoints.""" 

15 

16from __future__ import annotations 

17 

18from typing import ClassVar, cast 

19 

20import orjson as json 

21 

22from .. import ORJSON_OPTIONS 

23from ..utils.request_handler import APIRequestHandler, HTMLRequestHandler 

24from ..utils.utils import ModuleInfo, Permission, name_to_id 

25 

26 

27def get_module_info() -> ModuleInfo: 

28 """Create and return the ModuleInfo for this module.""" 

29 return ModuleInfo( 

30 handlers=( 

31 ("/endpunkte", Endpoints), 

32 ("/api/endpunkte", EndpointsAPI), 

33 ), 

34 name="API-Endpunkte", 

35 description="Alle API-Endpunkte unserer Webseite", 

36 path="/endpunkte", 

37 aliases={ 

38 "/endpoints": "/endpunkte", 

39 "/api": "/api/endpunkte", 

40 "/api/endpoints": "/api/endpunkte", 

41 }, 

42 keywords=("API", "Endpoints", "Endpunkte"), 

43 ) 

44 

45 

46class Endpoints(HTMLRequestHandler): 

47 """Endpoint page request handler.""" 

48 

49 async def get(self, *, head: bool = False) -> None: 

50 """Handle a GET request.""" 

51 if head: 

52 return 

53 return await self.render( 

54 "pages/endpoints.html", 

55 endpoints=self.get_endpoints(), 

56 name_to_id=name_to_id, 

57 ) 

58 

59 def get_endpoints( 

60 self, 

61 ) -> list[dict[str, str | list[dict[str, str | list[str]]]]]: 

62 """Get a list of all API endpoints and return it.""" 

63 endpoints: list[dict[str, str | list[dict[str, str | list[str]]]]] = [] 

64 for module_info in self.settings["MODULE_INFOS"]: 

65 api_paths: list[dict[str, str | list[str]]] = [ 

66 { 

67 "path": handler[0], 

68 "methods": methods, 

69 "content_types": list(handler[1].POSSIBLE_CONTENT_TYPES), 

70 } 

71 for handler in module_info.handlers 

72 if handler[0].startswith("/api/") 

73 if issubclass(handler[1], APIRequestHandler) 

74 if ( 

75 methods := [ 

76 method.upper() 

77 for method in handler[1].get_allowed_methods() 

78 if ( 

79 perm := getattr( 

80 getattr(handler[1], method.lower()), 

81 "required_perms", 

82 None, 

83 ) 

84 ) 

85 is None 

86 or self.is_authorized(cast(Permission, perm)) 

87 ] 

88 ) 

89 if methods != ["OPTIONS"] 

90 ] 

91 if len(api_paths) > 0: 

92 endpoints.append( 

93 { 

94 "name": module_info.name, 

95 "description": module_info.description, 

96 "endpoints": api_paths, 

97 } 

98 ) 

99 return endpoints 

100 

101 

102class EndpointsAPI(APIRequestHandler, Endpoints): 

103 """Show a list of all API endpoints.""" 

104 

105 POSSIBLE_CONTENT_TYPES: ClassVar[tuple[str, ...]] = ( 

106 APIRequestHandler.POSSIBLE_CONTENT_TYPES + ("application/x-ndjson",) 

107 ) 

108 

109 async def get(self, *, head: bool = False) -> None: 

110 """Handle a GET request.""" 

111 if self.content_type == "application/x-ndjson": 

112 await self.finish( 

113 b"\n".join( 

114 json.dumps(endpoint, option=ORJSON_OPTIONS) 

115 for endpoint in self.get_endpoints() 

116 ) 

117 ) 

118 else: 

119 await self.finish(self.dump(self.get_endpoints()))