Cat Feeder  1.0.0
The Cat feeder project
Loading...
Searching...
No Matches
redoc_class.py
Go to the documentation of this file.
1r"""
2# +==== BEGIN CatFeeder =================+
3# LOGO:
4# ..............(..../\
5# ...............)..(.')
6# ..............(../..)
7# ...............\‍(__)|
8# Inspired by Joan Stark
9# source https://www.asciiart.eu/
10# animals/cats
11# /STOP
12# PROJECT: CatFeeder
13# FILE: redoc_class.py
14# CREATION DATE: 26-11-2025
15# LAST Modified: 22:22:17 11-01-2026
16# DESCRIPTION:
17# This is the backend server in charge of making the actual website work.
18# /STOP
19# COPYRIGHT: (c) Cat Feeder
20# PURPOSE: The class in charge of handling the redoc instance.
21# // AR
22# +==== END CatFeeder =================+
23"""
24from typing import Optional, Dict, Any, TYPE_CHECKING
25from fastapi import FastAPI, Request, Response
26from fastapi.openapi.docs import get_redoc_html
27from fastapi.openapi.utils import get_openapi
28from display_tty import Disp, initialise_logger
29from . import redoc_constants as REDOC_CONST
30from ...core import FinalClass, RuntimeControl
31from ...http_codes import HCI, HTTP_DEFAULT_TYPE
32from ...core.runtime_manager import RuntimeManager, RI
33from ...server_header import ServerHeaders
34from ...path_manager import PathManager
35from ...boilerplates import BoilerplateResponses, BoilerplateIncoming
36
37if TYPE_CHECKING:
38 from fastapi import Request, Response
39
40
41class RedocHandler(metaclass=FinalClass):
42 """Handler for Swagger/OpenAPI documentation integration.
43
44 This class manages the configuration and injection of Swagger UI and ReDoc
45 documentation interfaces into the FastAPI application. It provides endpoints
46 for accessing interactive API documentation and the OpenAPI schema.
47
48 Attributes:
49 disp (Disp): Logger instance for this class.
50 debug (bool): Debug mode flag.
51 success (int): Success return code.
52 error (int): Error return code.
53 runtime_manager (RuntimeManager): Shared runtime manager instance.
54 path_manager_initialised (PathManager): Path manager for registering endpoints.
55 runtime_control_initialised (RuntimeControl): Runtime control for accessing app instance.
56 server_headers_initialised (ServerHeaders): Server header configuration.
57 boilerplate_responses_initialised (BoilerplateResponses): Response templates.
58 boilerplate_incoming_initialised (BoilerplateIncoming): Request handling utilities.
59 """
60
61 disp: Disp = initialise_logger(__qualname__, False)
62
63 def __init__(self, success: int = 0, error: int = 84, debug: bool = False) -> None:
64 """Initialize the SwaggerHandler.
65
66 Args:
67 success (int, optional): Success return code. Defaults to 0.
68 error (int, optional): Error return code. Defaults to 84.
69 debug (bool, optional): Enable debug logging. Defaults to False.
70 """
71 # ------------------------ The logging function ------------------------
72 self.disp.update_disp_debug(debug)
73 self.disp.log_debug("Initialising...")
74 # -------------------------- Inherited values --------------------------
75 self.debug: bool = debug
76 self.success: int = success
77 self.error: int = error
78 self.runtime_manager: RuntimeManager = RI
79 # -------------------------- Shared instances --------------------------
80 self.path_manager_initialised: PathManager = self.runtime_manager.get(
81 PathManager,
82 True,
83 **{"success": self.success, "error": self.error, "debug": self.debug}
84 )
85 self.runtime_control_initialised: RuntimeControl = self.runtime_manager.get(
86 RuntimeControl)
87 self.server_headers_initialised: ServerHeaders = self.runtime_manager.get(
88 ServerHeaders)
89 self.boilerplate_responses_initialised: BoilerplateResponses = self.runtime_manager.get(
90 BoilerplateResponses)
91 self.boilerplate_incoming_initialised: BoilerplateIncoming = self.runtime_manager.get(
92 BoilerplateIncoming)
93 self.disp.log_debug("Initialised")
94
95 def _get_custom_openapi_schema(self, app: "FastAPI") -> Dict[str, Any]:
96 """Generate custom OpenAPI schema with metadata.
97
98 Args:
99 app (FastAPI): The FastAPI application instance.
100
101 Returns:
102 Dict[str, Any]: The OpenAPI schema dictionary.
103 """
104 func_title = "_get_custom_openapi_schema"
105 self.disp.log_debug("Generating custom OpenAPI schema", func_title)
106
107 if app.openapi_schema:
108 self.disp.log_debug("Returning cached OpenAPI schema", func_title)
109 return app.openapi_schema
110
111 openapi_schema = get_openapi(
112 title=REDOC_CONST.API_TITLE,
113 version=REDOC_CONST.API_VERSION,
114 description=REDOC_CONST.API_DESCRIPTION,
115 routes=app.routes,
116 tags=REDOC_CONST.TAGS_METADATA,
117 servers=REDOC_CONST.SERVERS,
118 )
119
120 openapi_schema["info"]["contact"] = REDOC_CONST.CONTACT_INFO
121 openapi_schema["info"]["license"] = REDOC_CONST.LICENSE_INFO
122
123 app.openapi_schema = openapi_schema
124 self.disp.log_debug("OpenAPI schema generated and cached", func_title)
125 return app.openapi_schema
126
127 def _custom_openapi_wrapper(self, app: "FastAPI") -> Dict[str, Any]:
128 """Wrapper method for app.openapi() that uses the custom schema generator.
129
130 This method serves as the openapi() callable for the FastAPI app instance.
131
132 Args:
133 app (FastAPI): The FastAPI application instance.
134
135 Returns:
136 Dict[str, Any]: The OpenAPI schema dictionary.
137 """
138 return self._get_custom_openapi_schema(app)
139
140 async def get_redoc_documentation(self, request: Request) -> Response:
141 """Endpoint to serve ReDoc documentation.
142
143 Args:
144 request (Request): The incoming request object.
145
146 Returns:
147 Response: HTML response with ReDoc interface.
148 """
149 func_title = "get_redoc_documentation"
150 self.disp.log_debug("Serving ReDoc", func_title)
151
152 token = self.boilerplate_incoming_initialised.get_token_if_present(
153 request)
154 self.disp.log_debug(f"token = {token}", func_title)
155
156 if not self.runtime_control_initialised.app:
157 error_body = self.boilerplate_responses_initialised.build_response_body(
158 title="ReDoc",
159 message="Application not initialized",
160 resp="App instance not found",
161 token=token,
162 error=True
163 )
164 return HCI.service_unavailable(
165 content=error_body,
166 content_type=HTTP_DEFAULT_TYPE,
167 headers=self.server_headers_initialised.for_json()
168 )
169
170 return get_redoc_html(
171 openapi_url=REDOC_CONST.OPENAPI_URL,
172 title=f"{REDOC_CONST.API_TITLE} - ReDoc",
173 )
174
175 async def get_openapi_schema(self, request: Request) -> Response:
176 """Endpoint to serve the OpenAPI JSON schema.
177
178 Args:
179 request (Request): The incoming request object.
180
181 Returns:
182 Response: JSON response with OpenAPI schema.
183 """
184 func_title = "get_openapi_schema"
185 self.disp.log_debug("Serving OpenAPI schema", func_title)
186
187 token = self.boilerplate_incoming_initialised.get_token_if_present(
188 request)
189 self.disp.log_debug(f"token = {token}", func_title)
190
191 if not self.runtime_control_initialised.app:
192 error_body = self.boilerplate_responses_initialised.build_response_body(
193 title="OpenAPI Schema",
194 message="Application not initialized",
195 resp="App instance not found",
196 token=token,
197 error=True
198 )
199 return HCI.service_unavailable(
200 content=error_body,
201 content_type=HTTP_DEFAULT_TYPE,
202 headers=self.server_headers_initialised.for_json()
203 )
204
205 openapi_schema = self._get_custom_openapi_schema(
207 )
208
209 return HCI.success(
210 content=openapi_schema,
211 content_type=HTTP_DEFAULT_TYPE,
212 headers=self.server_headers_initialised.for_json()
213 )
214
215 def inject(self, app: Optional["FastAPI"] = None) -> int:
216 """Inject Swagger/OpenAPI configuration into the FastAPI application.
217
218 This method configures the FastAPI application with custom OpenAPI documentation
219 settings and registers the documentation endpoints (Swagger UI, ReDoc, OpenAPI schema).
220
221 Args:
222 app (Optional[FastAPI], optional): The FastAPI application instance.
223 If None, uses the instance from RuntimeControl. Defaults to None.
224
225 Returns:
226 int: success if injection succeeded, error if there was an error.
227
228 Raises:
229 RuntimeError: If no FastAPI application instance is available.
230 """
231 func_title = "inject"
232 self.disp.log_debug("Starting Swagger injection", func_title)
233
234 if app is None:
235 app = self.runtime_control_initialised.app
236
237 if not app:
238 self.disp.log_error(
239 "No FastAPI app instance available", func_title)
240 raise RuntimeError("FastAPI application instance not found")
241
242 if not isinstance(app, FastAPI):
243 self.disp.log_error(
244 f"Invalid app type: {type(app)}, expected FastAPI",
245 func_title
246 )
247 return self.error
248
249 self.disp.log_debug("Configuring FastAPI OpenAPI settings", func_title)
250
251 app.docs_url = None
252 app.redoc_url = None
253 app.openapi_url = None
254
255 app.title = REDOC_CONST.API_TITLE
256 app.version = REDOC_CONST.API_VERSION
257 app.description = REDOC_CONST.API_DESCRIPTION
258 app.openapi_tags = REDOC_CONST.TAGS_METADATA
259 app.contact = REDOC_CONST.CONTACT_INFO
260 app.license_info = REDOC_CONST.LICENSE_INFO
261 app.servers = REDOC_CONST.SERVERS
262
263 self.disp.log_debug("Registering documentation endpoints", func_title)
264
265 result = self.path_manager_initialised.add_path(
266 path=REDOC_CONST.REDOC_URL,
267 endpoint=self.get_redoc_documentation,
268 method="GET"
269 )
270 if result != self.success:
271 self.disp.log_error(
272 f"Failed to register ReDoc endpoint at {REDOC_CONST.REDOC_URL}",
273 func_title
274 )
275 return self.error
276
277 def custom_openapi() -> Dict[str, Any]:
278 return self._custom_openapi_wrapper(app)
279
280 app.openapi = custom_openapi
281
282 self.disp.log_info(
283 "ReDoc/OpenAPI injection completed successfully", func_title)
284 self.disp.log_info(
285 f"ReDoc available at: {REDOC_CONST.REDOC_URL}", func_title)
286 self.disp.log_info(
287 f"OpenAPI schema available at: {REDOC_CONST.OPENAPI_URL}", func_title)
288
289 return self.success
int inject(self, Optional["FastAPI"] app=None)
Response get_openapi_schema(self, Request request)
Dict[str, Any] _custom_openapi_wrapper(self, "FastAPI" app)
None __init__(self, int success=0, int error=84, bool debug=False)
Dict[str, Any] _get_custom_openapi_schema(self, "FastAPI" app)
Response get_redoc_documentation(self, Request request)