Sensible defaults for controller resolution

This commit is contained in:
John Kleijn
2015-12-06 07:55:27 +01:00
parent d133d6afc6
commit a5f1af62a7
6 changed files with 68 additions and 17 deletions

View File

@@ -33,7 +33,8 @@ class Api:
"""
def __init__(self, swagger_yaml_path, base_url=None, arguments=None, swagger_ui=None, swagger_path=None,
swagger_url=None, validate_responses=False, resolver=utils.get_function_from_name):
swagger_url=None, validate_responses=False, default_controller_name='',
resolver=utils.get_function_from_name):
"""
:type swagger_yaml_path: pathlib.Path
:type base_url: str | None
@@ -76,6 +77,7 @@ class Api:
self.definitions = self.specification.get('definitions', {})
self.parameter_definitions = self.specification.get('parameters', {})
self.default_controller_name = default_controller_name
self.swagger_path = swagger_path or SWAGGER_UI_PATH
self.swagger_url = swagger_url or SWAGGER_UI_URL
@@ -111,9 +113,9 @@ class Api:
:type swagger_operation: dict
"""
operation = Operation(method=method, path=path, operation=swagger_operation,
app_produces=self.produces, app_security=self.security,
security_definitions=self.security_definitions, definitions=self.definitions,
parameter_definitions=self.parameter_definitions,
default_controller_name=self.default_controller_name, app_produces=self.produces,
app_security=self.security, security_definitions=self.security_definitions,
definitions=self.definitions, parameter_definitions=self.parameter_definitions,
validate_responses=self.validate_responses, resolver=self.resolver)
operation_id = operation.operation_id
logger.debug('... Adding %s -> %s', method.upper(), operation_id, extra=vars(operation))

View File

@@ -26,7 +26,7 @@ logger = logging.getLogger('connexion.app')
class App:
def __init__(self, import_name, port=None, specification_dir='', server=None, arguments=None, debug=False,
swagger_ui=True, swagger_path=None, swagger_url=None):
swagger_ui=True, swagger_path=None, swagger_url=None, default_controller_name=''):
"""
:param import_name: the name of the application package
:type import_name: str
@@ -46,6 +46,8 @@ class App:
:type swagger_path: string | None
:param swagger_url: URL to access swagger-ui documentation
:type swagger_url: string | None
:param default_controller_name: Default controller name for operations
:type default_controller_name: string | import_name
"""
self.app = flask.Flask(import_name)
@@ -72,6 +74,7 @@ class App:
self.swagger_ui = swagger_ui
self.swagger_path = swagger_path
self.swagger_url = swagger_url
self.default_controller_name = default_controller_name or import_name
@staticmethod
def common_error_handler(exception):

View File

@@ -19,7 +19,7 @@ from .decorators.produces import BaseSerializer, Produces, Jsonifier
from .decorators.security import security_passthrough, verify_oauth
from .decorators.validation import RequestBodyValidator, ParameterValidator
from .decorators.metrics import UWSGIMetricsCollector
from.decorators.response import ResponseValidator
from .decorators.response import ResponseValidator
from .exceptions import InvalidSpecification
from .utils import flaskify_endpoint, produces_json
@@ -31,8 +31,8 @@ class Operation:
A single API operation on a path.
"""
def __init__(self, method, path, operation, app_produces, app_security,
security_definitions, definitions, parameter_definitions, resolver, validate_responses=False):
def __init__(self, method, path, operation, app_produces, app_security, security_definitions, definitions,
parameter_definitions, resolver, default_controller_name='', validate_responses=False):
"""
This class uses the OperationID identify the module and function that will handle the operation
@@ -74,13 +74,10 @@ class Operation:
'parameters': self.parameter_definitions
}
self.validate_responses = validate_responses
self.default_router_controller_name = default_controller_name
self.operation = operation
operation_id = operation['operationId']
router_controller = operation.get('x-swagger-router-controller')
self.operation_id = self.detect_controller(operation_id, router_controller)
self.operation_id = self.detect_controller()
# todo support definition references
# todo support references to application level parameters
self.parameters = list(self.resolve_parameters(operation.get('parameters', [])))
@@ -89,10 +86,22 @@ class Operation:
self.security = operation.get('security', app_security)
self.__undecorated_function = resolver(self.operation_id)
def detect_controller(self, operation_id, router_controller):
if router_controller is None:
def detect_controller(self):
operation_id = self.operation.get('operationId')
x_router_controller = self.operation.get('x-swagger-router-controller')
if operation_id:
if x_router_controller:
return x_router_controller + '.' + operation_id
return operation_id
return router_controller + '.' + operation_id
if x_router_controller:
mod_name = x_router_controller
else:
mod_name = self.default_router_controller_name
return mod_name + '.' + self.method.lower()
def resolve_reference(self, schema):
schema = schema.copy() # avoid changing the original schema

View File

@@ -14,6 +14,8 @@ class DummyClass:
class_instance = DummyClass()
def get():
return ''
def post_greeting(name):
data = {'greeting': 'Hello {name}'.format(name=name)}

View File

@@ -64,6 +64,11 @@ def test_app_with_relative_path():
test_app(app)
def test_default_controller_name_defaults_to_app_module(app):
app = App(__name__)
assert app.default_controller_name == 'test_app'
def test_app(app):
assert app.port == 5001

View File

@@ -122,6 +122,8 @@ OPERATION6 = {'description': 'Adds a new stack to be created by lizzy and return
'security': [{'oauth': ['uid']}],
'summary': 'Create new stack'}
OPERATION7 = {'x-swagger-router-controller': 'fakeapi.hello'}
SECURITY_DEFINITIONS = {'oauth': {'type': 'oauth2',
'flow': 'password',
'x-tokenInfoUrl': 'https://ouath.example/token_info',
@@ -249,6 +251,7 @@ def test_resolve_invalid_reference():
exception = exc_info.value # type: InvalidSpecification
assert exception.reason == "GET endpoint '$ref' needs to start with '#/'"
def test_detect_controller():
operation = Operation(method='GET',
path='endpoint',
@@ -260,3 +263,30 @@ def test_detect_controller():
parameter_definitions=PARAMETER_DEFINITIONS,
resolver=get_function_from_name)
assert operation.operation_id == 'fakeapi.hello.post_greeting'
def test_controller_resolution_with_x_controller_router():
operation = Operation(method='GET',
path='endpoint',
operation=OPERATION7,
app_produces=['application/json'],
app_security=[],
security_definitions={},
definitions={},
parameter_definitions=PARAMETER_DEFINITIONS,
resolver=get_function_from_name)
assert operation.operation_id == 'fakeapi.hello.get'
def test_controller_resolution_with_default_controller_name():
operation = Operation(method='GET',
path='endpoint',
operation={},
default_controller_name='fakeapi.hello',
app_produces=['application/json'],
app_security=[],
security_definitions={},
definitions={},
parameter_definitions=PARAMETER_DEFINITIONS,
resolver=get_function_from_name)
assert operation.operation_id == 'fakeapi.hello.get'