Skip to content

Flask OpenAPI request parameters #185

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,21 @@ As an alternative to the decorator-based integration, Flask method based views c

app.add_url_rule('/home', view_func=MyView.as_view('home', spec))

Request parameters
==================

In Flask, all unmarshalled request data are provided as Flask request object's openapi.parameters attribute

.. code-block:: python

from flask.globals import request

@app.route('/browse/<id>/')
@openapi
def home():
browse_id = request.openapi.parameters.path['id']
page = request.openapi.parameters.query.get('page', 1)

Low level
=========

Expand Down
6 changes: 6 additions & 0 deletions openapi_core/contrib/flask/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ def __init__(
request_provider, openapi_errors_handler,
)

def _handle_request_view(self, request_result, view, *args, **kwargs):
request = self._get_request(*args, **kwargs)
request.openapi = request_result
return super(FlaskOpenAPIViewDecorator, self)._handle_request_view(
request_result, view, *args, **kwargs)

@classmethod
def from_spec(
cls,
Expand Down
26 changes: 17 additions & 9 deletions openapi_core/validation/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,30 @@ def __call__(self, view):
def decorated(*args, **kwargs):
request = self._get_request(*args, **kwargs)
openapi_request = self._get_openapi_request(request)
errors = self.process_request(openapi_request)
if errors:
return self._handle_openapi_errors(errors)
response = view(*args, **kwargs)
request_result = self.process_request(openapi_request)
if request_result.errors:
return self._handle_request_errors(request_result)
response = self._handle_request_view(
request_result, view, *args, **kwargs)
openapi_response = self._get_openapi_response(response)
errors = self.process_response(openapi_request, openapi_response)
if errors:
return self._handle_openapi_errors(errors)
response_result = self.process_response(
openapi_request, openapi_response)
if response_result.errors:
return self._handle_response_errors(response_result)
return response
return decorated

def _get_request(self, *args, **kwargs):
return self.request_provider.provide(*args, **kwargs)

def _handle_openapi_errors(self, errors):
return self.openapi_errors_handler.handle(errors)
def _handle_request_view(self, request_result, view, *args, **kwargs):
return view(*args, **kwargs)

def _handle_request_errors(self, request_result):
return self.openapi_errors_handler.handle(request_result.errors)

def _handle_response_errors(self, response_result):
return self.openapi_errors_handler.handle(response_result.errors)

def _get_openapi_request(self, request):
return self.request_factory.create(request)
Expand Down
21 changes: 2 additions & 19 deletions openapi_core/validation/processors.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
"""OpenAPI core validation processors module"""
from openapi_core.schema.servers.exceptions import InvalidServer
from openapi_core.schema.exceptions import OpenAPIMappingError


class OpenAPIProcessor(object):
Expand All @@ -10,22 +8,7 @@ def __init__(self, request_validator, response_validator):
self.response_validator = response_validator

def process_request(self, request):
request_result = self.request_validator.validate(request)
try:
request_result.raise_for_errors()
# return instantly on server error
except InvalidServer as exc:
return [exc, ]
except OpenAPIMappingError:
return request_result.errors
else:
return
return self.request_validator.validate(request)

def process_response(self, request, response):
response_result = self.response_validator.validate(request, response)
try:
response_result.raise_for_errors()
except OpenAPIMappingError:
return response_result.errors
else:
return
return self.response_validator.validate(request, response)
36 changes: 29 additions & 7 deletions tests/integration/contrib/flask/test_flask_decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@

from openapi_core.contrib.flask.decorators import FlaskOpenAPIViewDecorator
from openapi_core.shortcuts import create_spec
from openapi_core.validation.request.datatypes import RequestParameters


class TestFlaskOpenAPIDecorator(object):

view_response = None
view_response_callable = None

@pytest.fixture
def spec(self, factory):
Expand All @@ -31,17 +32,30 @@ def client(self, app):
with app.app_context():
yield client

@pytest.fixture
def view_response(self):
def view_response(*args, **kwargs):
return self.view_response_callable(*args, **kwargs)
return view_response

@pytest.fixture(autouse=True)
def view(self, app, decorator):
def view(self, app, decorator, view_response):
@app.route("/browse/<id>/")
@decorator
def browse_details(id):
return self.view_response
def browse_details(*args, **kwargs):
return view_response(*args, **kwargs)
return browse_details

def test_invalid_content_type(self, client):
self.view_response = make_response('success', 200)

def view_response_callable(*args, **kwargs):
from flask.globals import request
assert request.openapi
assert not request.openapi.errors
assert request.openapi.parameters == RequestParameters(path={
'id': 12,
})
return make_response('success', 200)
self.view_response_callable = view_response_callable
result = client.get('/browse/12/')

assert result.json == {
Expand Down Expand Up @@ -101,7 +115,15 @@ def test_endpoint_error(self, client):
assert result.json == expected_data

def test_valid(self, client):
self.view_response = jsonify(data='data')
def view_response_callable(*args, **kwargs):
from flask.globals import request
assert request.openapi
assert not request.openapi.errors
assert request.openapi.parameters == RequestParameters(path={
'id': 12,
})
return jsonify(data='data')
self.view_response_callable = view_response_callable

result = client.get('/browse/12/')

Expand Down