|  | @@ -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)
 |