Coverage for fastapi_docx/openapi.py: 96%

23 statements  

« prev     ^ index     » next       coverage.py v7.6.0, created at 2024-07-24 20:22 +0000

1from collections.abc import Callable 

2from typing import Any 

3 

4from fastapi import FastAPI 

5from fastapi.openapi.utils import get_openapi 

6 

7from fastapi_docx.exception_finder import ErrType, RouteExcFinder 

8from fastapi_docx.response_generator import ( 

9 ErrSchema, 

10 HTTPExceptionSchema, 

11 add_model_to_openapi, 

12 write_response, 

13) 

14 

15 

16def custom_openapi( 

17 app: FastAPI, 

18 customError: type[ErrType] | None = None, 

19 customErrSchema: type[ErrSchema] | None = None, 

20 HTTPExcSchema: type[ErrSchema] = HTTPExceptionSchema, 

21 dependencyClasses: tuple[type] | None = None, 

22 serviceClasses: tuple[type] | None = None, 

23) -> Callable: 

24 """Modify the OpenAPI specification for a FastAPI app to include any `HTTPException` raised in service classes and/or dependency classes. 

25 

26 Optionally include custom exceptions and their schemas in the OpenAPI specification. 

27 

28 Optionally include service classes and/or dependency classes. 

29 Specified service and dependency classes will be scanned for both HTTPExceptions and custom exceptions. 

30 

31 Parameters: 

32 `app`: The FastAPI app for which to generate the OpenAPI specification. 

33 `customError`: A custom exception class that should be included in the OpenAPI specification. 

34 All subclasses of the `customError` will be found and added to the spec. 

35 Note: any custom error should be able to be instantiated without required arguments. 

36 If paramaterizing your custom exceptions, ensure they use default kwargs. 

37 `customErrSchema`: The schema for the `customError` exception class. 

38 `HTTPExcSchema`: The schema for fastAPI/starlette HTTPExceptions raised by the app. 

39 Defaults to one field: `detail: Optional[str]` 

40 `dependencyClasses`: A tuple of classes that represent dependencies for the app's routes. 

41 You can subclass all dependencies and pass only the base e.g. `dependencyClasses=(BaseDependency,)` 

42 `serviceClasses`: A tuple of classes that represent service classes for the app's routes. 

43 You can subclass all services and pass only the base e.g. `serviceClasses=(BaseService,)` 

44 Returns: 

45 A callable that returns the modified OpenAPI specification as a dictionary or else None. 

46 """ 

47 

48 def _custom_openapi() -> Any: 

49 if app.openapi_schema: 

50 return app.openapi_schema 

51 openapi_schema = get_openapi( 

52 title=app.title, 

53 version=app.version, 

54 description=app.description, 

55 routes=app.routes, 

56 ) 

57 add_model_to_openapi(openapi_schema, HTTPExcSchema) 

58 if customErrSchema: 

59 add_model_to_openapi(openapi_schema, customErrSchema) 

60 finder = RouteExcFinder(customError, dependencyClasses, serviceClasses) 

61 for route in app.routes: 

62 if getattr(route, "include_in_schema", None): 

63 for exception in finder.extract_exceptions(route): 

64 write_response( 

65 openapi_schema, 

66 route, 

67 exception, 

68 customError, 

69 customErrSchema, 

70 ) 

71 finder.clear() 

72 app.openapi_schema = openapi_schema 

73 return app.openapi_schema 

74 

75 return _custom_openapi