Skip to content

Commit

Permalink
#380 first hack (tests broken) works with helloworld
Browse files Browse the repository at this point in the history
  • Loading branch information
hjacobs committed Jan 12, 2017
1 parent 2de1b01 commit 47d694c
Show file tree
Hide file tree
Showing 9 changed files with 69 additions and 43 deletions.
9 changes: 4 additions & 5 deletions connexion/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ def __init__(self, specification, framework, base_url=None, arguments=None,
else:
self.base_url = canonical_base_url(base_url)
self.specification['basePath'] = base_url
self.framework.set_base_url(self.base_url)

# A list of MIME types the APIs can produce. This is global to all APIs but can be overridden on specific
# API calls.
Expand Down Expand Up @@ -138,11 +139,8 @@ def __init__(self, specification, framework, base_url=None, arguments=None,
logger.debug('Strict Request Validation: %s', str(validate_responses))
self.strict_validation = strict_validation

# Create blueprint and endpoints
self.blueprint = self.create_blueprint()

if swagger_json:
self.framework.register_swagger_json()
self.framework.register_swagger_json(self.specification)
if swagger_ui:
self.framework.register_swagger_ui()

Expand All @@ -168,7 +166,8 @@ def add_operation(self, method, path, swagger_operation, path_parameters):
:type path: str
:type swagger_operation: dict
"""
operation = Operation(method=method,
operation = Operation(framework=self.framework,
method=method,
path=path,
path_parameters=path_parameters,
operation=swagger_operation,
Expand Down
2 changes: 1 addition & 1 deletion connexion/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ def add_api(self, specification, base_path=None, arguments=None, auth_all_paths=
auth_all_paths=auth_all_paths,
debug=self.debug,
validator_map=self.validator_map)
self.app.register_blueprint(api.blueprint)
framework.register_app(self.app)
return api

def _resolver_error_handler(self, *args, **kwargs):
Expand Down
7 changes: 5 additions & 2 deletions connexion/decorators/decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def __call__(self, function):
:rtype: types.FunctionType
"""
@functools.wraps(function)
def wrapper(*args, **kwargs):
def wrapper(request, *args, **kwargs):
operation_handler_result = function(*args, **kwargs)
return self.get_response_container(operation_handler_result)

Expand All @@ -107,6 +107,8 @@ class EndOfRequestLifecycleDecorator(BaseDecorator):
Filter the ResponseContainer instance to return the corresponding
flask.Response object.
"""
def __init__(self, framework):
self.framework = framework

def __call__(self, function):
"""
Expand All @@ -115,7 +117,8 @@ def __call__(self, function):
"""
@functools.wraps(function)
def wrapper(*args, **kwargs):
response_container = function(*args, **kwargs)
request = self.framework.get_request(*args, **kwargs)
response_container = function(request, *args, **kwargs)
return response_container.flask_response_object()

return wrapper
Expand Down
13 changes: 6 additions & 7 deletions connexion/decorators/produces.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import functools
import logging

import flask
from flask import json

from .decorator import BaseDecorator
Expand Down Expand Up @@ -54,9 +53,9 @@ def __call__(self, function):
"""

@functools.wraps(function)
def wrapper(*args, **kwargs):
url = flask.request.url
response = function(*args, **kwargs)
def wrapper(request, *args, **kwargs):
url = request.url
response = function(request, *args, **kwargs)
logger.debug('Returning %s', url,
extra={'url': url, 'mimetype': self.mimetype})
return response
Expand Down Expand Up @@ -85,13 +84,13 @@ def __call__(self, function):
"""

@functools.wraps(function)
def wrapper(*args, **kwargs):
url = flask.request.url
def wrapper(request, *args, **kwargs):
url = request.url

logger.debug('Jsonifing %s', url,
extra={'url': url, 'mimetype': self.mimetype})

response = function(*args, **kwargs)
response = function(request, *args, **kwargs)

if response.is_handler_response_object:
logger.debug('Endpoint returned a Flask Response',
Expand Down
10 changes: 5 additions & 5 deletions connexion/decorators/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def __init__(self, operation, mimetype):
self.operation = operation
self.mimetype = mimetype

def validate_response(self, data, status_code, headers):
def validate_response(self, data, status_code, headers, url):
"""
Validates the Response object based on what has been declared in the specification.
Ensures the response body matches the declated schema.
Expand All @@ -47,7 +47,7 @@ def validate_response(self, data, status_code, headers):
data = json.dumps(data)
data = json.loads(data)

v.validate_schema(data)
v.validate_schema(data, url)
except ValidationError as e:
raise NonConformingResponseBody(message=str(e))

Expand Down Expand Up @@ -86,10 +86,10 @@ def __call__(self, function):
:rtype: types.FunctionType
"""
@functools.wraps(function)
def wrapper(*args, **kwargs):
response = function(*args, **kwargs)
def wrapper(request, *args, **kwargs):
response = function(request, *args, **kwargs)
try:
self.validate_response(response.get_data(), response.status_code, response.headers)
self.validate_response(response.get_data(), response.status_code, response.headers, request.url)
except NonConformingResponseBody as e:
return problem(500, e.reason, e.message)
except NonConformingResponseHeaders as e:
Expand Down
24 changes: 12 additions & 12 deletions connexion/decorators/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,21 +102,21 @@ def __call__(self, function):
"""

@functools.wraps(function)
def wrapper(*args, **kwargs):
def wrapper(request, *args, **kwargs):
if all_json(self.consumes):
data = flask.request.json
data = request.json

logger.debug("%s validating schema...", flask.request.url)
error = self.validate_schema(data)
logger.debug("%s validating schema...", request.url)
error = self.validate_schema(data, request.url)
if error and not self.has_default:
return error

response = function(*args, **kwargs)
response = function(request, *args, **kwargs)
return response

return wrapper

def validate_schema(self, data):
def validate_schema(self, data, url):
"""
:type data: dict
:rtype: flask.Response | None
Expand All @@ -127,7 +127,7 @@ def validate_schema(self, data):
try:
self.validator.validate(data)
except ValidationError as exception:
logger.error("{url} validation error: {error}".format(url=flask.request.url,
logger.error("{url} validation error: {error}".format(url=url,
error=exception.message))
return problem(400, 'Bad Request', str(exception.message))

Expand All @@ -145,15 +145,15 @@ def __init__(self, schema, validator=None):
ValidatorClass = validator or Draft4Validator
self.validator = ValidatorClass(schema, format_checker=draft4_format_checker)

def validate_schema(self, data):
def validate_schema(self, data, url):
"""
:type data: dict
:rtype: flask.Response | None
"""
try:
self.validator.validate(data)
except ValidationError as exception:
logger.error("{url} validation error: {error}".format(url=flask.request.url,
logger.error("{url} validation error: {error}".format(url=url,
error=exception))
six.reraise(*sys.exc_info())

Expand Down Expand Up @@ -253,8 +253,8 @@ def __call__(self, function):
"""

@functools.wraps(function)
def wrapper(*args, **kwargs):
logger.debug("%s validating parameters...", flask.request.url)
def wrapper(request, *args, **kwargs):
logger.debug("%s validating parameters...", request.url)

if self.strict_validation:
query_errors = self.validate_query_parameter_list()
Expand Down Expand Up @@ -283,6 +283,6 @@ def wrapper(*args, **kwargs):
if error:
return problem(400, 'Bad Request', error)

return function(*args, **kwargs)
return function(request, *args, **kwargs)

return wrapper
13 changes: 12 additions & 1 deletion connexion/frameworks/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@


class Framework:
'''Generic interface for framework implementations'''
'''Generic interface for framework implementations
assumptions about request object:
* headers: dict (case insensitive or not)
* data: Python object (e.g. JSON request body)
* args:
* form:
* files:
'''
def __init__(self, base_url):
raise NotImplementedError()

def register_operation(self, method, path, operation):
raise NotImplementedError()
Expand Down
26 changes: 19 additions & 7 deletions connexion/frameworks/flask.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging

import connexion.api
import connexion.utils as utils
import flask
import werkzeug.exceptions
Expand All @@ -10,7 +11,19 @@

class FlaskFramework:
def __init__(self):
self.blueprint = self._create_blueprint()
self.swagger_path = connexion.api.SWAGGER_UI_PATH
self.swagger_url = connexion.api.SWAGGER_UI_URL

def get_request(self, *args, **kwargs):
return flask.request

def register_app(self, app):
self.app = app
self.app.register_blueprint(self.blueprint)

def set_base_url(self, base_url):
self.base_url = base_url
self.blueprint = self._create_blueprint(base_url)

def register_operation(self, method, path, operation):
operation_id = operation.operation_id
Expand All @@ -20,15 +33,15 @@ def register_operation(self, method, path, operation):
flask_path = utils.flaskify_path(path, operation.get_path_parameter_types())
self.blueprint.add_url_rule(flask_path, operation.endpoint_name, operation.function, methods=[method])

def register_swagger_json(self):
def register_swagger_json(self, specification):
"""
Adds swagger json to {base_url}/swagger.json
"""
logger.debug('Adding swagger.json: %s/swagger.json', self.base_url)
endpoint_name = "{name}_swagger_json".format(name=self.blueprint.name)
self.blueprint.add_url_rule('/swagger.json',
endpoint_name,
lambda: flask.jsonify(self.specification))
lambda: flask.jsonify(specification))

def register_swagger_ui(self):
"""
Expand All @@ -37,10 +50,10 @@ def register_swagger_ui(self):
logger.debug('Adding swagger-ui: %s/%s/', self.base_url, self.swagger_url)
static_endpoint_name = "{name}_swagger_ui_static".format(name=self.blueprint.name)
self.blueprint.add_url_rule('/{swagger_url}/<path:filename>'.format(swagger_url=self.swagger_url),
static_endpoint_name, self.swagger_ui_static)
static_endpoint_name, self._swagger_ui_static)
index_endpoint_name = "{name}_swagger_ui_index".format(name=self.blueprint.name)
self.blueprint.add_url_rule('/{swagger_url}/'.format(swagger_url=self.swagger_url),
index_endpoint_name, self.swagger_ui_index)
index_endpoint_name, self._swagger_ui_index)

def register_auth_on_not_found(self):
"""
Expand All @@ -52,12 +65,11 @@ def register_auth_on_not_found(self):
endpoint_name = "{name}_not_found".format(name=self.blueprint.name)
self.blueprint.add_url_rule('/<path:invalid_path>', endpoint_name, not_found_error.function)

def _create_blueprint(self, base_url=None):
def _create_blueprint(self, base_url):
"""
:type base_url: str | None
:rtype: flask.Blueprint
"""
base_url = base_url or self.base_url
logger.debug('Creating API blueprint: %s', base_url)
endpoint = utils.flaskify_endpoint(base_url)
blueprint = flask.Blueprint(endpoint, __name__, url_prefix=base_url,
Expand Down
8 changes: 5 additions & 3 deletions connexion/operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,15 @@

class SecureOperation(object):

def __init__(self, security, security_definitions):
def __init__(self, framework, security, security_definitions):
"""
:param security: list of security rules the application uses by default
:type security: list
:param security_definitions: `Security Definitions Object
<https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#security-definitions-object>`_
:type security_definitions: dict
"""
self.framework = framework
self.security = security
self.security_definitions = security_definitions

Expand Down Expand Up @@ -120,7 +121,7 @@ def _request_end_lifecycle_decorator(self):
:rtype: types.FunctionType
"""
return EndOfRequestLifecycleDecorator()
return EndOfRequestLifecycleDecorator(self.framework)


class Operation(SecureOperation):
Expand All @@ -129,7 +130,7 @@ class Operation(SecureOperation):
A single API operation on a path.
"""

def __init__(self, method, path, operation, resolver, app_produces, app_consumes,
def __init__(self, framework, method, path, operation, resolver, app_produces, app_consumes,
path_parameters=None, app_security=None, security_definitions=None,
definitions=None, parameter_definitions=None, response_definitions=None,
validate_responses=False, strict_validation=False, randomize_endpoint=None,
Expand Down Expand Up @@ -179,6 +180,7 @@ def __init__(self, method, path, operation, resolver, app_produces, app_consumes
:type strict_validation: bool
"""

self.framework = framework
self.method = method
self.path = path
self.validator_map = dict(VALIDATOR_MAP)
Expand Down

0 comments on commit 47d694c

Please sign in to comment.