Skip to content

Commit

Permalink
Merge a54d69f into dcb6ded
Browse files Browse the repository at this point in the history
  • Loading branch information
lafrech committed Mar 29, 2019
2 parents dcb6ded + a54d69f commit d24ee25
Show file tree
Hide file tree
Showing 10 changed files with 56 additions and 41 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ Changelog
Features:

- Official Python 3.7 support (:pr:`45`).
- Rename ``Api.definition`` as ``Api.schema``. Keep ``Api.definition`` as an
alias to ``Api.schema`` for backward compatibility.

0.14.0 (2019-03-08)
+++++++++++++++++++
Expand Down
1 change: 1 addition & 0 deletions docs/api_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Api
.. autoclass:: flask_rest_api.Api
:members:

.. automethod:: schema
.. automethod:: definition
.. automethod:: register_converter
.. automethod:: register_field
Expand Down
9 changes: 4 additions & 5 deletions docs/openapi.rst
Original file line number Diff line number Diff line change
Expand Up @@ -117,17 +117,16 @@ Register Definitions
--------------------

When a schema is used multiple times throughout the spec, it is better to
add it to the spec's definitions so as to reference it rather than duplicate
its content.
add it to the spec's schema components so as to reference it rather than
duplicate its content.

To register a definition from a schema, use the :meth:`Api.definition`
decorator:
To register a schema, use the :meth:`Api.schema` decorator:

.. code-block:: python
api = Api()
@api.definition('Pet')
@api.schema('Pet')
class Pet(Schema):
...
Expand Down
2 changes: 1 addition & 1 deletion docs/quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Define a marshmallow :class:`Schema <marshmallow.Schema>` to expose the model.

