Skip to content
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

Make OpenAPIConverter class a class member of MarshmallowPlugin #493

Merged
merged 4 commits into from Sep 3, 2019
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/using_plugins.rst
Expand Up @@ -288,7 +288,7 @@ method. Continuing from the example above:
return ret


ma_plugin.openapi.add_attribute_function(my_custom_field2properties)
ma_plugin.converter.add_attribute_function(my_custom_field2properties)

Next Steps
----------
Expand Down
27 changes: 14 additions & 13 deletions src/apispec/ext/marshmallow/__init__.py
Expand Up @@ -54,8 +54,7 @@ class UserSchema(Schema):


def resolver(schema):
"""Default implementation of a schema name resolver function
"""
"""Default schema name resolver function that strips 'Schema' from the end of the class name."""
schema_cls = resolve_schema_cls(schema)
name = schema_cls.__name__
if name.endswith("Schema"):
Expand All @@ -64,7 +63,7 @@ def resolver(schema):


class MarshmallowPlugin(BasePlugin):
"""APISpec plugin handling marshmallow schemas
"""APISpec plugin for translating marshmallow schemas to OpenAPI/JSONSchema format.

:param callable schema_name_resolver: Callable to generate the schema definition name.
Receives the `Schema` class and returns the name to be used in refs within
Expand All @@ -80,18 +79,20 @@ def schema_name_resolver(schema):
return schema_cls.__name__
"""

Converter = OpenAPIConverter

def __init__(self, schema_name_resolver=None):
super().__init__()
self.schema_name_resolver = schema_name_resolver or resolver
self.spec = None
self.openapi_version = None
self.openapi = None
self.converter = None

def init_spec(self, spec):
super().init_spec(spec)
self.spec = spec
self.openapi_version = spec.openapi_version
self.openapi = OpenAPIConverter(
self.converter = self.Converter(
openapi_version=spec.openapi_version,
schema_name_resolver=self.schema_name_resolver,
spec=spec,
Expand All @@ -106,7 +107,7 @@ def resolve_parameters(self, parameters):
and "in" in parameter
):
schema_instance = resolve_schema_instance(parameter.pop("schema"))
resolved += self.openapi.schema2parameters(
resolved += self.converter.schema2parameters(
schema_instance, default_in=parameter.pop("in"), **parameter
)
else:
Expand All @@ -121,7 +122,7 @@ def resolve_schema_in_request_body(self, request_body):
content = request_body["content"]
for content_type in content:
schema = content[content_type]["schema"]
content[content_type]["schema"] = self.openapi.resolve_schema_dict(schema)
content[content_type]["schema"] = self.converter.resolve_schema_dict(schema)

def resolve_schema(self, data):
"""Function to resolve a schema in a parameter or response - modifies the
Expand All @@ -136,13 +137,13 @@ def resolve_schema(self, data):

