context.py 4.5KB

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