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

Add components.example tests #516

Merged
merged 2 commits into from
Nov 4, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 37 additions & 37 deletions src/apispec/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,17 @@ def __init__(self, plugins, openapi_version):
self._plugins = plugins
self.openapi_version = openapi_version
self._schemas = {}
self._examples = {}
self._parameters = {}
self._responses = {}
self._parameters = {}
self._examples = {}
self._security_schemes = {}

def to_dict(self):
subsections = {
"example": self._examples,
"schema": self._schemas,
"parameter": self._parameters,
"response": self._responses,
"parameter": self._parameters,
"example": self._examples,
"security_scheme": self._security_schemes,
}
return {
Expand All @@ -49,21 +49,6 @@ def to_dict(self):
if v != {}
}

def example(self, name, component, **kwargs):
"""Add an example which can be referenced

:param str name: identifier by which example may be referenced.
:param dict component: example fields.

https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#exampleObject
"""
if name in self._examples:
raise DuplicateComponentNameError(
'Another example with name "{}" is already registered.'.format(name)
)
self._examples[name] = component
return self

def schema(self, name, component=None, **kwargs):
"""Add a new schema to the spec.

Expand Down Expand Up @@ -100,6 +85,30 @@ def schema(self, name, component=None, **kwargs):
self._schemas[name] = ret
return self

def response(self, component_id, component=None, **kwargs):
"""Add a response which can be referenced.

:param str component_id: ref_id to use as reference
:param dict component: response fields
:param dict kwargs: plugin-specific arguments
"""
if component_id in self._responses:
raise DuplicateComponentNameError(
'Another response with name "{}" is already registered.'.format(
component_id
)
)
component = component or {}
ret = component.copy()
# Execute all helpers from plugins
for plugin in self._plugins:
try:
ret.update(plugin.response_helper(component, **kwargs) or {})
except PluginMethodNotImplementedError:
continue
self._responses[component_id] = ret
return self

def parameter(self, component_id, location, component=None, **kwargs):
""" Add a parameter which can be referenced.

Expand Down Expand Up @@ -132,28 +141,19 @@ def parameter(self, component_id, location, component=None, **kwargs):
self._parameters[component_id] = ret
return self

def response(self, component_id, component=None, **kwargs):
"""Add a response which can be referenced.
def example(self, name, component, **kwargs):
"""Add an example which can be referenced

:param str component_id: ref_id to use as reference
:param dict component: response fields
:param dict kwargs: plugin-specific arguments
:param str name: identifier by which example may be referenced.
:param dict component: example fields.

https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#exampleObject
"""
if component_id in self._responses:
if name in self._examples:
raise DuplicateComponentNameError(
'Another response with name "{}" is already registered.'.format(
component_id
)
'Another example with name "{}" is already registered.'.format(name)
)
component = component or {}
ret = component.copy()
# Execute all helpers from plugins
for plugin in self._plugins:
try:
ret.update(plugin.response_helper(component, **kwargs) or {})
except PluginMethodNotImplementedError:
continue
self._responses[component_id] = ret
self._examples[name] = component
return self

def security_scheme(self, component_id, component):
Expand Down
16 changes: 8 additions & 8 deletions src/apispec/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,6 @@ def schema_helper(self, name, definition, **kwargs):
"""
raise PluginMethodNotImplementedError

def parameter_helper(self, parameter, **kwargs):
"""May return parameter component description as a dict.

:param dict parameter: Parameter fields
:param dict kwargs: All additional keywords arguments sent to `APISpec.parameter()`
"""
raise PluginMethodNotImplementedError

def response_helper(self, response, **kwargs):
"""May return response component description as a dict.

Expand All @@ -38,6 +30,14 @@ def response_helper(self, response, **kwargs):
"""
raise PluginMethodNotImplementedError

def parameter_helper(self, parameter, **kwargs):
"""May return parameter component description as a dict.

:param dict parameter: Parameter fields
:param dict kwargs: All additional keywords arguments sent to `APISpec.parameter()`
"""
raise PluginMethodNotImplementedError

