Skip to content

Commit

Permalink
Merge 3fc697a into 3b33d22
Browse files Browse the repository at this point in the history
  • Loading branch information
lafrech committed May 2, 2019
2 parents 3b33d22 + 3fc697a commit b4256ff
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 7 deletions.
17 changes: 12 additions & 5 deletions flask_rest_api/blueprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,13 +184,20 @@ def _prepare_doc(operation, openapi_version):
versions: the OpenAPI version is not known when the decorators are
applied but only at registration time when this method is called.
"""
if openapi_version.major >= 3:
if openapi_version.major < 3:
if 'responses' in operation:
for resp in operation['responses'].values():
if 'schema' in resp:
resp['content'] = {
'application/json': {
'schema': resp.pop('schema')}}
if 'example' in resp:
resp['examples'] = {
'application/json': resp.pop('example')}
else:
if 'responses' in operation:
for resp in operation['responses'].values():
for field in ('schema', 'example', 'examples'):
if field in resp:
resp.setdefault('content', {})
resp['content'].setdefault('application/json', {})[
field] = resp.pop(field)
if 'parameters' in operation:
for param in operation['parameters']:
if param['in'] == 'body':
Expand Down
20 changes: 19 additions & 1 deletion flask_rest_api/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,20 @@
class ResponseMixin:
"""Extend Blueprint to add response handling"""

def response(self, schema=None, *, code=200, description=None):
def response(
self, schema=None, *, code=200, description=None,
example=None, examples=None, headers=None
):
"""Decorator generating an endpoint response
:param schema: :class:`Schema <marshmallow.Schema>` class or instance.
If not None, will be used to serialize response data.
:param int code: HTTP status code (default: 200). Used if none is
returned from the view function.
:param str description: Description of the response (default: None).
:param dict example: Example of response message.
:param list examples: Examples of response message.
:param dict headers: Headers returned by the response.
The decorated function is expected to return the same types of value
than a typical flask view function, except the body part may be an
Expand All @@ -32,6 +38,12 @@ def response(self, schema=None, *, code=200, description=None):
If the decorated function returns a ``Response`` object, the ``schema``
and ``code`` parameters are only used to document the resource.
The `example` and `examples` parameters are mutually exclusive. The
latter should only be used with OpenAPI 3.
The `example`, `examples` and `headers` parameters are only used to
document the resource.
See :doc:`Response <response>`.
"""
if isinstance(schema, type):
Expand All @@ -46,6 +58,12 @@ def decorator(func):
resp_doc['schema'] = doc_schema
if description is not None:
resp_doc['description'] = description
if example is not None:
resp_doc['example'] = example
if examples is not None:
resp_doc['examples'] = examples
if headers is not None:
resp_doc['headers'] = headers
doc = {'responses': {str(code): resp_doc}}
func._apidoc = deepupdate(getattr(func, '_apidoc', {}), doc)

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,6 @@
'flask>=1.0',
'marshmallow>=2.15.2',
'webargs>=1.5.2',
'apispec>=1.0.0',
'apispec>=1.3.2',
],
)
62 changes: 62 additions & 0 deletions tests/test_blueprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,68 @@ def func_2():
get_2 = api.spec.to_dict()['paths']['/test/route_2']['get']
assert get_2['responses']['200']['description'] == 'Test'

@pytest.mark.parametrize('openapi_version', ('2.0', '3.0.2'))
def test_blueprint_response_example(self, app, openapi_version):
app.config['OPENAPI_VERSION'] = openapi_version
api = Api(app)
blp = Blueprint('test', 'test', url_prefix='/test')

example = {'name': 'One'}

@blp.route('/')
@blp.response(example=example)
def func():
pass

api.register_blueprint(blp)

get = api.spec.to_dict()['paths']['/test/']['get']
if openapi_version == '2.0':
assert get['responses']['200']['examples'][
'application/json'] == example
else:
assert get['responses']['200']['content'][
'application/json']['example'] == example

# This is only relevant to OAS3.
@pytest.mark.parametrize('openapi_version', ('3.0.2', ))
def test_blueprint_response_examples(self, app, openapi_version):
app.config['OPENAPI_VERSION'] = openapi_version
api = Api(app)
blp = Blueprint('test', 'test', url_prefix='/test')

examples = {
'example 1': {'summary': 'Example 1', 'value': {'name': 'One'}},
'example 2': {'summary': 'Example 2', 'value': {'name': 'Two'}},
}

@blp.route('/')
@blp.response(examples=examples)
def func():
pass

api.register_blueprint(blp)

get = api.spec.to_dict()['paths']['/test/']['get']
assert get['responses']['200']['content']['application/json'][
'examples'] == examples

def test_blueprint_response_headers(self, app):
api = Api(app)
blp = Blueprint('test', 'test', url_prefix='/test')

headers = {'X-Header': {'description': 'Custom header'}}

@blp.route('/')
@blp.response(headers=headers)
def func():
pass

api.register_blueprint(blp)

get = api.spec.to_dict()['paths']['/test/']['get']
assert get['responses']['200']['headers'] == headers

@pytest.mark.parametrize('openapi_version', ('2.0', '3.0.2'))
def test_blueprint_pagination(self, app, schemas, openapi_version):
app.config['OPENAPI_VERSION'] = openapi_version
Expand Down

0 comments on commit b4256ff

Please sign in to comment.