context.py 4.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. # -*- coding: utf-8 -*-
  2. import json
  3. import re
  4. import typing
  5. from http import HTTPStatus
  6. from pyramid.request import Request
  7. from pyramid.response import Response
  8. from pyramid.config import Configurator
  9. from hapic.context import ContextInterface
  10. from hapic.context import RouteRepresentation
  11. from hapic.decorator import DecoratedController
  12. from hapic.decorator import DECORATION_ATTRIBUTE_NAME
  13. from hapic.ext.bottle.context import BOTTLE_RE_PATH_URL
  14. from hapic.exception import OutputValidationException
  15. from hapic.processor import RequestParameters
  16. from hapic.processor import ProcessValidationError
  17. # Bottle regular expression to locate url parameters
  18. PYRAMID_RE_PATH_URL = re.compile(r'')
  19. class PyramidContext(ContextInterface):
  20. def __init__(self, configurator: Configurator):
  21. self.configurator = configurator
  22. def get_request_parameters(self, *args, **kwargs) -> RequestParameters:
  23. req = args[-1] # TODO : Check
  24. assert isinstance(req, Request)
  25. # TODO : move this code to check_json
  26. # same idea as in : https://bottlepy.org/docs/dev/_modules/bottle.html#BaseRequest.json
  27. if req.body and req.content_type in ('application/json', 'application/json-rpc'):
  28. json_body = req.json_body
  29. # TODO : raise exception if not correct , return 400 if uncorrect instead ?
  30. else:
  31. json_body = None
  32. return RequestParameters(
  33. path_parameters=req.matchdict,
  34. query_parameters=req.GET,
  35. body_parameters=json_body,
  36. form_parameters=req.POST,
  37. header_parameters=req.headers,
  38. )
  39. def get_response(
  40. self,
  41. response: dict,
  42. http_code: int,
  43. ) -> Response:
  44. return Response(
  45. body=json.dumps(response),
  46. headers=[
  47. ('Content-Type', 'application/json'),
  48. ],
  49. status=http_code,
  50. )
  51. def get_validation_error_response(
  52. self,
  53. error: ProcessValidationError,
  54. http_code: HTTPStatus=HTTPStatus.BAD_REQUEST,
  55. ) -> typing.Any:
  56. # TODO BS 20171010: Manage error schemas, see #4
  57. from hapic.hapic import _default_global_error_schema
  58. unmarshall = _default_global_error_schema.dump(error)
  59. if unmarshall.errors:
  60. raise OutputValidationException(
  61. 'Validation error during dump of error response: {}'.format(
  62. str(unmarshall.errors)
  63. )
  64. )
  65. return Response(
  66. body=json.dumps(unmarshall.data),
  67. headers=[
  68. ('Content-Type', 'application/json'),
  69. ],
  70. status=int(http_code),
  71. )
  72. def find_route(
  73. self,
  74. decorated_controller: DecoratedController,
  75. ) -> RouteRepresentation:
  76. for category in self.configurator.introspector.get_category('views'):
  77. view_intr = category['introspectable']
  78. route_intr = category['related']
  79. reference = decorated_controller.reference
  80. route_token = getattr(
  81. view_intr.get('callable'),
  82. DECORATION_ATTRIBUTE_NAME,
  83. None,
  84. )
  85. match_with_wrapper = view_intr.get('callable') == reference.wrapper
  86. match_with_wrapped = view_intr.get('callable') == reference.wrapped
  87. match_with_token = route_token == reference.token
  88. if match_with_wrapper or match_with_wrapped or match_with_token:
  89. # TODO BS 20171107: C'est une liste de route sous pyramid !!!
  90. # Mais de toute maniere les framework womme pyramid, flask
  91. # peuvent avoir un controlleur pour plusieurs routes doc
  92. # .find_route doit retourner une liste au lieu d'une seule
  93. # route
  94. route_pattern = route_intr[0].get('pattern')
  95. route_method = route_intr[0].get('request_methods')[0]
  96. return RouteRepresentation(
  97. # TODO BS 20171107: ce code n'es pas du tout finis
  98. # (import bottle)
  99. rule=BOTTLE_RE_PATH_URL.sub(
  100. r'{\1}',
  101. route_pattern,
  102. ),
  103. method=route_method,
  104. )
  105. def get_swagger_path(self, contextualised_rule: str) -> str:
  106. # TODO BS 20171110: Pyramid allow route like '/{foo:\d+}', so adapt
  107. # and USE regular expression (see https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/urldispatch.html#custom-route-predicates) # nopep8
  108. return contextualised_rule