def path_helper(self, path=None, operations=None, parameters=None, **kwargs):
"""May return a path as string and mutate operations dict and parameters list.

Expand Down
6 changes: 3 additions & 3 deletions src/apispec/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@
COMPONENT_SUBSECTIONS = {
2: {
"schema": "definitions",
"parameter": "parameters",
"response": "responses",
"parameter": "parameters",
"security_scheme": "securityDefinitions",
},
3: {
"example": "examples",
"schema": "schemas",
"parameter": "parameters",
"response": "responses",
"parameter": "parameters",
"example": "examples",
"security_scheme": "securitySchemes",
},
}
Expand Down
82 changes: 49 additions & 33 deletions tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,47 +138,40 @@ class TestComponents:
"name": {"type": "string", "example": "doggie"},
}

# Referenced examples are only supported in OAS 3.x
@pytest.mark.parametrize("spec", ("3.0.0",), indirect=True)
def test_example(self, spec):
spec.components.example("PetExample", {"value": {"a": "b"}})
defs = get_examples(spec)
assert defs["PetExample"]["value"] == {"a": "b"}

def test_schema(self, spec):
spec.components.schema("Pet", {"properties": self.properties})
defs = get_schemas(spec)
assert "Pet" in defs
assert defs["Pet"]["properties"] == self.properties
schemas = get_schemas(spec)
assert "Pet" in schemas
assert schemas["Pet"]["properties"] == self.properties

def test_schema_is_chainable(self, spec):
spec.components.schema("Pet", {"properties": {}}).schema(
"Plant", {"properties": {}}
)
defs = get_schemas(spec)
assert "Pet" in defs
assert "Plant" in defs
schemas = get_schemas(spec)
assert "Pet" in schemas
assert "Plant" in schemas

def test_schema_description(self, spec):
model_description = "An animal which lives with humans."
spec.components.schema(
"Pet", {"properties": self.properties, "description": model_description}
)
defs = get_schemas(spec)
assert defs["Pet"]["description"] == model_description
schemas = get_schemas(spec)
assert schemas["Pet"]["description"] == model_description

def test_schema_stores_enum(self, spec):
enum = ["name", "photoUrls"]
spec.components.schema("Pet", {"properties": self.properties, "enum": enum})
defs = get_schemas(spec)
assert defs["Pet"]["enum"] == enum
schemas = get_schemas(spec)
assert schemas["Pet"]["enum"] == enum

def test_schema_discriminator(self, spec):
spec.components.schema(
"Pet", {"properties": self.properties, "discriminator": "name"}
)
defs = get_schemas(spec)
assert defs["Pet"]["discriminator"] == "name"
schemas = get_schemas(spec)
assert schemas["Pet"]["discriminator"] == "name"

def test_schema_duplicate_name(self, spec):
spec.components.schema("Pet", {"properties": self.properties})
Expand All @@ -188,6 +181,26 @@ def test_schema_duplicate_name(self, spec):
):
spec.components.schema("Pet", properties=self.properties)

def test_response(self, spec):
response = {"description": "Pet not found"}
spec.components.response("NotFound", response)
responses = get_responses(spec)
assert responses["NotFound"] == response

def test_response_is_chainable(self, spec):
spec.components.response("resp1").response("resp2")
responses = get_responses(spec)
assert "resp1" in responses
assert "resp2" in responses

def test_response_duplicate_name(self, spec):
spec.components.response("test_response")
with pytest.raises(
DuplicateComponentNameError,
match='Another response with name "test_response" is already registered.',
):
spec.components.response("test_response")

def test_parameter(self, spec):
parameter = {"format": "int64", "type": "integer"}
spec.components.parameter("PetId", "path", parameter)
Expand All @@ -214,25 +227,28 @@ def test_parameter_duplicate_name(self, spec):
):
spec.components.parameter("test_parameter", "path")

def test_response(self, spec):
response = {"description": "Pet not found"}
spec.components.response("NotFound", response)
responses = get_responses(spec)
assert responses["NotFound"] == response
# Referenced examples are only supported in OAS 3.x
@pytest.mark.parametrize("spec", ("3.0.0",), indirect=True)
def test_example(self, spec):
spec.components.example("test_example", {"value": {"a": "b"}})
examples = get_examples(spec)
assert examples["test_example"]["value"] == {"a": "b"}

def test_response_is_chainable(self, spec):
spec.components.response("resp1").response("resp2")
responses = get_responses(spec)
assert "resp1" in responses
assert "resp2" in responses
@pytest.mark.parametrize("spec", ("3.0.0",), indirect=True)
def test_example_is_chainable(self, spec):
spec.components.example("test_example_1", {}).example("test_example_2", {})
examples = get_examples(spec)
assert "test_example_1" in examples
assert "test_example_2" in examples

def test_response_duplicate_name(self, spec):
spec.components.response("test_response")
@pytest.mark.parametrize("spec", ("3.0.0",), indirect=True)
def test_example_duplicate_name(self, spec):
spec.components.example("test_example", {})
with pytest.raises(
DuplicateComponentNameError,
match='Another response with name "test_response" is already registered.',
match='Another example with name "test_example" is already registered.',
):
spec.components.response("test_response")
spec.components.example("test_example", {})

def test_security_scheme(self, spec):
sec_scheme = {"type": "apiKey", "in": "header", "name": "X-API-Key"}
Expand Down
16 changes: 8 additions & 8 deletions tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,26 @@
from apispec.utils import build_reference


def get_examples(spec):
return spec.to_dict()["components"]["examples"]


def get_schemas(spec):
if spec.openapi_version.major < 3:
return spec.to_dict()["definitions"]
return spec.to_dict()["components"]["schemas"]


def get_responses(spec):
if spec.openapi_version.major < 3:
return spec.to_dict()["responses"]
return spec.to_dict()["components"]["responses"]


def get_parameters(spec):
if spec.openapi_version.major < 3:
return spec.to_dict()["parameters"]
return spec.to_dict()["components"]["parameters"]


def get_responses(spec):
if spec.openapi_version.major < 3:
return spec.to_dict()["responses"]
return spec.to_dict()["components"]["responses"]
def get_examples(spec):
return spec.to_dict()["components"]["examples"]


def get_security_schemes(spec):
Expand Down