manager.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. from apispec import APISpec
  2. from apispec.ext.marshmallow import MarshmallowPlugin
  3. from apispec.ext.marshmallow.common import resolve_schema_cls
  4. from flask import current_app, request
  5. from flask_appbuilder.api import BaseApi
  6. from flask_appbuilder.api import expose, protect, safe
  7. from flask_appbuilder.basemanager import BaseManager
  8. from flask_appbuilder.baseviews import BaseView
  9. from flask_appbuilder.security.decorators import has_access
  10. def resolver(schema):
  11. schema_cls = resolve_schema_cls(schema)
  12. name = schema_cls.__name__
  13. if name == "MetaSchema":
  14. if hasattr(schema_cls, "Meta"):
  15. return (
  16. f"{schema_cls.Meta.parent_schema_name}.{schema_cls.Meta.model.__name__}"
  17. )
  18. if name.endswith("Schema"):
  19. return name[:-6] or name
  20. return name
  21. class OpenApi(BaseApi):
  22. route_base = "/api"
  23. allow_browser_login = True
  24. @expose("/<version>/_openapi")
  25. @protect()
  26. @safe
  27. def get(self, version):
  28. """Endpoint that renders an OpenApi spec for all views that belong
  29. to a certain version
  30. ---
  31. get:
  32. description: >-
  33. Get the OpenAPI spec for a specific API version
  34. parameters:
  35. - in: path
  36. schema:
  37. type: string
  38. name: version
  39. responses:
  40. 200:
  41. description: The OpenAPI spec
  42. content:
  43. application/json:
  44. schema:
  45. type: object
  46. 404:
  47. $ref: '#/components/responses/404'
  48. 500:
  49. $ref: '#/components/responses/500'
  50. """
  51. version_found = False
  52. api_spec = self._create_api_spec(version)
  53. for base_api in current_app.appbuilder.baseviews:
  54. if isinstance(base_api, BaseApi) and base_api.version == version:
  55. base_api.add_api_spec(api_spec)
  56. version_found = True
  57. if version_found:
  58. return self.response(200, **api_spec.to_dict())
  59. else:
  60. return self.response_404()
  61. @staticmethod
  62. def _create_api_spec(version):
  63. servers = current_app.config.get(
  64. "FAB_OPENAPI_SERVERS", [{"url": request.host_url}]
  65. )
  66. return APISpec(
  67. title=current_app.appbuilder.app_name,
  68. version=version,
  69. openapi_version="3.0.2",
  70. info=dict(description=current_app.appbuilder.app_name),
  71. plugins=[MarshmallowPlugin(schema_name_resolver=resolver)],
  72. servers=servers,
  73. )
  74. class SwaggerView(BaseView):
  75. route_base = "/swagger"
  76. default_view = "ui"
  77. openapi_uri = "/api/{}/_openapi"
  78. @expose("/<version>")
  79. @has_access
  80. def show(self, version):
  81. return self.render_template(
  82. self.appbuilder.app.config.get(
  83. "FAB_API_SWAGGER_TEMPLATE", "appbuilder/swagger/swagger.html"
  84. ),
  85. openapi_uri=self.openapi_uri.format(version),
  86. )
  87. class OpenApiManager(BaseManager):
  88. def register_views(self):
  89. if not self.appbuilder.app.config.get("FAB_ADD_OPENAPI_VIEWS", True):
  90. return
  91. if self.appbuilder.get_app.config.get("FAB_API_SWAGGER_UI", False):
  92. self.appbuilder.add_api(OpenApi)
  93. self.appbuilder.add_view_no_menu(SwaggerView)