.. code-block:: python
@api.definition('Pet')
@api.schema('Pet')
class PetSchema(ma.Schema):
class Meta:
Expand Down
2 changes: 1 addition & 1 deletion flask_rest_api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def __init__(self, app=None, *, spec_kwargs=None):
self._app = app
self.spec = None
# Use lists to enforce order
self._definitions = []
self._schemas = []
self._fields = []
self._converters = []
if app is not None:
Expand Down
8 changes: 4 additions & 4 deletions flask_rest_api/blueprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
- At initialization time
- Schema instances are replaced either by their reference in the `definition`
- Schema instances are replaced either by their reference in the `schemas`
section of the spec if applicable, otherwise by their json representation.
- Automatic documentation is adapted to OpenAPI version and deep-merged with
Expand Down Expand Up @@ -146,10 +146,10 @@ def register_views_in_doc(self, app, spec):
"""Register views information in documentation
If a schema in a parameter or a response appears in the spec
`definitions` section, it is replaced by a reference to its definition
in the parameter or response documentation:
`schemas` section, it is replaced by a reference in the parameter or
response documentation:
"schema":{"$ref": "#/definitions/MySchema"}
"schema":{"$ref": "#/components/schemas/MySchema"}
"""
# This method uses the documentation information associated with each
# endpoint in self._[auto|manual]_docs to provide documentation for
Expand Down
24 changes: 14 additions & 10 deletions flask_rest_api/spec/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,35 +188,39 @@ def _init_spec(
# Register custom fields in spec
for args in self._fields:
self._register_field(*args)
# Register schema definitions in spec
for name, schema_cls, kwargs in self._definitions:
# Register schemas in spec
for name, schema_cls, kwargs in self._schemas:
self.spec.components.schema(name, schema=schema_cls, **kwargs)
# Register custom converters in spec
for args in self._converters:
self._register_converter(*args)

def definition(self, name):
def schema(self, name):
"""Decorator to register a Schema in the doc
This allows a schema to be defined once in the `definitions`
This allows a schema to be defined once in the `schemas`
section of the spec and be referenced throughout the spec.
:param str name: Name of the definition in the spec
:param str name: Name of the schema in the spec
Example: ::
@api.definition('Pet')
@api.schema('Pet')
class PetSchema(Schema):
...
"""
def decorator(schema_cls, **kwargs):
self._definitions.append((name, schema_cls, kwargs))
# Register definition in spec if app is already initialized
self._schemas.append((name, schema_cls, kwargs))
# Register schema in spec if app is already initialized
if self.spec is not None:
self.spec.components.schema(name, schema=schema_cls, **kwargs)
return schema_cls
return decorator

def definition(self, name):
"""Alias to :meth:`schema <Api.schema>`"""
return self.schema(name)

def register_converter(self, converter, conv_type, conv_format=None):
"""Register custom path parameter converter
Expand Down Expand Up @@ -276,8 +280,8 @@ def register_field(self, field, *args):
# Map to ('integer, 'int32') passing a code marshmallow field
api.register_field(CustomInteger, ma.fields.Integer)
Should be called before registering definitions with
:meth:`definition <Api.definition>`.
Should be called before registering schemas with
:meth:`schema <Api.schema>`.
"""
self._fields.append((field, *args))
# Register field in spec if app is already initialized
Expand Down
39 changes: 24 additions & 15 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,22 @@
from flask_rest_api import Api, Blueprint
from flask_rest_api.exceptions import OpenAPIVersionNotSpecified

from .utils import get_definitions
from .utils import get_schemas


class TestApi():
"""Test Api class"""

def test_api_schema(self, app, schemas):
DocSchema = schemas.DocSchema
api = Api(app)
with mock.patch.object(apispec.core.Components, 'schema') as mock_def:
ret = api.schema('Document')(DocSchema)
assert ret is DocSchema
mock_def.assert_called_once_with('Document', schema=DocSchema)

def test_api_definition(self, app, schemas):
"""Compatibility: definition is an alias for schema"""
DocSchema = schemas.DocSchema
api = Api(app)
with mock.patch.object(apispec.core.Components, 'schema') as mock_def:
Expand All @@ -28,22 +37,22 @@ def test_api_definition(self, app, schemas):
mock_def.assert_called_once_with('Document', schema=DocSchema)

@pytest.mark.parametrize('openapi_version', ['2.0', '3.0.2'])
def test_api_definition_before_and_after_init(self, app, openapi_version):
def test_api_schema_before_and_after_init(self, app, openapi_version):
app.config['OPENAPI_VERSION'] = openapi_version
api = Api()

@api.definition('Schema_1')
@api.schema('Schema_1')
class Schema_1(ma.Schema):
int_1 = ma.fields.Int()

api.init_app(app)

@api.definition('Schema_2')
@api.schema('Schema_2')
class Schema_2(ma.Schema):
int_2 = ma.fields.Int()

definitions = get_definitions(api.spec)
assert {'Schema_1', 'Schema_2'}.issubset(definitions)
schema_defs = get_schemas(api.spec)
assert {'Schema_1', 'Schema_2'}.issubset(schema_defs)

@pytest.mark.parametrize('openapi_version', ['2.0', '3.0.2'])
@pytest.mark.parametrize('view_type', ['function', 'method'])
Expand Down Expand Up @@ -133,7 +142,7 @@ class CustomField(ma.fields.Field):

api.register_field(CustomField, *mapping)

@api.definition('Document')
@api.schema('Document')
class Document(ma.Schema):
field = CustomField()

Expand All @@ -145,7 +154,7 @@ class Document(ma.Schema):
else:
properties = {'field': {'type': 'integer', 'format': 'int32'}}

assert get_definitions(api.spec)['Document'] == {
assert get_schemas(api.spec)['Document'] == {
'properties': properties, 'type': 'object'}

@pytest.mark.parametrize('openapi_version', ['2.0', '3.0.2'])
Expand All @@ -162,23 +171,23 @@ class CustomField_2(ma.fields.Field):

api.register_field(CustomField_1, 'custom string', 'custom')

@api.definition('Schema_1')
@api.schema('Schema_1')
class Schema_1(ma.Schema):
int_1 = ma.fields.Int()
custom_1 = CustomField_1()

api.init_app(app)
api.register_field(CustomField_2, 'custom string', 'custom')

@api.definition('Schema_2')
@api.schema('Schema_2')
class Schema_2(ma.Schema):
int_2 = ma.fields.Int()
custom_2 = CustomField_2()

definitions = get_definitions(api.spec)
assert definitions['Schema_1']['properties']['custom_1'] == {
schema_defs = get_schemas(api.spec)
assert schema_defs['Schema_1']['properties']['custom_1'] == {
'type': 'custom string', 'format': 'custom'}
assert definitions['Schema_2']['properties']['custom_2'] == {
assert schema_defs['Schema_2']['properties']['custom_2'] == {
'type': 'custom string', 'format': 'custom'}

def test_api_extra_spec_kwargs(self, app):
Expand All @@ -200,8 +209,8 @@ def schema_helper(self, name, definition, **kwargs):
return {'dummy': 'whatever'}

api = Api(app, spec_kwargs={'extra_plugins': (MyPlugin(), )})
api.definition('Pet')(schemas.DocSchema)
assert get_definitions(api.spec)['Pet']['dummy'] == 'whatever'
api.schema('Pet')(schemas.DocSchema)
assert get_schemas(api.spec)['Pet']['dummy'] == 'whatever'

@pytest.mark.parametrize('openapi_version', ['2.0', '3.0.2'])
def test_api_gets_apispec_parameters_from_app(self, app, openapi_version):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_blueprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ def test_blueprint_response_schema(self, app, openapi_version, schemas):
api = Api(app)
blp = Blueprint('test', 'test', url_prefix='/test')

api.definition('Doc')(schemas.DocSchema)
api.schema('Doc')(schemas.DocSchema)

@blp.route('/schema_many_false')
@blp.response(schemas.DocSchema(many=False))
Expand Down
8 changes: 4 additions & 4 deletions tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ def __exit__(self, et, ev, tb):
self.app.logger.disabled = self.logger_was_disabled


def get_definitions(spec):
"""Get definitions/schemas from spec"""
def get_schemas(spec):
"""Get schema components from spec"""
if spec.openapi_version.major < 3:
return spec.to_dict()['definitions']
return spec.to_dict()['components']['schemas']
return spec.to_dict().get('definitions')
return spec.to_dict()['components'].get('schemas')

0 comments on commit d24ee25

Please sign in to comment.