# OAS 2 component or OAS 3 header
if "schema" in data:
data["schema"] = self.openapi.resolve_schema_dict(data["schema"])
data["schema"] = self.converter.resolve_schema_dict(data["schema"])
# OAS 3 component except header
if self.openapi_version.major >= 3:
if "content" in data:
for content in data["content"].values():
if "schema" in content:
content["schema"] = self.openapi.resolve_schema_dict(
content["schema"] = self.converter.resolve_schema_dict(
content["schema"]
)

Expand All @@ -164,7 +165,7 @@ class MyCustomField(Integer):
class MyCustomFieldThatsKindaLikeAnInteger(Integer):
# ...
"""
return self.openapi.map_to_openapi_type(*args)
return self.converter.map_to_openapi_type(*args)

def schema_helper(self, name, _, schema=None, **kwargs):
"""Definition helper that allows using a marshmallow
Expand All @@ -180,9 +181,9 @@ def schema_helper(self, name, _, schema=None, **kwargs):

schema_key = make_schema_key(schema_instance)
self.warn_if_schema_already_in_spec(schema_key)
self.openapi.refs[schema_key] = name
self.converter.refs[schema_key] = name

json_schema = self.openapi.schema2jsonschema(schema_instance)
json_schema = self.converter.schema2jsonschema(schema_instance)

return json_schema

Expand Down Expand Up @@ -231,7 +232,7 @@ def warn_if_schema_already_in_spec(self, schema_key):
"""Method to warn the user if the schema has already been added to the
spec.
"""
if schema_key in self.openapi.refs:
if schema_key in self.converter.refs:
warnings.warn(
"{} has already been added to the spec. Adding it twice may "
"cause references to not resolve properly.".format(schema_key[0]),
Expand Down
15 changes: 7 additions & 8 deletions src/apispec/ext/marshmallow/common.py
Expand Up @@ -11,7 +11,7 @@


def resolve_schema_instance(schema):
"""Return schema instance for given schema (instance or class)
"""Return schema instance for given schema (instance or class).

:param type|Schema|str schema: instance, class or class name of marshmallow.Schema
:return: schema instance of given schema (instance or class)
Expand All @@ -25,12 +25,12 @@ def resolve_schema_instance(schema):
except marshmallow.exceptions.RegistryError:
raise ValueError(
"{!r} is not a marshmallow.Schema subclass or instance and has not"
" been registered in the Marshmallow class registry.".format(schema)
" been registered in the marshmallow class registry.".format(schema)
)


def resolve_schema_cls(schema):
"""Return schema class for given schema (instance or class)
"""Return schema class for given schema (instance or class).

:param type|Schema|str: instance, class or class name of marshmallow.Schema
:return: schema class of given schema (instance or class)
Expand All @@ -44,12 +44,12 @@ def resolve_schema_cls(schema):
except marshmallow.exceptions.RegistryError:
raise ValueError(
"{!r} is not a marshmallow.Schema subclass or instance and has not"
" been registered in the Marshmallow class registry.".format(schema)
" been registered in the marshmallow class registry.".format(schema)
)


def get_fields(schema, exclude_dump_only=False):
"""Return fields from schema
"""Return fields from schema.

:param Schema schema: A marshmallow Schema instance or a class object
:param bool exclude_dump_only: whether to filter fields in Meta.dump_only
Expand All @@ -69,8 +69,7 @@ def get_fields(schema, exclude_dump_only=False):


def warn_if_fields_defined_in_meta(fields, Meta):
"""Warns user that fields defined in Meta.fields or Meta.additional will
be ignored
"""Warns user that fields defined in Meta.fields or Meta.additional will be ignored.

:param dict fields: A dictionary of fields name field object pairs
:param Meta: the schema's Meta class
Expand All @@ -88,7 +87,7 @@ def warn_if_fields_defined_in_meta(fields, Meta):


def filter_excluded_fields(fields, Meta, exclude_dump_only):
"""Filter fields that should be ignored in the OpenAPI spec
"""Filter fields that should be ignored in the OpenAPI spec.

:param dict fields: A dictionary of fields name field object pairs
:param Meta: the schema's Meta class
Expand Down
22 changes: 9 additions & 13 deletions src/apispec/ext/marshmallow/field_converter.py
Expand Up @@ -83,7 +83,7 @@


class FieldConverterMixin:
"""Mixin class to convert fields to an OpenAPI property"""
"""Adds methods for converting marshmallow fields to an OpenAPI properties."""

field_mapping = DEFAULT_FIELD_MAPPING

Expand Down Expand Up @@ -128,7 +128,7 @@ def inner(field_type):
def add_attribute_function(self, func):
"""Method to add an attribute function to the list of attribute functions
that will be called on a field to convert it from a field to an OpenAPI
property
property.

:param func func: the attribute function to add - will be called for each
field in a schema with a `field <marshmallow.fields.Field>` instance
Expand Down Expand Up @@ -163,8 +163,7 @@ def field2property(self, field):
return ret

def field2type_and_format(self, field, **kwargs):
"""Return the dictionary of OpenAPI type and format based on the field
type
"""Return the dictionary of OpenAPI type and format based on the field type.

:param Field field: A marshmallow field.
:rtype: dict
Expand Down Expand Up @@ -192,7 +191,7 @@ def field2type_and_format(self, field, **kwargs):
return ret

def field2default(self, field, **kwargs):
"""Return the dictionary containing the field's default value
"""Return the dictionary containing the field's default value.

Will first look for a `doc_default` key in the field's metadata and then
fall back on the field's `missing` parameter. A callable passed to the
Expand All @@ -212,7 +211,7 @@ def field2default(self, field, **kwargs):
return ret

def field2choices(self, field, **kwargs):
"""Return the dictionary of OpenAPI field attributes for valid choices definition
"""Return the dictionary of OpenAPI field attributes for valid choices definition.

:param Field field: A marshmallow field.
:rtype: dict
Expand Down Expand Up @@ -371,7 +370,7 @@ def field2pattern(self, field, **kwargs):
return attributes

def metadata2properties(self, field, **kwargs):
"""Return a dictionary of properties extracted from field Metadata
"""Return a dictionary of properties extracted from field metadata.

Will include field metadata that are valid properties of `OpenAPI schema
objects
Expand Down Expand Up @@ -402,8 +401,7 @@ def metadata2properties(self, field, **kwargs):
return ret

def nested2properties(self, field, ret):
"""Return a dictionary of properties from :class:`Nested <marshmallow.fields.Nested`
fields
"""Return a dictionary of properties from :class:`Nested <marshmallow.fields.Nested` fields.

Typically provides a reference object and will add the schema to the spec
if it is not already present
Expand All @@ -423,8 +421,7 @@ def nested2properties(self, field, ret):
return ret

def list2properties(self, field, **kwargs):
"""Return a dictionary of properties from :class:`List <marshmallow.fields.List`
fields
"""Return a dictionary of properties from :class:`List <marshmallow.fields.List>` fields.

Will provide an `items` property based on the field's `inner` attribute

Expand All @@ -440,8 +437,7 @@ def list2properties(self, field, **kwargs):
return ret

def dict2properties(self, field, **kwargs):
"""Return a dictionary of properties from :class:`Dict <marshmallow.fields.Dict`
fields
"""Return a dictionary of properties from :class:`Dict <marshmallow.fields.Dict>` fields.

Only applicable for Marshmallow versions greater than 3. Will provide an
`additionalProperties` property based on the field's `value_field` attribute
Expand Down
26 changes: 2 additions & 24 deletions src/apispec/ext/marshmallow/openapi.py
Expand Up @@ -39,7 +39,7 @@


class OpenAPIConverter(FieldConverterMixin):
"""Converter generating OpenAPI specification from Marshmallow schemas and fields
"""Adds methods for generating OpenAPI specification from marshmallow schemas and fields.

:param str|OpenAPIVersion openapi_version: The OpenAPI version to use.
Should be in the form '2.x' or '3.x.x' to comply with the OpenAPI standard.
Expand Down Expand Up @@ -75,7 +75,7 @@ def _observed_name(field, name):
return field.data_key or name

def resolve_nested_schema(self, schema):
"""Return the Open API representation of a marshmallow Schema.
"""Return the OpenAPI representation of a marshmallow Schema.

Adds the schema to the spec if it isn't already present.

Expand Down Expand Up @@ -257,28 +257,6 @@ def schema2jsonschema(self, schema):

https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#schemaObject

Example: ::

class UserSchema(Schema):
_id = fields.Int()
email = fields.Email(description='email address of the user')
name = fields.Str()

class Meta:
title = 'User'
description = 'A registered user'

oaic = OpenAPIConverter(openapi_version='3.0.2', schema_name_resolver=resolver, spec=spec)
pprint(oaic.schema2jsonschema(UserSchema))
# {'description': 'A registered user',
# 'properties': {'_id': {'format': 'int32', 'type': 'integer'},
# 'email': {'description': 'email address of the user',
# 'format': 'email',
# 'type': 'string'},
# 'name': {'type': 'string'}},
# 'title': 'User',
# 'type': 'object'}

:param Schema schema: A marshmallow Schema instance
:rtype: dict, a JSON Schema Object
"""
Expand Down
2 changes: 1 addition & 1 deletion tests/conftest.py
Expand Up @@ -15,7 +15,7 @@ def make_spec(openapi_version):
plugins=(ma_plugin,),
)
return namedtuple("Spec", ("spec", "marshmallow_plugin", "openapi"))(
spec, ma_plugin, ma_plugin.openapi
spec, ma_plugin, ma_plugin.converter
)


Expand Down
4 changes: 2 additions & 2 deletions tests/test_ext_marshmallow_field.py
Expand Up @@ -284,6 +284,6 @@ def custom_string2properties(field, **kwargs):
APISpec(
title="Validation", version="0.1", openapi_version="3.0.0", plugins=(ma_plugin,)
)
ma_plugin.openapi.add_attribute_function(custom_string2properties)
properties = ma_plugin.openapi.field2property(CustomStringField())
ma_plugin.converter.add_attribute_function(custom_string2properties)
properties = ma_plugin.converter.field2property(CustomStringField())
assert properties["x-customString"]
4 changes: 2 additions & 2 deletions tests/test_ext_marshmallow_openapi.py
Expand Up @@ -467,7 +467,7 @@ def test_openapi_tools_validate_v2():
spec = APISpec(
title="Pets", version="0.1", plugins=(ma_plugin,), openapi_version="2.0"
)
openapi = ma_plugin.openapi
openapi = ma_plugin.converter

spec.components.schema("Category", schema=CategorySchema)
spec.components.schema("Pet", {"discriminator": "name"}, schema=PetSchema)
Expand Down Expand Up @@ -524,7 +524,7 @@ def test_openapi_tools_validate_v3():
spec = APISpec(
title="Pets", version="0.1", plugins=(ma_plugin,), openapi_version="3.0.0"
)
openapi = ma_plugin.openapi
openapi = ma_plugin.converter

spec.components.schema("Category", schema=CategorySchema)
spec.components.schema("Pet", schema=PetSchema)
Expand Down