context.py 4.7KB

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