123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- # -*- coding: utf-8 -*-
- import typing
- from http import HTTPStatus
- import functools
-
- import marshmallow
-
- from hapic.buffer import DecorationBuffer
- from hapic.context import ContextInterface, BottleContext
- from hapic.decorator import DecoratedController
- from hapic.decorator import ExceptionHandlerControllerWrapper
- from hapic.decorator import InputBodyControllerWrapper
- from hapic.decorator import InputHeadersControllerWrapper
- from hapic.decorator import InputPathControllerWrapper
- from hapic.decorator import InputQueryControllerWrapper
- from hapic.decorator import OutputBodyControllerWrapper
- from hapic.decorator import OutputHeadersControllerWrapper
- from hapic.description import InputBodyDescription, ErrorDescription
- from hapic.description import InputFormsDescription
- from hapic.description import InputHeadersDescription
- from hapic.description import InputPathDescription
- from hapic.description import InputQueryDescription
- from hapic.description import OutputBodyDescription
- from hapic.description import OutputHeadersDescription
- from hapic.doc import DocGenerator
- from hapic.processor import ProcessorInterface
- from hapic.processor import MarshmallowInputProcessor
-
- # TODO: Gérer les erreurs avec schema
- # TODO: Gérer les erreurs avec schema: pouvoir le spécialiser
- # TODO: Gérer les cas ou c'est une liste la réponse (items, item_nb)
- # TODO: Confusion nommage body/json/forms
-
- # _waiting = {}
- # _endpoints = {}
- # FIXME: Voir
- class ErrorResponseSchema(marshmallow.Schema):
- error_message = marshmallow.fields.String(required=True)
-
-
- error_details = marshmallow.fields.Dict(required=True)
- # FIXME: C'est un gros gros fake !
- _default_global_error_schema = ErrorResponseSchema()
-
-
- class Hapic(object):
- def __init__(self):
- self._buffer = DecorationBuffer()
- self._controllers = [] # type: typing.List[DecoratedController]
- self._context = None
- # TODO: Permettre la surcharge des classes utilisés ci-dessous
-
- def with_api_doc(self):
- def decorator(func):
-
- # FIXME: casse ou casse pas le bis ?
- # @functools.wraps(func)
- def wrapper(*args, **kwargs):
- return func(*args, **kwargs)
-
- description = self._buffer.get_description()
- decorated_controller = DecoratedController(
- reference=wrapper,
- description=description,
- )
- self._buffer.clear()
- self._controllers.append(decorated_controller)
- return wrapper
-
- return decorator
-
- def set_context(self, context: ContextInterface) -> None:
- assert not self._context
- self._context = context
-
- def output_body(
- self,
- schema: typing.Any,
- processor: ProcessorInterface = None,
- context: ContextInterface = None,
- error_http_code: HTTPStatus = HTTPStatus.BAD_REQUEST,
- default_http_code: HTTPStatus = HTTPStatus.OK,
- ) -> typing.Callable[[typing.Callable[..., typing.Any]], typing.Any]:
- processor = processor or MarshmallowInputProcessor()
- processor.schema = schema
- context = context or self._context
-
- decoration = OutputBodyControllerWrapper(
- context=context,
- processor=processor,
- error_http_code=error_http_code,
- default_http_code=default_http_code,
- )
-
- def decorator(func):
- self._buffer.output_body = OutputBodyDescription(decoration)
- return decoration.get_wrapper(func)
- return decorator
-
- def output_headers(
- self,
- schema: typing.Any,
- processor: ProcessorInterface = None,
- context: ContextInterface = None,
- error_http_code: HTTPStatus = HTTPStatus.BAD_REQUEST,
- default_http_code: HTTPStatus = HTTPStatus.OK,
- ) -> typing.Callable[[typing.Callable[..., typing.Any]], typing.Any]:
- processor = processor or MarshmallowInputProcessor()
- processor.schema = schema
- context = context or self._context
-
- decoration = OutputHeadersControllerWrapper(
- context=context,
- processor=processor,
- error_http_code=error_http_code,
- default_http_code=default_http_code,
- )
-
- def decorator(func):
- self._buffer.output_headers = OutputHeadersDescription(decoration)
- return decoration.get_wrapper(func)
- return decorator
-
- def input_headers(
- self,
- schema: typing.Any,
- processor: ProcessorInterface = None,
- context: ContextInterface = None,
- error_http_code: HTTPStatus = HTTPStatus.BAD_REQUEST,
- default_http_code: HTTPStatus = HTTPStatus.OK,
- ) -> typing.Callable[[typing.Callable[..., typing.Any]], typing.Any]:
- processor = processor or MarshmallowInputProcessor()
- processor.schema = schema
- context = context or self._context
-
- decoration = InputHeadersControllerWrapper(
- context=context,
- processor=processor,
- error_http_code=error_http_code,
- default_http_code=default_http_code,
- )
-
- def decorator(func):
- self._buffer.input_headers = InputHeadersDescription(decoration)
- return decoration.get_wrapper(func)
- return decorator
-
- def input_path(
- self,
- schema: typing.Any,
- processor: ProcessorInterface = None,
- context: ContextInterface = None,
- error_http_code: HTTPStatus = HTTPStatus.BAD_REQUEST,
- default_http_code: HTTPStatus = HTTPStatus.OK,
- ) -> typing.Callable[[typing.Callable[..., typing.Any]], typing.Any]:
- processor = processor or MarshmallowInputProcessor()
- processor.schema = schema
- context = context or self._context
-
- decoration = InputPathControllerWrapper(
- context=context,
- processor=processor,
- error_http_code=error_http_code,
- default_http_code=default_http_code,
- )
-
- def decorator(func):
- self._buffer.input_path = InputPathDescription(decoration)
- return decoration.get_wrapper(func)
- return decorator
-
- def input_query(
- self,
- schema: typing.Any,
- processor: ProcessorInterface = None,
- context: ContextInterface = None,
- error_http_code: HTTPStatus = HTTPStatus.BAD_REQUEST,
- default_http_code: HTTPStatus = HTTPStatus.OK,
- ) -> typing.Callable[[typing.Callable[..., typing.Any]], typing.Any]:
- processor = processor or MarshmallowInputProcessor()
- processor.schema = schema
- context = context or self._context
-
- decoration = InputQueryControllerWrapper(
- context=context,
- processor=processor,
- error_http_code=error_http_code,
- default_http_code=default_http_code,
- )
-
- def decorator(func):
- self._buffer.input_query = InputQueryDescription(decoration)
- return decoration.get_wrapper(func)
- return decorator
-
- def input_body(
- self,
- schema: typing.Any,
- processor: ProcessorInterface = None,
- context: ContextInterface = None,
- error_http_code: HTTPStatus = HTTPStatus.BAD_REQUEST,
- default_http_code: HTTPStatus = HTTPStatus.OK,
- ) -> typing.Callable[[typing.Callable[..., typing.Any]], typing.Any]:
- processor = processor or MarshmallowInputProcessor()
- processor.schema = schema
- context = context or self._context
-
- decoration = InputBodyControllerWrapper(
- context=context,
- processor=processor,
- error_http_code=error_http_code,
- default_http_code=default_http_code,
- )
-
- def decorator(func):
- self._buffer.input_body = InputBodyDescription(decoration)
- return decoration.get_wrapper(func)
- return decorator
-
- def input_forms(
- self,
- schema: typing.Any,
- processor: ProcessorInterface=None,
- context: ContextInterface=None,
- error_http_code: HTTPStatus = HTTPStatus.BAD_REQUEST,
- default_http_code: HTTPStatus = HTTPStatus.OK,
- ) -> typing.Callable[[typing.Callable[..., typing.Any]], typing.Any]:
- processor = processor or MarshmallowInputProcessor()
- processor.schema = schema
- context = context or self._context
-
- decoration = InputBodyControllerWrapper(
- context=context,
- processor=processor,
- error_http_code=error_http_code,
- default_http_code=default_http_code,
- )
-
- def decorator(func):
- self._buffer.input_forms = InputFormsDescription(decoration)
- return decoration.get_wrapper(func)
- return decorator
-
- def handle_exception(
- self,
- handled_exception_class: typing.Type[Exception],
- http_code: HTTPStatus = HTTPStatus.INTERNAL_SERVER_ERROR,
- context: ContextInterface = None,
- ) -> typing.Callable[[typing.Callable[..., typing.Any]], typing.Any]:
- context = context or self._context
-
- decoration = ExceptionHandlerControllerWrapper(
- handled_exception_class,
- context,
- http_code,
- )
-
- def decorator(func):
- self._buffer.errors.append(ErrorDescription(decoration))
- return decoration.get_wrapper(func)
- return decorator
-
- def generate_doc(self, app): # FIXME: j'ai du tricher avec app
- # FIXME @Damien bottle specific code !
- # rendre ca generique
- app = app or self._context.get_app()
- doc_generator = DocGenerator()
- return doc_generator.get_doc(self._controllers, app)
|