|
@@ -1,17 +1,28 @@
|
1
|
1
|
# -*- coding: utf-8 -*-
|
2
|
2
|
import json
|
|
3
|
+import re
|
3
|
4
|
import typing
|
4
|
5
|
from http import HTTPStatus
|
5
|
6
|
|
6
|
7
|
from hapic.context import ContextInterface
|
|
8
|
+from hapic.context import RouteRepresentation
|
|
9
|
+from hapic.decorator import DecoratedController
|
|
10
|
+from hapic.decorator import DECORATION_ATTRIBUTE_NAME
|
7
|
11
|
from hapic.exception import OutputValidationException
|
8
|
12
|
from hapic.processor import RequestParameters, ProcessValidationError
|
|
13
|
+from flask import Flask
|
9
|
14
|
|
10
|
15
|
if typing.TYPE_CHECKING:
|
11
|
16
|
from flask import Response
|
12
|
17
|
|
|
18
|
+# flask regular expression to locate url parameters
|
|
19
|
+FLASK_RE_PATH_URL = re.compile(r'<(?:[^:<>]+:)?([^<>]+)>')
|
|
20
|
+
|
13
|
21
|
|
14
|
22
|
class FlaskContext(ContextInterface):
|
|
23
|
+ def __init__(self, app: Flask):
|
|
24
|
+ self.app = app
|
|
25
|
+
|
15
|
26
|
def get_request_parameters(self, *args, **kwargs) -> RequestParameters:
|
16
|
27
|
from flask import request
|
17
|
28
|
return RequestParameters(
|
|
@@ -56,5 +67,37 @@ class FlaskContext(ContextInterface):
|
56
|
67
|
status=int(http_code),
|
57
|
68
|
)
|
58
|
69
|
|
59
|
|
-# TODO BS 20171115: Implement other context methods
|
60
|
|
-# (take source in example_a_flask.py)
|
|
70
|
+ def find_route(
|
|
71
|
+ self,
|
|
72
|
+ decorated_controller: 'DecoratedController',
|
|
73
|
+ ):
|
|
74
|
+ reference = decorated_controller.reference
|
|
75
|
+ for route in self.app.url_map.iter_rules():
|
|
76
|
+ if route.endpoint not in self.app.view_functions:
|
|
77
|
+ continue
|
|
78
|
+ route_callback = self.app.view_functions[route.endpoint]
|
|
79
|
+ route_token = getattr(
|
|
80
|
+ route_callback,
|
|
81
|
+ DECORATION_ATTRIBUTE_NAME,
|
|
82
|
+ None,
|
|
83
|
+ )
|
|
84
|
+ match_with_wrapper = route_callback == reference.wrapper
|
|
85
|
+ match_with_wrapped = route_callback == reference.wrapped
|
|
86
|
+ match_with_token = route_token == reference.token
|
|
87
|
+
|
|
88
|
+ # FIXME - G.M - 2017-12-04 - return list instead of one method
|
|
89
|
+ # This fix, return only 1 allowed method, change this when
|
|
90
|
+ # RouteRepresentation is adapted to return multiples methods.
|
|
91
|
+ method = [x for x in route.methods
|
|
92
|
+ if x not in ['OPTIONS', 'HEAD']][0]
|
|
93
|
+
|
|
94
|
+ if match_with_wrapper or match_with_wrapped or match_with_token:
|
|
95
|
+ return RouteRepresentation(
|
|
96
|
+ rule=self.get_swagger_path(route.rule),
|
|
97
|
+ method=method,
|
|
98
|
+ original_route_object=route,
|
|
99
|
+ )
|
|
100
|
+
|
|
101
|
+ def get_swagger_path(self, contextualised_rule: str) -> str:
|
|
102
|
+ # TODO - G.M - 2017-12-05 Check if all route path are handled correctly
|
|
103
|
+ return FLASK_RE_PATH_URL.sub(r'{\1}', contextualised_rule)
|