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