2# +==== BEGIN CatFeeder =================+
5# ...............)..(.')
7# ...............\(__)|
8# Inspired by Joan Stark
9# source https://www.asciiart.eu/
13# FILE: docs_handler.py
14# CREATION DATE: 26-11-2025
15# LAST Modified: 2:9:10 24-01-2026
17# This is the backend server in charge of making the actual website work.
19# COPYRIGHT: (c) Cat Feeder
20# PURPOSE: The file containing the class in charge of injecting the documentation handler desired by the user.
22# +==== END CatFeeder =================+
24from typing
import Optional, Dict, Any
25from fastapi
import FastAPI, Request, Response
26from fastapi.openapi.utils
import get_openapi
27from display_tty
import Disp, initialise_logger
28from .
import docs_constants
as DOCS_CONST
29from .swagger
import SwaggerHandler
30from .redoc
import RedocHandler
31from .rapidoc
import RapiDocProvider
32from .scalar
import ScalarProvider
33from .elements
import StoplightElementsProvider
34from .editor
import SwaggerEditorProvider
35from .explorer
import OpenAPIExplorerProvider
36from .rapipdf
import RapiPDFProvider
37from ..core
import FinalClass, RuntimeControl
38from ..http_codes
import HCI, HttpDataTypes
39from ..core.runtime_manager
import RuntimeManager, RI
40from ..server_header
import ServerHeaders
41from ..path_manager
import PathManager
42from ..boilerplates
import BoilerplateResponses, BoilerplateIncoming
46 """Unified documentation handler for managing multiple API documentation providers.
48 This class provides a centralized interface for enabling and managing different
49 API documentation providers (Swagger UI, ReDoc, RapiDoc, Scalar, etc.). It handles
50 the registration of documentation endpoints and the serving of OpenAPI schemas.
53 disp (Disp): Logger instance for this class.
54 debug (bool): Debug mode flag.
55 success (int): Success return code.
56 error (int): Error return code.
57 runtime_manager (RuntimeManager): Shared runtime manager instance.
58 enabled_providers (tuple): Tuple of enabled documentation providers.
59 providers (Dict): Dictionary mapping provider names to their instances.
62 disp: Disp = initialise_logger(__qualname__,
False)
66 providers: Optional[tuple[DOCS_CONST.DocumentationProvider, ...]] =
None,
67 openapi_url: str = DOCS_CONST.OPENAPI_URL,
68 api_title: str = DOCS_CONST.OPENAPI_TITLE,
69 api_version: str = DOCS_CONST.OPENAPI_VERSION,
70 api_description: str = DOCS_CONST.OPENAPI_DESCRIPTION,
75 """Initialize the DocumentationHandler.
78 providers (Optional[tuple], optional): Tuple of documentation providers to enable.
79 Defaults to DOCS_CONST.DEFAULT_PROVIDERS.
80 openapi_url (str, optional): URL path for OpenAPI JSON schema.
81 Defaults to DOCS_CONST.OPENAPI_URL.
82 api_title (str, optional): API title for documentation.
83 Defaults to DOCS_CONST.OPENAPI_TITLE.
84 api_version (str, optional): API version string.
85 Defaults to DOCS_CONST.OPENAPI_VERSION.
86 api_description (str, optional): API description for documentation.
87 Defaults to DOCS_CONST.OPENAPI_DESCRIPTION.
88 success (int, optional): Success return code. Defaults to 0.
89 error (int, optional): Error return code. Defaults to 84.
90 debug (bool, optional): Enable debug logging. Defaults to False.
93 self.
disp.update_disp_debug(debug)
94 self.
disp.log_debug(
"Initialising...")
109 BoilerplateResponses)
118 if providers
is None:
132 DOCS_CONST.DocumentationProvider.REDOC:
lambda:
RedocHandler(
170 self.
disp.log_debug(
"Initialised")
173 """Initialize the enabled documentation providers.
175 Creates instances of the selected providers and stores them in the providers dictionary.
177 func_title =
"_initialize_providers"
179 "Initializing documentation providers...", func_title)
186 f
"Initialized {provider.value} provider", func_title
190 f
"Initialized {len(self.providers)} documentation provider(s)", func_title
194 """Generate custom OpenAPI schema with metadata.
197 app (Optional[FastAPI]): The FastAPI application instance.
200 Dict[str, Any]: The custom OpenAPI schema.
202 func_title =
"_get_custom_openapi_schema"
205 self.
disp.log_error(
"FastAPI app is None", func_title)
208 if app.openapi_schema:
209 self.
disp.log_debug(
"Returning cached OpenAPI schema", func_title)
210 return app.openapi_schema
212 self.
disp.log_debug(
"Generating custom OpenAPI schema", func_title)
214 openapi_schema = get_openapi(
221 openapi_schema[
"info"][
"x-logo"] = {
222 "url":
"/static/logo.png"
225 if DOCS_CONST.ENABLE_OAUTH2_DOCS
and DOCS_CONST.OAUTH2_AUTHORIZATION_URL
and DOCS_CONST.OAUTH2_TOKEN_URL:
226 openapi_schema[
"components"] = openapi_schema.get(
"components", {})
227 openapi_schema[
"components"][
"securitySchemes"] = {
231 "authorizationCode": {
232 "authorizationUrl": DOCS_CONST.OAUTH2_AUTHORIZATION_URL,
233 "tokenUrl": DOCS_CONST.OAUTH2_TOKEN_URL,
234 "scopes": DOCS_CONST.OAUTH2_SCOPES
241 "bearerFormat":
"JWT"
245 "Added OAuth2 security scheme to OpenAPI schema", func_title)
247 app.openapi_schema = openapi_schema
248 self.
disp.log_debug(
"OpenAPI schema generated and cached", func_title)
249 return app.openapi_schema
252 """Wrapper for custom OpenAPI endpoint.
255 request (Request): The incoming request.
258 Response: JSON response containing the OpenAPI schema.
260 func_title =
"_custom_openapi_wrapper"
261 self.
disp.log_debug(
"Serving OpenAPI schema", func_title)
265 self.
disp.log_debug(f
"token = {token}", func_title)
270 return HCI.success(content=openapi_schema, content_type=HttpDataTypes.JSON)
273 """Handle OAuth2 redirect for Swagger UI authentication.
275 This endpoint is called by OAuth2 providers after user authentication.
276 It extracts the authorization code/token and passes it back to Swagger UI.
279 request (Request): The incoming request with OAuth2 callback parameters.
282 Response: HTML response that passes credentials back to Swagger UI.
284 func_title =
"_oauth2_redirect_handler"
285 self.
disp.log_debug(
"Handling OAuth2 redirect", func_title)
289 self.
disp.log_debug(f
"token = {token}", func_title)
295 <meta charset="UTF-8">
296 <title>OAuth2 Redirect</title>
300 // This script passes the OAuth2 response back to Swagger UI
303 var oauth2 = window.opener.swaggerUIRedirectOauth2;
304 var sentState = oauth2.state;
305 var redirectUrl = oauth2.redirectUrl;
306 var isValid, qp, arr;
308 if (/code|token|error/.test(window.location.hash)) {
309 qp = window.location.hash.substring(1);
311 qp = location.search.substring(1);
315 arr.forEach(function (v, i, _arr) {
316 var _arr2 = v.split("=");
317 if (_arr2[0] === "state") {
318 isValid = _arr2[1] === sentState;
322 if (oauth2.auth.schema.get("flow") === "accessCode" && !oauth2.auth.code) {
325 authId: oauth2.auth.name,
328 message: "Authorization may be unsafe, passed state was changed in server Passed state wasn't returned from auth server"
334 arr.forEach(function (v, i, _arr) {
335 var _arr3 = v.split("=");
336 if (_arr3[0] === "code") {
337 oauth2.auth.code = _arr3[1];
338 } else if (_arr3[0] === "error") {
339 oauth2.auth.error = _arr3[1];
345 if (oauth2.auth.code || oauth2.auth.error) {
350 window.addEventListener('DOMContentLoaded', function () {
357 return HCI.success(content=html_content, content_type=HttpDataTypes.HTML)
360 """Create an async handler function for a documentation provider.
363 provider_instance (Any): The provider instance to create a handler for.
364 provider_name (str): Name of the provider for unique operation ID.
367 Any: An async handler function with unique name.
369 async def handler(request: Request) -> Response:
370 return await provider_instance.get_documentation(request)
373 handler.__name__ = f
"{provider_name}_documentation_handler"
377 def inject(self, providers: Optional[tuple[DOCS_CONST.DocumentationProvider, ...]] =
None) -> int:
378 """Inject documentation endpoints into the FastAPI application.
380 Registers all enabled documentation providers and the OpenAPI schema endpoint.
383 providers (Optional[tuple], optional): Tuple of documentation providers to enable.
384 If None, uses the providers set in the constructor. Defaults to None.
387 int: Success or error code.
389 func_title =
"inject"
390 self.
disp.log_debug(
"Injecting documentation endpoints...", func_title)
392 if providers
is not None:
400 "Initialized providers during inject()", func_title
410 f
"Failed to register OpenAPI schema endpoint at {self.openapi_url}",
416 f
"Registered OpenAPI schema endpoint: {self.openapi_url}", func_title
419 if DOCS_CONST.ENABLE_OAUTH2_DOCS:
421 path=DOCS_CONST.OAUTH2_REDIRECT_URL,
427 f
"Failed to register OAuth2 redirect endpoint at {DOCS_CONST.OAUTH2_REDIRECT_URL}",
432 f
"Registered OAuth2 redirect endpoint: {DOCS_CONST.OAUTH2_REDIRECT_URL}", func_title)
435 if provider_name
in [DOCS_CONST.DocumentationProvider.SWAGGER.value, DOCS_CONST.DocumentationProvider.REDOC.value]:
436 inject_result = provider_instance.inject()
437 if inject_result != self.
success:
439 f
"Failed to inject {provider_name} endpoints",
444 f
"Injected {provider_name} endpoints via inject() method", func_title
446 elif provider_name
in [DOCS_CONST.DocumentationProvider.RAPIPDF.value]:
447 doc_url = provider_instance.get_url()
450 provider_instance, provider_name)
454 endpoint=handler_func,
459 f
"Failed to register {provider_name} endpoint at {doc_url}",
463 result = provider_instance.inject_js_ressource(
468 f
"Failed to register {provider_name} child javascript ressources"
472 f
"Registered {provider_name} endpoint: {doc_url}", func_title
475 doc_url = provider_instance.get_url()
478 provider_instance, provider_name)
482 endpoint=handler_func,
487 f
"Failed to register {provider_name} endpoint at {doc_url}",
493 f
"Registered {provider_name} endpoint: {doc_url}", func_title
497 f
"Successfully injected {len(self.providers)} documentation provider(s)", func_title
int inject(self, Optional[tuple[DOCS_CONST.DocumentationProvider,...]] providers=None)
ServerHeaders server_headers_initialised
RuntimeManager runtime_manager
None __init__(self, Optional[tuple[DOCS_CONST.DocumentationProvider,...]] providers=None, str openapi_url=DOCS_CONST.OPENAPI_URL, str api_title=DOCS_CONST.OPENAPI_TITLE, str api_version=DOCS_CONST.OPENAPI_VERSION, str api_description=DOCS_CONST.OPENAPI_DESCRIPTION, int success=0, int error=84, bool debug=False)
Response _oauth2_redirect_handler(self, Request request)
Any _create_provider_handler(self, Any provider_instance, str provider_name)
Response _custom_openapi_wrapper(self, Request request)
PathManager path_manager_initialised
BoilerplateIncoming boilerplate_incoming_initialised
RuntimeControl runtime_control_initialised
None _initialize_providers(self)
Dict[str, Any] _get_custom_openapi_schema(self, Optional["FastAPI"] app)
BoilerplateResponses boilerplate_responses_initialised