oggit/packages/flask-restx/opengnsys-flask-restx-1.3.0/tests/test_swagger.py

3580 lines
109 KiB
Python

import pytest
from textwrap import dedent
from flask import url_for, Blueprint
from werkzeug.datastructures import FileStorage
import flask_restx as restx
from flask_restx import inputs
class SwaggerTest(object):
def test_specs_endpoint(self, api, client):
data = client.get_specs("")
assert data["swagger"] == "2.0"
assert data["basePath"] == "/"
assert data["produces"] == ["application/json"]
assert data["consumes"] == ["application/json"]
assert data["paths"] == {}
assert "info" in data
@pytest.mark.api(prefix="/api")
def test_specs_endpoint_with_prefix(self, api, client):
data = client.get_specs("/api")
assert data["swagger"] == "2.0"
assert data["basePath"] == "/api"
assert data["produces"] == ["application/json"]
assert data["consumes"] == ["application/json"]
assert data["paths"] == {}
assert "info" in data
def test_specs_endpoint_produces(self, api, client):
def output_xml(data, code, headers=None):
pass
api.representations["application/xml"] = output_xml
data = client.get_specs()
assert len(data["produces"]) == 2
assert "application/json" in data["produces"]
assert "application/xml" in data["produces"]
def test_specs_endpoint_info(self, app, client):
api = restx.Api(
version="1.0",
title="My API",
description="This is a testing API",
terms_url="http://somewhere.com/terms/",
contact="Support",
contact_url="http://support.somewhere.com",
contact_email="contact@somewhere.com",
license="Apache 2.0",
license_url="http://www.apache.org/licenses/LICENSE-2.0.html",
)
api.init_app(app)
data = client.get_specs()
assert data["swagger"] == "2.0"
assert data["basePath"] == "/"
assert data["produces"] == ["application/json"]
assert data["paths"] == {}
assert "info" in data
assert data["info"]["title"] == "My API"
assert data["info"]["version"] == "1.0"
assert data["info"]["description"] == "This is a testing API"
assert data["info"]["termsOfService"] == "http://somewhere.com/terms/"
assert data["info"]["contact"] == {
"name": "Support",
"url": "http://support.somewhere.com",
"email": "contact@somewhere.com",
}
assert data["info"]["license"] == {
"name": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html",
}
def test_specs_endpoint_info_delayed(self, app, client):
api = restx.Api(version="1.0")
api.init_app(
app,
title="My API",
description="This is a testing API",
terms_url="http://somewhere.com/terms/",
contact="Support",
contact_url="http://support.somewhere.com",
contact_email="contact@somewhere.com",
license="Apache 2.0",
license_url="http://www.apache.org/licenses/LICENSE-2.0.html",
)
data = client.get_specs()
assert data["swagger"] == "2.0"
assert data["basePath"] == "/"
assert data["produces"] == ["application/json"]
assert data["paths"] == {}
assert "info" in data
assert data["info"]["title"] == "My API"
assert data["info"]["version"] == "1.0"
assert data["info"]["description"] == "This is a testing API"
assert data["info"]["termsOfService"] == "http://somewhere.com/terms/"
assert data["info"]["contact"] == {
"name": "Support",
"url": "http://support.somewhere.com",
"email": "contact@somewhere.com",
}
assert data["info"]["license"] == {
"name": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html",
}
def test_specs_endpoint_info_callable(self, app, client):
api = restx.Api(
version=lambda: "1.0",
title=lambda: "My API",
description=lambda: "This is a testing API",
terms_url=lambda: "http://somewhere.com/terms/",
contact=lambda: "Support",
contact_url=lambda: "http://support.somewhere.com",
contact_email=lambda: "contact@somewhere.com",
license=lambda: "Apache 2.0",
license_url=lambda: "http://www.apache.org/licenses/LICENSE-2.0.html",
)
api.init_app(app)
data = client.get_specs()
assert data["swagger"] == "2.0"
assert data["basePath"] == "/"
assert data["produces"] == ["application/json"]
assert data["paths"] == {}
assert "info" in data
assert data["info"]["title"] == "My API"
assert data["info"]["version"] == "1.0"
assert data["info"]["description"] == "This is a testing API"
assert data["info"]["termsOfService"] == "http://somewhere.com/terms/"
assert data["info"]["contact"] == {
"name": "Support",
"url": "http://support.somewhere.com",
"email": "contact@somewhere.com",
}
assert data["info"]["license"] == {
"name": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html",
}
def test_specs_endpoint_no_host(self, app, client):
restx.Api(app)
data = client.get_specs("")
assert "host" not in data
assert data["basePath"] == "/"
@pytest.mark.options(server_name="api.restx.org")
def test_specs_endpoint_host(self, app, client):
# app.config['SERVER_NAME'] = 'api.restx.org'
restx.Api(app)
data = client.get_specs("")
assert data["host"] == "api.restx.org"
assert data["basePath"] == "/"
@pytest.mark.options(server_name="api.restx.org")
def test_specs_endpoint_host_with_url_prefix(self, app, client):
blueprint = Blueprint("api", __name__, url_prefix="/api/1")
restx.Api(blueprint)
app.register_blueprint(blueprint)
data = client.get_specs("/api/1")
assert data["host"] == "api.restx.org"
assert data["basePath"] == "/api/1"
@pytest.mark.options(server_name="restx.org")
def test_specs_endpoint_host_and_subdomain(self, app, client):
blueprint = Blueprint("api", __name__, subdomain="api")
restx.Api(blueprint)
app.register_blueprint(blueprint)
data = client.get_specs(base_url="http://api.restx.org")
assert data["host"] == "api.restx.org"
assert data["basePath"] == "/"
def test_specs_endpoint_tags_short(self, app, client):
restx.Api(app, tags=["tag-1", "tag-2", "tag-3"])
data = client.get_specs("")
assert data["tags"] == [{"name": "tag-1"}, {"name": "tag-2"}, {"name": "tag-3"}]
def test_specs_endpoint_tags_tuple(self, app, client):
restx.Api(
app,
tags=[
("tag-1", "Tag 1"),
("tag-2", "Tag 2"),
("tag-3", "Tag 3"),
],
)
data = client.get_specs("")
assert data["tags"] == [
{"name": "tag-1", "description": "Tag 1"},
{"name": "tag-2", "description": "Tag 2"},
{"name": "tag-3", "description": "Tag 3"},
]
def test_specs_endpoint_tags_dict(self, app, client):
restx.Api(
app,
tags=[
{"name": "tag-1", "description": "Tag 1"},
{"name": "tag-2", "description": "Tag 2"},
{"name": "tag-3", "description": "Tag 3"},
],
)
data = client.get_specs("")
assert data["tags"] == [
{"name": "tag-1", "description": "Tag 1"},
{"name": "tag-2", "description": "Tag 2"},
{"name": "tag-3", "description": "Tag 3"},
]
@pytest.mark.api(tags=["ns", "tag"])
def test_specs_endpoint_tags_namespaces(self, api, client):
api.namespace("ns", "Description")
data = client.get_specs("")
assert data["tags"] == [{"name": "ns"}, {"name": "tag"}]
def test_specs_endpoint_invalid_tags(self, app, client):
api = restx.Api(app, tags=[{"description": "Tag 1"}])
client.get_specs("", status=500)
assert list(api.__schema__.keys()) == ["error"]
def test_specs_endpoint_default_ns_with_resources(self, app, client):
restx.Api(app)
data = client.get_specs("")
assert data["tags"] == []
def test_specs_endpoint_default_ns_without_resources(self, app, client):
api = restx.Api(app)
@api.route("/test", endpoint="test")
class TestResource(restx.Resource):
def get(self):
return {}
data = client.get_specs("")
assert data["tags"] == [{"name": "default", "description": "Default namespace"}]
def test_specs_endpoint_default_ns_with_specified_ns(self, app, client):
api = restx.Api(app)
ns = api.namespace("ns", "Test namespace")
@ns.route("/test2", endpoint="test2")
@api.route("/test", endpoint="test")
class TestResource(restx.Resource):
def get(self):
return {}
data = client.get_specs("")
assert data["tags"] == [
{"name": "default", "description": "Default namespace"},
{"name": "ns", "description": "Test namespace"},
]
def test_specs_endpoint_specified_ns_without_default_ns(self, app, client):
api = restx.Api(app)
ns = api.namespace("ns", "Test namespace")
@ns.route("/", endpoint="test2")
class TestResource(restx.Resource):
def get(self):
return {}
data = client.get_specs("")
assert data["tags"] == [{"name": "ns", "description": "Test namespace"}]
def test_specs_endpoint_namespace_without_description(self, app, client):
api = restx.Api(app)
ns = api.namespace("ns")
@ns.route("/test", endpoint="test")
class TestResource(restx.Resource):
def get(self):
return {}
data = client.get_specs("")
assert data["tags"] == [{"name": "ns"}]
def test_specs_endpoint_namespace_all_resources_hidden(self, app, client):
api = restx.Api(app)
ns = api.namespace("ns")
@ns.route("/test", endpoint="test", doc=False)
class TestResource(restx.Resource):
def get(self):
return {}
@ns.route("/test2", endpoint="test2")
@ns.hide
class TestResource2(restx.Resource):
def get(self):
return {}
@ns.route("/test3", endpoint="test3")
@ns.doc(False)
class TestResource3(restx.Resource):
def get(self):
return {}
data = client.get_specs("")
assert data["tags"] == []
def test_specs_authorizations(self, app, client):
authorizations = {"apikey": {"type": "apiKey", "in": "header", "name": "X-API"}}
restx.Api(app, authorizations=authorizations)
data = client.get_specs()
assert "securityDefinitions" in data
assert data["securityDefinitions"] == authorizations
@pytest.mark.api(prefix="/api")
def test_minimal_documentation(self, api, client):
ns = api.namespace("ns", "Test namespace")
@ns.route("/", endpoint="test")
class TestResource(restx.Resource):
def get(self):
return {}
data = client.get_specs("/api")
paths = data["paths"]
assert len(paths.keys()) == 1
assert "/ns/" in paths
assert "get" in paths["/ns/"]
op = paths["/ns/"]["get"]
assert op["tags"] == ["ns"]
assert op["operationId"] == "get_test_resource"
assert "parameters" not in op
assert "summary" not in op
assert "description" not in op
assert op["responses"] == {
"200": {
"description": "Success",
}
}
assert url_for("api.test") == "/api/ns/"
@pytest.mark.api(prefix="/api", version="1.0")
def test_default_ns_resource_documentation(self, api, client):
@api.route("/test/", endpoint="test")
class TestResource(restx.Resource):
def get(self):
return {}
data = client.get_specs("/api")
paths = data["paths"]
assert len(paths.keys()) == 1
assert "/test/" in paths
assert "get" in paths["/test/"]
op = paths["/test/"]["get"]
assert op["tags"] == ["default"]
assert op["responses"] == {
"200": {
"description": "Success",
}
}
assert len(data["tags"]) == 1
tag = data["tags"][0]
assert tag["name"] == "default"
assert tag["description"] == "Default namespace"
assert url_for("api.test") == "/api/test/"
@pytest.mark.api(default="site", default_label="Site namespace")
def test_default_ns_resource_documentation_with_override(self, api, client):
@api.route("/test/", endpoint="test")
class TestResource(restx.Resource):
def get(self):
return {}
data = client.get_specs()
paths = data["paths"]
assert len(paths.keys()) == 1
assert "/test/" in paths
assert "get" in paths["/test/"]
op = paths["/test/"]["get"]
assert op["tags"] == ["site"]
assert op["responses"] == {
"200": {
"description": "Success",
}
}
assert len(data["tags"]) == 1
tag = data["tags"][0]
assert tag["name"] == "site"
assert tag["description"] == "Site namespace"
assert url_for("api.test") == "/test/"
@pytest.mark.api(prefix="/api")
def test_ns_resource_documentation(self, api, client):
ns = api.namespace("ns", "Test namespace")
@ns.route("/", endpoint="test")
class TestResource(restx.Resource):
def get(self):
return {}
data = client.get_specs("/api")
paths = data["paths"]
assert len(paths.keys()) == 1
assert "/ns/" in paths
assert "get" in paths["/ns/"]
op = paths["/ns/"]["get"]
assert op["tags"] == ["ns"]
assert op["responses"] == {
"200": {
"description": "Success",
}
}
assert "parameters" not in op
assert len(data["tags"]) == 1
tag = data["tags"][-1]
assert tag["name"] == "ns"
assert tag["description"] == "Test namespace"
assert url_for("api.test") == "/api/ns/"
def test_ns_resource_documentation_lazy(self, app, client):
api = restx.Api()
ns = api.namespace("ns", "Test namespace")
@ns.route("/", endpoint="test")
class TestResource(restx.Resource):
def get(self):
return {}
api.init_app(app)
data = client.get_specs()
paths = data["paths"]
assert len(paths.keys()) == 1
assert "/ns/" in paths
assert "get" in paths["/ns/"]
op = paths["/ns/"]["get"]
assert op["tags"] == ["ns"]
assert op["responses"] == {
"200": {
"description": "Success",
}
}
assert len(data["tags"]) == 1
tag = data["tags"][-1]
assert tag["name"] == "ns"
assert tag["description"] == "Test namespace"
assert url_for("test") == "/ns/"
def test_methods_docstring_to_summary(self, api, client):
@api.route("/test/", endpoint="test")
class TestResource(restx.Resource):
def get(self):
"""
GET operation
"""
return {}
def post(self):
"""POST operation.
Should be ignored
"""
return {}
def put(self):
"""PUT operation. Should be ignored"""
return {}
def delete(self):
"""
DELETE operation.
Should be ignored.
"""
return {}
data = client.get_specs()
path = data["paths"]["/test/"]
assert len(path.keys()) == 4
for method in path.keys():
operation = path[method]
assert method in ("get", "post", "put", "delete")
assert operation["summary"] == "{0} operation".format(method.upper())
assert operation["operationId"] == "{0}_test_resource".format(
method.lower()
)
# assert operation['parameters'] == []
def test_path_parameter_no_type(self, api, client):
@api.route("/id/<id>/", endpoint="by-id")
class ByIdResource(restx.Resource):
def get(self, id):
return {}
data = client.get_specs()
assert "/id/{id}/" in data["paths"]
path = data["paths"]["/id/{id}/"]
assert len(path["parameters"]) == 1
parameter = path["parameters"][0]
assert parameter["name"] == "id"
assert parameter["type"] == "string"
assert parameter["in"] == "path"
assert parameter["required"] is True
def test_path_parameter_with_type(self, api, client):
@api.route("/name/<int:age>/", endpoint="by-name")
class ByNameResource(restx.Resource):
def get(self, age):
return {}
data = client.get_specs()
assert "/name/{age}/" in data["paths"]
path = data["paths"]["/name/{age}/"]
assert len(path["parameters"]) == 1
parameter = path["parameters"][0]
assert parameter["name"] == "age"
assert parameter["type"] == "integer"
assert parameter["in"] == "path"
assert parameter["required"] is True
def test_path_parameter_with_type_with_argument(self, api, client):
@api.route("/name/<string(length=2):id>/", endpoint="by-name")
class ByNameResource(restx.Resource):
def get(self, id):
return {}
data = client.get_specs()
assert "/name/{id}/" in data["paths"]
path = data["paths"]["/name/{id}/"]
assert len(path["parameters"]) == 1
parameter = path["parameters"][0]
assert parameter["name"] == "id"
assert parameter["type"] == "string"
assert parameter["in"] == "path"
assert parameter["required"] is True
def test_path_parameter_with_explicit_details(self, api, client):
@api.route(
"/name/<int:age>/",
endpoint="by-name",
doc={"params": {"age": {"description": "An age"}}},
)
class ByNameResource(restx.Resource):
def get(self, age):
return {}
data = client.get_specs()
assert "/name/{age}/" in data["paths"]
path = data["paths"]["/name/{age}/"]
assert len(path["parameters"]) == 1
parameter = path["parameters"][0]
assert parameter["name"] == "age"
assert parameter["type"] == "integer"
assert parameter["in"] == "path"
assert parameter["required"] is True
assert parameter["description"] == "An age"
def test_path_parameter_with_decorator_details(self, api, client):
@api.route("/name/<int:age>/")
@api.param("age", "An age")
class ByNameResource(restx.Resource):
def get(self, age):
return {}
data = client.get_specs()
assert "/name/{age}/" in data["paths"]
path = data["paths"]["/name/{age}/"]
assert len(path["parameters"]) == 1
parameter = path["parameters"][0]
assert parameter["name"] == "age"
assert parameter["type"] == "integer"
assert parameter["in"] == "path"
assert parameter["required"] is True
assert parameter["description"] == "An age"
def test_expect_parser(self, api, client):
parser = api.parser()
parser.add_argument("param", type=int, help="Some param")
parser.add_argument("jsonparam", type=str, location="json", help="Some param")
@api.route("/with-parser/", endpoint="with-parser")
class WithParserResource(restx.Resource):
@api.expect(parser)
def get(self):
return {}
data = client.get_specs()
assert "/with-parser/" in data["paths"]
op = data["paths"]["/with-parser/"]["get"]
assert len(op["parameters"]) == 2
parameter = [o for o in op["parameters"] if o["in"] == "query"][0]
assert parameter["name"] == "param"
assert parameter["type"] == "integer"
assert parameter["in"] == "query"
assert parameter["description"] == "Some param"
parameter = [o for o in op["parameters"] if o["in"] == "body"][0]
assert parameter["name"] == "payload"
assert parameter["required"]
assert parameter["in"] == "body"
assert parameter["schema"]["properties"]["jsonparam"]["type"] == "string"
def test_expect_parser_on_class(self, api, client):
parser = api.parser()
parser.add_argument("param", type=int, help="Some param")
@api.route("/with-parser/", endpoint="with-parser")
@api.expect(parser)
class WithParserResource(restx.Resource):
def get(self):
return {}
data = client.get_specs()
assert "/with-parser/" in data["paths"]
path = data["paths"]["/with-parser/"]
assert len(path["parameters"]) == 1
parameter = path["parameters"][0]
assert parameter["name"] == "param"
assert parameter["type"] == "integer"
assert parameter["in"] == "query"
assert parameter["description"] == "Some param"
def test_method_parser_on_class(self, api, client):
parser = api.parser()
parser.add_argument("param", type=int, help="Some param")
@api.route("/with-parser/", endpoint="with-parser")
@api.doc(get={"expect": parser})
class WithParserResource(restx.Resource):
def get(self):
return {}
def post(self):
return {}
data = client.get_specs()
assert "/with-parser/" in data["paths"]
op = data["paths"]["/with-parser/"]["get"]
assert len(op["parameters"]) == 1
parameter = op["parameters"][0]
assert parameter["name"] == "param"
assert parameter["type"] == "integer"
assert parameter["in"] == "query"
assert parameter["description"] == "Some param"
op = data["paths"]["/with-parser/"]["post"]
assert "parameters" not in op
def test_parser_parameters_override(self, api, client):
parser = api.parser()
parser.add_argument("param", type=int, help="Some param")
@api.route("/with-parser/", endpoint="with-parser")
class WithParserResource(restx.Resource):
@api.expect(parser)
@api.doc(params={"param": {"description": "New description"}})
def get(self):
return {}
data = client.get_specs()
assert "/with-parser/" in data["paths"]
op = data["paths"]["/with-parser/"]["get"]
assert len(op["parameters"]) == 1
parameter = op["parameters"][0]
assert parameter["name"] == "param"
assert parameter["type"] == "integer"
assert parameter["in"] == "query"
assert parameter["description"] == "New description"
def test_parser_parameter_in_form(self, api, client):
parser = api.parser()
parser.add_argument("param", type=int, help="Some param", location="form")
@api.route("/with-parser/", endpoint="with-parser")
class WithParserResource(restx.Resource):
@api.expect(parser)
def get(self):
return {}
data = client.get_specs()
assert "/with-parser/" in data["paths"]
op = data["paths"]["/with-parser/"]["get"]
assert len(op["parameters"]) == 1
parameter = op["parameters"][0]
assert parameter["name"] == "param"
assert parameter["type"] == "integer"
assert parameter["in"] == "formData"
assert parameter["description"] == "Some param"
assert op["consumes"] == [
"application/x-www-form-urlencoded",
"multipart/form-data",
]
def test_parser_parameter_in_files(self, api, client):
parser = api.parser()
parser.add_argument("in_files", type=FileStorage, location="files")
@api.route("/with-parser/", endpoint="with-parser")
class WithParserResource(restx.Resource):
@api.expect(parser)
def get(self):
return {}
data = client.get_specs()
assert "/with-parser/" in data["paths"]
op = data["paths"]["/with-parser/"]["get"]
assert len(op["parameters"]) == 1
parameter = op["parameters"][0]
assert parameter["name"] == "in_files"
assert parameter["type"] == "file"
assert parameter["in"] == "formData"
assert op["consumes"] == ["multipart/form-data"]
def test_parser_parameter_in_files_on_class(self, api, client):
parser = api.parser()
parser.add_argument("in_files", type=FileStorage, location="files")
@api.route("/with-parser/", endpoint="with-parser")
@api.expect(parser)
class WithParserResource(restx.Resource):
def get(self):
return {}
data = client.get_specs()
assert "/with-parser/" in data["paths"]
path = data["paths"]["/with-parser/"]
assert len(path["parameters"]) == 1
parameter = path["parameters"][0]
assert parameter["name"] == "in_files"
assert parameter["type"] == "file"
assert parameter["in"] == "formData"
assert "consumes" not in path
op = path["get"]
assert "consumes" in op
assert op["consumes"] == ["multipart/form-data"]
def test_explicit_parameters(self, api, client):
@api.route("/name/<int:age>/", endpoint="by-name")
class ByNameResource(restx.Resource):
@api.doc(
params={
"q": {
"type": "string",
"in": "query",
"description": "A query string",
}
}
)
def get(self, age):
return {}
data = client.get_specs()
assert "/name/{age}/" in data["paths"]
path = data["paths"]["/name/{age}/"]
assert len(path["parameters"]) == 1
parameter = path["parameters"][0]
assert parameter["name"] == "age"
assert parameter["type"] == "integer"
assert parameter["in"] == "path"
assert parameter["required"] is True
op = path["get"]
assert len(op["parameters"]) == 1
parameter = op["parameters"][0]
assert parameter["name"] == "q"
assert parameter["type"] == "string"
assert parameter["in"] == "query"
assert parameter["description"] == "A query string"
def test_explicit_parameters_with_decorator(self, api, client):
@api.route("/name/")
class ByNameResource(restx.Resource):
@api.param("q", "A query string", type="string", _in="formData")
def get(self, age):
return {}
data = client.get_specs()
assert "/name/" in data["paths"]
op = data["paths"]["/name/"]["get"]
assert len(op["parameters"]) == 1
parameter = op["parameters"][0]
assert parameter["name"] == "q"
assert parameter["type"] == "string"
assert parameter["in"] == "formData"
assert parameter["description"] == "A query string"
def test_class_explicit_parameters(self, api, client):
@api.route(
"/name/<int:age>/",
endpoint="by-name",
doc={
"params": {
"q": {
"type": "string",
"in": "query",
"description": "A query string",
}
}
},
)
class ByNameResource(restx.Resource):
def get(self, age):
return {}
data = client.get_specs()
assert "/name/{age}/" in data["paths"]
path = data["paths"]["/name/{age}/"]
assert len(path["parameters"]) == 2
by_name = dict((p["name"], p) for p in path["parameters"])
parameter = by_name["age"]
assert parameter["name"] == "age"
assert parameter["type"] == "integer"
assert parameter["in"] == "path"
assert parameter["required"] is True
parameter = by_name["q"]
assert parameter["name"] == "q"
assert parameter["type"] == "string"
assert parameter["in"] == "query"
assert parameter["description"] == "A query string"
def test_explicit_parameters_override(self, api, client):
@api.route(
"/name/<int:age>/",
endpoint="by-name",
doc={
"params": {
"q": {
"type": "string",
"in": "query",
"description": "Overriden description",
},
"age": {"description": "An age"},
}
},
)
class ByNameResource(restx.Resource):
@api.doc(params={"q": {"description": "A query string"}})
def get(self, age):
return {}
def post(self, age):
pass
data = client.get_specs()
assert "/name/{age}/" in data["paths"]
path = data["paths"]["/name/{age}/"]
assert len(path["parameters"]) == 1
by_name = dict((p["name"], p) for p in path["parameters"])
parameter = by_name["age"]
assert parameter["name"] == "age"
assert parameter["type"] == "integer"
assert parameter["in"] == "path"
assert parameter["required"] is True
assert parameter["description"] == "An age"
# Don't duplicate parameters
assert "q" not in by_name
get = data["paths"]["/name/{age}/"]["get"]
assert len(get["parameters"]) == 1
parameter = get["parameters"][0]
assert parameter["name"] == "q"
assert parameter["type"] == "string"
assert parameter["in"] == "query"
assert parameter["description"] == "A query string"
post = data["paths"]["/name/{age}/"]["post"]
assert len(post["parameters"]) == 1
parameter = post["parameters"][0]
assert parameter["name"] == "q"
assert parameter["type"] == "string"
assert parameter["in"] == "query"
assert parameter["description"] == "Overriden description"
def test_explicit_parameters_override_by_method(self, api, client):
@api.route(
"/name/<int:age>/",
endpoint="by-name",
doc={
"get": {
"params": {
"q": {
"type": "string",
"in": "query",
"description": "A query string",
}
}
},
"params": {"age": {"description": "An age"}},
},
)
class ByNameResource(restx.Resource):
@api.doc(params={"age": {"description": "Overriden"}})
def get(self, age):
return {}
def post(self, age):
return {}
data = client.get_specs()
assert "/name/{age}/" in data["paths"]
path = data["paths"]["/name/{age}/"]
assert "parameters" not in path
get = path["get"]
assert len(get["parameters"]) == 2
by_name = dict((p["name"], p) for p in get["parameters"])
parameter = by_name["age"]
assert parameter["name"] == "age"
assert parameter["type"] == "integer"
assert parameter["in"] == "path"
assert parameter["required"] is True
assert parameter["description"] == "Overriden"
parameter = by_name["q"]
assert parameter["name"] == "q"
assert parameter["type"] == "string"
assert parameter["in"] == "query"
assert parameter["description"] == "A query string"
post = path["post"]
assert len(post["parameters"]) == 1
by_name = dict((p["name"], p) for p in post["parameters"])
parameter = by_name["age"]
assert parameter["name"] == "age"
assert parameter["type"] == "integer"
assert parameter["in"] == "path"
assert parameter["required"] is True
assert parameter["description"] == "An age"
def test_parameters_cascading_with_apidoc_false(self, api, client):
@api.route(
"/name/<int:age>/",
endpoint="by-name",
doc={
"get": {
"params": {
"q": {
"type": "string",
"in": "query",
"description": "A query string",
}
}
},
"params": {"age": {"description": "An age"}},
},
)
class ByNameResource(restx.Resource):
@api.doc(params={"age": {"description": "Overriden"}})
def get(self, age):
return {}
@api.doc(False)
def post(self, age):
return {}
data = client.get_specs()
assert "/name/{age}/" in data["paths"]
path = data["paths"]["/name/{age}/"]
assert "parameters" not in path
get = path["get"]
assert len(get["parameters"]) == 2
by_name = dict((p["name"], p) for p in get["parameters"])
assert "age" in by_name
assert "q" in by_name
assert "post" not in path
def test_explicit_parameters_desription_shortcut(self, api, client):
@api.route(
"/name/<int:age>/",
endpoint="by-name",
doc={
"get": {
"params": {
"q": "A query string",
}
},
"params": {"age": "An age"},
},
)
class ByNameResource(restx.Resource):
@api.doc(params={"age": "Overriden"})
def get(self, age):
return {}
def post(self, age):
return {}
data = client.get_specs()
assert "/name/{age}/" in data["paths"]
path = data["paths"]["/name/{age}/"]
assert "parameters" not in path
get = path["get"]
assert len(get["parameters"]) == 2
by_name = dict((p["name"], p) for p in get["parameters"])
parameter = by_name["age"]
assert parameter["name"] == "age"
assert parameter["type"] == "integer"
assert parameter["in"] == "path"
assert parameter["required"] is True
assert parameter["description"] == "Overriden"
parameter = by_name["q"]
assert parameter["name"] == "q"
assert parameter["type"] == "string"
assert parameter["in"] == "query"
assert parameter["description"] == "A query string"
post = path["post"]
assert len(post["parameters"]) == 1
by_name = dict((p["name"], p) for p in post["parameters"])
parameter = by_name["age"]
assert parameter["name"] == "age"
assert parameter["type"] == "integer"
assert parameter["in"] == "path"
assert parameter["required"] is True
assert parameter["description"] == "An age"
assert "q" not in by_name
def test_explicit_parameters_native_types(self, api, client):
@api.route("/types/", endpoint="native")
class NativeTypesResource(restx.Resource):
@api.doc(
params={
"int": {
"type": int,
"in": "query",
},
"float": {
"type": float,
"in": "query",
},
"bool": {
"type": bool,
"in": "query",
},
"str": {
"type": str,
"in": "query",
},
"int-array": {
"type": [int],
"in": "query",
},
"float-array": {
"type": [float],
"in": "query",
},
"bool-array": {
"type": [bool],
"in": "query",
},
"str-array": {
"type": [str],
"in": "query",
},
}
)
def get(self, age):
return {}
data = client.get_specs()
op = data["paths"]["/types/"]["get"]
parameters = dict((p["name"], p) for p in op["parameters"])
assert parameters["int"]["type"] == "integer"
assert parameters["float"]["type"] == "number"
assert parameters["str"]["type"] == "string"
assert parameters["bool"]["type"] == "boolean"
assert parameters["int-array"]["type"] == "array"
assert parameters["int-array"]["items"]["type"] == "integer"
assert parameters["float-array"]["type"] == "array"
assert parameters["float-array"]["items"]["type"] == "number"
assert parameters["str-array"]["type"] == "array"
assert parameters["str-array"]["items"]["type"] == "string"
assert parameters["bool-array"]["type"] == "array"
assert parameters["bool-array"]["items"]["type"] == "boolean"
def test_response_on_method(self, api, client):
api.model(
"ErrorModel",
{
"message": restx.fields.String,
},
)
@api.route("/test/")
class ByNameResource(restx.Resource):
@api.doc(
responses={
404: "Not found",
405: ("Some message", "ErrorModel"),
}
)
def get(self):
return {}
data = client.get_specs("")
paths = data["paths"]
assert len(paths.keys()) == 1
op = paths["/test/"]["get"]
assert op["tags"] == ["default"]
assert op["responses"] == {
"404": {
"description": "Not found",
},
"405": {
"description": "Some message",
"schema": {
"$ref": "#/definitions/ErrorModel",
},
},
}
assert "definitions" in data
assert "ErrorModel" in data["definitions"]
def test_api_response(self, api, client):
@api.route("/test/")
class TestResource(restx.Resource):
@api.response(200, "Success")
def get(self):
pass
data = client.get_specs("")
paths = data["paths"]
op = paths["/test/"]["get"]
assert op["responses"] == {
"200": {
"description": "Success",
}
}
def test_api_response_multiple(self, api, client):
@api.route("/test/")
class TestResource(restx.Resource):
@api.response(200, "Success")
@api.response(400, "Validation error")
def get(self):
pass
data = client.get_specs("")
paths = data["paths"]
op = paths["/test/"]["get"]
assert op["responses"] == {
"200": {
"description": "Success",
},
"400": {
"description": "Validation error",
},
}
def test_api_response_with_model(self, api, client):
model = api.model(
"SomeModel",
{
"message": restx.fields.String,
},
)
@api.route("/test/")
class TestResource(restx.Resource):
@api.response(200, "Success", model)
def get(self):
pass
data = client.get_specs("")
paths = data["paths"]
op = paths["/test/"]["get"]
assert op["responses"] == {
"200": {
"description": "Success",
"schema": {
"$ref": "#/definitions/SomeModel",
},
}
}
assert "SomeModel" in data["definitions"]
def test_api_response_default(self, api, client):
@api.route("/test/")
class TestResource(restx.Resource):
@api.response("default", "Error")
def get(self):
pass
data = client.get_specs("")
paths = data["paths"]
op = paths["/test/"]["get"]
assert op["responses"] == {
"default": {
"description": "Error",
}
}
def test_api_header(self, api, client):
@api.route("/test/")
@api.header("X-HEADER", "A class header")
class TestResource(restx.Resource):
@api.header(
"X-HEADER-2", "Another header", type=[int], collectionFormat="csv"
)
@api.header("X-HEADER-3", type=int)
@api.header("X-HEADER-4", type="boolean")
def get(self):
pass
data = client.get_specs("")
headers = data["paths"]["/test/"]["get"]["responses"]["200"]["headers"]
assert "X-HEADER" in headers
assert headers["X-HEADER"] == {
"type": "string",
"description": "A class header",
}
assert "X-HEADER-2" in headers
assert headers["X-HEADER-2"] == {
"type": "array",
"items": {"type": "integer"},
"description": "Another header",
"collectionFormat": "csv",
}
assert "X-HEADER-3" in headers
assert headers["X-HEADER-3"] == {"type": "integer"}
assert "X-HEADER-4" in headers
assert headers["X-HEADER-4"] == {"type": "boolean"}
def test_response_header(self, api, client):
@api.route("/test/")
class TestResource(restx.Resource):
@api.response(200, "Success")
@api.response(400, "Validation", headers={"X-HEADER": "An header"})
def get(self):
pass
data = client.get_specs("")
headers = data["paths"]["/test/"]["get"]["responses"]["400"]["headers"]
assert "X-HEADER" in headers
assert headers["X-HEADER"] == {
"type": "string",
"description": "An header",
}
def test_api_and_response_header(self, api, client):
@api.route("/test/")
@api.header("X-HEADER", "A class header")
class TestResource(restx.Resource):
@api.header("X-HEADER-2", type=int)
@api.response(200, "Success")
@api.response(400, "Validation", headers={"X-ERROR": "An error header"})
def get(self):
pass
data = client.get_specs("")
headers200 = data["paths"]["/test/"]["get"]["responses"]["200"]["headers"]
headers400 = data["paths"]["/test/"]["get"]["responses"]["400"]["headers"]
for headers in (headers200, headers400):
assert "X-HEADER" in headers
assert "X-HEADER-2" in headers
assert "X-ERROR" in headers400
assert "X-ERROR" not in headers200
def test_expect_header(self, api, client):
parser = api.parser()
parser.add_argument(
"X-Header", location="headers", required=True, help="A required header"
)
parser.add_argument(
"X-Header-2",
location="headers",
type=int,
action="split",
help="Another header",
)
parser.add_argument("X-Header-3", location="headers", type=int)
parser.add_argument("X-Header-4", location="headers", type=inputs.boolean)
@api.route("/test/")
class TestResource(restx.Resource):
@api.expect(parser)
def get(self):
pass
data = client.get_specs("")
parameters = data["paths"]["/test/"]["get"]["parameters"]
def get_param(name):
candidates = [p for p in parameters if p["name"] == name]
assert len(candidates) == 1, "parameter {0} not found".format(name)
return candidates[0]
parameter = get_param("X-Header")
assert parameter["type"] == "string"
assert parameter["in"] == "header"
assert parameter["required"] is True
assert parameter["description"] == "A required header"
parameter = get_param("X-Header-2")
assert parameter["type"] == "array"
assert parameter["in"] == "header"
assert parameter["items"]["type"] == "integer"
assert parameter["description"] == "Another header"
assert parameter["collectionFormat"] == "csv"
parameter = get_param("X-Header-3")
assert parameter["type"] == "integer"
assert parameter["in"] == "header"
parameter = get_param("X-Header-4")
assert parameter["type"] == "boolean"
assert parameter["in"] == "header"
def test_description(self, api, client):
@api.route(
"/description/",
endpoint="description",
doc={
"description": "Parent description.",
"delete": {"description": "A delete operation"},
},
)
class ResourceWithDescription(restx.Resource):
@api.doc(description="Some details")
def get(self):
return {}
def post(self):
"""
Do something.
Extra description
"""
return {}
def put(self):
"""No description (only summary)"""
def delete(self):
"""No description (only summary)"""
@api.route("/descriptionless/", endpoint="descriptionless")
class ResourceWithoutDescription(restx.Resource):
def get(self):
"""No description (only summary)"""
return {}
data = client.get_specs()
description = lambda m: data["paths"]["/description/"][m]["description"] # noqa
assert description("get") == dedent(
"""\
Parent description.
Some details"""
)
assert description("post") == dedent(
"""\
Parent description.
Extra description"""
)
assert description("delete") == dedent(
"""\
Parent description.
A delete operation"""
)
assert description("put") == "Parent description."
assert "description" not in data["paths"]["/descriptionless/"]["get"]
def test_operation_id(self, api, client):
@api.route("/test/", endpoint="test")
class TestResource(restx.Resource):
@api.doc(id="get_objects")
def get(self):
return {}
def post(self):
return {}
data = client.get_specs()
path = data["paths"]["/test/"]
assert path["get"]["operationId"] == "get_objects"
assert path["post"]["operationId"] == "post_test_resource"
def test_operation_id_shortcut(self, api, client):
@api.route("/test/", endpoint="test")
class TestResource(restx.Resource):
@api.doc("get_objects")
def get(self):
return {}
data = client.get_specs()
path = data["paths"]["/test/"]
assert path["get"]["operationId"] == "get_objects"
def test_custom_default_operation_id(self, app, client):
def default_id(resource, method):
return "{0}{1}".format(method, resource)
api = restx.Api(app, default_id=default_id)
@api.route("/test/", endpoint="test")
class TestResource(restx.Resource):
@api.doc(id="get_objects")
def get(self):
return {}
def post(self):
return {}
data = client.get_specs()
path = data["paths"]["/test/"]
assert path["get"]["operationId"] == "get_objects"
assert path["post"]["operationId"] == "postTestResource"
@pytest.mark.api(default_id=lambda r, m: "{0}{1}".format(m, r))
def test_custom_default_operation_id_blueprint(self, api, client):
@api.route("/test/", endpoint="test")
class TestResource(restx.Resource):
@api.doc(id="get_objects")
def get(self):
return {}
def post(self):
return {}
data = client.get_specs()
path = data["paths"]["/test/"]
assert path["get"]["operationId"] == "get_objects"
assert path["post"]["operationId"] == "postTestResource"
def test_model_primitive_types(self, api, client):
@api.route("/model-int/")
class ModelInt(restx.Resource):
@api.doc(model=int)
def get(self):
return {}
data = client.get_specs()
assert "definitions" not in data
assert data["paths"]["/model-int/"]["get"]["responses"] == {
"200": {"description": "Success", "schema": {"type": "integer"}}
}
def test_model_as_flat_dict(self, api, client):
fields = api.model(
"Person",
{
"name": restx.fields.String,
"age": restx.fields.Integer,
"birthdate": restx.fields.DateTime,
},
)
@api.route("/model-as-dict/")
class ModelAsDict(restx.Resource):
@api.doc(model=fields)
def get(self):
return {}
@api.doc(model="Person")
def post(self):
return {}
data = client.get_specs()
assert "definitions" in data
assert "Person" in data["definitions"]
path = data["paths"]["/model-as-dict/"]
assert (
path["get"]["responses"]["200"]["schema"]["$ref"] == "#/definitions/Person"
)
assert (
path["post"]["responses"]["200"]["schema"]["$ref"] == "#/definitions/Person"
)
def test_model_as_nested_dict(self, api, client):
address_fields = api.model(
"Address",
{
"road": restx.fields.String,
},
)
fields = api.model("Person", {"address": restx.fields.Nested(address_fields)})
@api.route("/model-as-dict/")
class ModelAsDict(restx.Resource):
@api.doc(model=fields)
def get(self):
return {}
@api.doc(model="Person")
def post(self):
return {}
data = client.get_specs()
assert "definitions" in data
assert "Person" in data["definitions"]
assert data["definitions"]["Person"] == {
"properties": {
"address": {"$ref": "#/definitions/Address"},
},
"type": "object",
}
assert "Address" in data["definitions"]
assert data["definitions"]["Address"] == {
"properties": {
"road": {"type": "string"},
},
"type": "object",
}
path = data["paths"]["/model-as-dict/"]
assert (
path["get"]["responses"]["200"]["schema"]["$ref"] == "#/definitions/Person"
)
assert (
path["post"]["responses"]["200"]["schema"]["$ref"] == "#/definitions/Person"
)
def test_model_as_nested_dict_with_details(self, api, client):
address_fields = api.model(
"Address",
{
"road": restx.fields.String,
},
)
fields = api.model(
"Person",
{
"address": restx.fields.Nested(
address_fields, description="description", readonly=True
)
},
)
@api.route("/model-as-dict/")
class ModelAsDict(restx.Resource):
@api.doc(model=fields)
def get(self):
return {}
@api.doc(model="Person")
def post(self):
return {}
data = client.get_specs()
assert "definitions" in data
assert "Person" in data["definitions"]
assert data["definitions"]["Person"] == {
"properties": {
"address": {
"description": "description",
"readOnly": True,
"allOf": [{"$ref": "#/definitions/Address"}],
},
},
"type": "object",
}
assert "Address" in data["definitions"]
assert data["definitions"]["Address"] == {
"properties": {
"road": {"type": "string"},
},
"type": "object",
}
def test_model_as_flat_dict_with_marchal_decorator(self, api, client):
fields = api.model(
"Person",
{
"name": restx.fields.String,
"age": restx.fields.Integer,
"birthdate": restx.fields.DateTime,
},
)
@api.route("/model-as-dict/")
class ModelAsDict(restx.Resource):
@api.marshal_with(fields)
def get(self):
return {}
data = client.get_specs()
assert "definitions" in data
assert "Person" in data["definitions"]
responses = data["paths"]["/model-as-dict/"]["get"]["responses"]
assert responses == {
"200": {
"description": "Success",
"schema": {"$ref": "#/definitions/Person"},
}
}
def test_model_with_non_uri_chars_in_name(self, api, client):
# name will be encoded as 'Person%2F%2F%3Flots%7B%7D%20of%20%26illegals%40%60'
name = "Person//?lots{} of &illegals@`"
fields = api.model(name, {})
@api.route("/model-bad-uri/")
class ModelBadUri(restx.Resource):
@api.doc(model=fields)
def get(self):
return {}
@api.response(201, "", model=name)
def post(self):
return {}
data = client.get_specs()
assert "definitions" in data
assert name in data["definitions"]
path = data["paths"]["/model-bad-uri/"]
assert (
path["get"]["responses"]["200"]["schema"]["$ref"]
== "#/definitions/Person%2F%2F%3Flots%7B%7D%20of%20%26illegals%40%60"
)
assert (
path["post"]["responses"]["201"]["schema"]["$ref"]
== "#/definitions/Person%2F%2F%3Flots%7B%7D%20of%20%26illegals%40%60"
)
def test_marchal_decorator_with_code(self, api, client):
fields = api.model(
"Person",
{
"name": restx.fields.String,
"age": restx.fields.Integer,
"birthdate": restx.fields.DateTime,
},
)
@api.route("/model-as-dict/")
class ModelAsDict(restx.Resource):
@api.marshal_with(fields, code=204)
def delete(self):
return {}
data = client.get_specs()
assert "definitions" in data
assert "Person" in data["definitions"]
responses = data["paths"]["/model-as-dict/"]["delete"]["responses"]
assert responses == {
"204": {
"description": "Success",
"schema": {"$ref": "#/definitions/Person"},
}
}
def test_marchal_decorator_with_description(self, api, client):
person = api.model(
"Person",
{
"name": restx.fields.String,
"age": restx.fields.Integer,
"birthdate": restx.fields.DateTime,
},
)
@api.route("/model-as-dict/")
class ModelAsDict(restx.Resource):
@api.marshal_with(person, description="Some details")
def get(self):
return {}
data = client.get_specs()
assert "definitions" in data
assert "Person" in data["definitions"]
responses = data["paths"]["/model-as-dict/"]["get"]["responses"]
assert responses == {
"200": {
"description": "Some details",
"schema": {"$ref": "#/definitions/Person"},
}
}
def test_marhsal_decorator_with_envelope(self, api, client):
person = api.model(
"Person",
{
"name": restx.fields.String,
"age": restx.fields.Integer,
"birthdate": restx.fields.DateTime,
},
)
@api.route("/model-as-dict/")
class ModelAsDict(restx.Resource):
@api.marshal_with(person, envelope="person")
def get(self):
return {}
data = client.get_specs()
assert "definitions" in data
assert "Person" in data["definitions"]
responses = data["paths"]["/model-as-dict/"]["get"]["responses"]
assert responses == {
"200": {
"description": "Success",
"schema": {"properties": {"person": {"$ref": "#/definitions/Person"}}},
}
}
def test_model_as_flat_dict_with_marchal_decorator_list(self, api, client):
fields = api.model(
"Person",
{
"name": restx.fields.String,
"age": restx.fields.Integer,
"birthdate": restx.fields.DateTime,
},
)
@api.route("/model-as-dict/")
class ModelAsDict(restx.Resource):
@api.marshal_with(fields, as_list=True)
def get(self):
return {}
data = client.get_specs()
assert "definitions" in data
assert "Person" in data["definitions"]
assert data["definitions"]["Person"] == {
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"birthdate": {"type": "string", "format": "date-time"},
},
"type": "object",
}
path = data["paths"]["/model-as-dict/"]
assert path["get"]["responses"]["200"]["schema"] == {
"type": "array",
"items": {"$ref": "#/definitions/Person"},
}
def test_model_as_flat_dict_with_marchal_decorator_list_alt(self, api, client):
fields = api.model(
"Person",
{
"name": restx.fields.String,
"age": restx.fields.Integer,
"birthdate": restx.fields.DateTime,
},
)
@api.route("/model-as-dict/")
class ModelAsDict(restx.Resource):
@api.marshal_list_with(fields)
def get(self):
return {}
data = client.get_specs()
assert "definitions" in data
assert "Person" in data["definitions"]
path = data["paths"]["/model-as-dict/"]
assert path["get"]["responses"]["200"]["schema"] == {
"type": "array",
"items": {"$ref": "#/definitions/Person"},
}
def test_model_as_flat_dict_with_marchal_decorator_list_kwargs(self, api, client):
fields = api.model(
"Person",
{
"name": restx.fields.String,
"age": restx.fields.Integer,
"birthdate": restx.fields.DateTime,
},
)
@api.route("/model-as-dict/")
class ModelAsDict(restx.Resource):
@api.marshal_list_with(fields, code=201, description="Some details")
def get(self):
return {}
data = client.get_specs()
assert "definitions" in data
assert "Person" in data["definitions"]
path = data["paths"]["/model-as-dict/"]
assert path["get"]["responses"] == {
"201": {
"description": "Some details",
"schema": {
"type": "array",
"items": {"$ref": "#/definitions/Person"},
},
}
}
def test_model_as_dict_with_list(self, api, client):
fields = api.model(
"Person",
{
"name": restx.fields.String,
"age": restx.fields.Integer,
"tags": restx.fields.List(restx.fields.String),
},
)
@api.route("/model-with-list/")
class ModelAsDict(restx.Resource):
@api.doc(model=fields)
def get(self):
return {}
data = client.get_specs()
assert "definitions" in data
assert "Person" in data["definitions"]
assert data["definitions"]["Person"] == {
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"tags": {"type": "array", "items": {"type": "string"}},
},
"type": "object",
}
path = data["paths"]["/model-with-list/"]
assert path["get"]["responses"]["200"]["schema"] == {
"$ref": "#/definitions/Person"
}
def test_model_as_nested_dict_with_list(self, api, client):
address = api.model(
"Address",
{
"road": restx.fields.String,
},
)
person = api.model(
"Person",
{
"name": restx.fields.String,
"age": restx.fields.Integer,
"birthdate": restx.fields.DateTime,
"addresses": restx.fields.List(restx.fields.Nested(address)),
},
)
@api.route("/model-with-list/")
class ModelAsDict(restx.Resource):
@api.doc(model=person)
def get(self):
return {}
data = client.get_specs()
assert "definitions" in data
assert "Person" in data["definitions"]
assert "Address" in data["definitions"]
def test_model_list_of_primitive_types(self, api, client):
@api.route("/model-list/")
class ModelAsDict(restx.Resource):
@api.doc(model=[int])
def get(self):
return {}
@api.doc(model=[str])
def post(self):
return {}
data = client.get_specs()
assert "definitions" not in data
path = data["paths"]["/model-list/"]
assert path["get"]["responses"]["200"]["schema"] == {
"type": "array",
"items": {"type": "integer"},
}
assert path["post"]["responses"]["200"]["schema"] == {
"type": "array",
"items": {"type": "string"},
}
def test_model_list_as_flat_dict(self, api, client):
fields = api.model(
"Person",
{
"name": restx.fields.String,
"age": restx.fields.Integer,
"birthdate": restx.fields.DateTime,
},
)
@api.route("/model-as-dict/")
class ModelAsDict(restx.Resource):
@api.doc(model=[fields])
def get(self):
return {}
@api.doc(model=["Person"])
def post(self):
return {}
data = client.get_specs()
assert "definitions" in data
assert "Person" in data["definitions"]
path = data["paths"]["/model-as-dict/"]
for method in "get", "post":
assert path[method]["responses"]["200"]["schema"] == {
"type": "array",
"items": {"$ref": "#/definitions/Person"},
}
def test_model_doc_on_class(self, api, client):
fields = api.model(
"Person",
{
"name": restx.fields.String,
"age": restx.fields.Integer,
"birthdate": restx.fields.DateTime,
},
)
@api.route("/model-as-dict/")
@api.doc(model=fields)
class ModelAsDict(restx.Resource):
def get(self):
return {}
def post(self):
return {}
data = client.get_specs()
assert "definitions" in data
assert "Person" in data["definitions"]
path = data["paths"]["/model-as-dict/"]
for method in "get", "post":
assert path[method]["responses"]["200"]["schema"] == {
"$ref": "#/definitions/Person"
}
def test_model_doc_for_method_on_class(self, api, client):
fields = api.model(
"Person",
{
"name": restx.fields.String,
"age": restx.fields.Integer,
"birthdate": restx.fields.DateTime,
},
)
@api.route("/model-as-dict/")
@api.doc(get={"model": fields})
class ModelAsDict(restx.Resource):
def get(self):
return {}
def post(self):
return {}
data = client.get_specs()
assert "definitions" in data
assert "Person" in data["definitions"]
path = data["paths"]["/model-as-dict/"]
assert path["get"]["responses"]["200"]["schema"] == {
"$ref": "#/definitions/Person"
}
assert "schema" not in path["post"]["responses"]["200"]
def test_model_with_discriminator(self, api, client):
fields = api.model(
"Person",
{
"name": restx.fields.String(discriminator=True),
"age": restx.fields.Integer,
},
)
@api.route("/model-with-discriminator/")
class ModelAsDict(restx.Resource):
@api.marshal_with(fields)
def get(self):
return {}
data = client.get_specs()
assert "definitions" in data
assert "Person" in data["definitions"]
assert data["definitions"]["Person"] == {
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
},
"discriminator": "name",
"required": ["name"],
"type": "object",
}
def test_model_with_discriminator_override_require(self, api, client):
fields = api.model(
"Person",
{
"name": restx.fields.String(discriminator=True, required=False),
"age": restx.fields.Integer,
},
)
@api.route("/model-with-discriminator/")
class ModelAsDict(restx.Resource):
@api.marshal_with(fields)
def get(self):
return {}
data = client.get_specs()
assert "definitions" in data
assert "Person" in data["definitions"]
assert data["definitions"]["Person"] == {
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
},
"discriminator": "name",
"required": ["name"],
"type": "object",
}
def test_model_not_found(self, api, client):
@api.route("/model-not-found/")
class ModelAsDict(restx.Resource):
@api.doc(model="NotFound")
def get(self):
return {}
client.get_specs(status=500)
def test_recursive_model(self, api, client):
fields = api.model(
"Person",
{
"name": restx.fields.String,
"age": restx.fields.Integer,
"birthdate": restx.fields.DateTime,
},
)
fields["children"] = restx.fields.List(
restx.fields.Nested(fields),
default=[],
)
@api.route("/recursive-model/")
@api.doc(get={"model": fields})
class ModelAsDict(restx.Resource):
@api.marshal_with(fields)
def get(self):
return {}
client.get_specs(status=200)
def test_specs_no_duplicate_response_keys(self, api, client):
"""
This tests that the swagger.json document will not be written with duplicate object keys
due to the coercion of dict keys to string. The last @api.response should win.
"""
# Note the use of a strings '404' and '200' in class decorators as opposed to ints in method decorators.
@api.response("404", "Not Found")
class BaseResource(restx.Resource):
def get(self):
pass
model = api.model(
"SomeModel",
{
"message": restx.fields.String,
},
)
@api.route("/test/")
@api.response("200", "Success")
class TestResource(BaseResource):
# @api.marshal_with also yields a response
@api.marshal_with(model, code=200, description="Success on method")
@api.response(404, "Not Found on method")
def get(self):
{}
data = client.get_specs("")
paths = data["paths"]
op = paths["/test/"]["get"]
print(op["responses"])
assert op["responses"] == {
"200": {
"description": "Success on method",
"schema": {"$ref": "#/definitions/SomeModel"},
},
"404": {
"description": "Not Found on method",
},
}
def test_clone(self, api, client):
parent = api.model(
"Person",
{
"name": restx.fields.String,
"age": restx.fields.Integer,
"birthdate": restx.fields.DateTime,
},
)
child = api.clone(
"Child",
parent,
{
"extra": restx.fields.String,
},
)
@api.route("/extend/")
class ModelAsDict(restx.Resource):
@api.doc(model=child)
def get(self):
return {}
@api.doc(model="Child")
def post(self):
return {}
data = client.get_specs()
assert "definitions" in data
assert "Person" not in data["definitions"]
assert "Child" in data["definitions"]
path = data["paths"]["/extend/"]
assert (
path["get"]["responses"]["200"]["schema"]["$ref"] == "#/definitions/Child"
)
assert (
path["post"]["responses"]["200"]["schema"]["$ref"] == "#/definitions/Child"
)
def test_inherit(self, api, client):
parent = api.model(
"Person",
{
"name": restx.fields.String,
"age": restx.fields.Integer,
},
)
child = api.inherit(
"Child",
parent,
{
"extra": restx.fields.String,
},
)
@api.route("/inherit/")
class ModelAsDict(restx.Resource):
@api.marshal_with(child)
def get(self):
return {
"name": "John",
"age": 42,
"extra": "test",
}
@api.doc(model="Child")
def post(self):
return {}
data = client.get_specs()
assert "definitions" in data
assert "Person" in data["definitions"]
assert "Child" in data["definitions"]
assert data["definitions"]["Person"] == {
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
},
"type": "object",
}
assert data["definitions"]["Child"] == {
"allOf": [
{"$ref": "#/definitions/Person"},
{"properties": {"extra": {"type": "string"}}, "type": "object"},
]
}
path = data["paths"]["/inherit/"]
assert (
path["get"]["responses"]["200"]["schema"]["$ref"] == "#/definitions/Child"
)
assert (
path["post"]["responses"]["200"]["schema"]["$ref"] == "#/definitions/Child"
)
data = client.get_json("/inherit/")
assert data == {
"name": "John",
"age": 42,
"extra": "test",
}
def test_inherit_inline(self, api, client):
parent = api.model(
"Person",
{
"name": restx.fields.String,
"age": restx.fields.Integer,
},
)
child = api.inherit(
"Child",
parent,
{
"extra": restx.fields.String,
},
)
output = api.model(
"Output",
{
"child": restx.fields.Nested(child),
"children": restx.fields.List(restx.fields.Nested(child)),
},
)
@api.route("/inherit/")
class ModelAsDict(restx.Resource):
@api.marshal_with(output)
def get(self):
return {
"child": {
"name": "John",
"age": 42,
"extra": "test",
},
"children": [
{
"name": "John",
"age": 42,
"extra": "test",
},
{
"name": "Doe",
"age": 33,
"extra": "test2",
},
],
}
data = client.get_specs()
assert "definitions" in data
assert "Person" in data["definitions"]
assert "Child" in data["definitions"]
data = client.get_json("/inherit/")
assert data == {
"child": {
"name": "John",
"age": 42,
"extra": "test",
},
"children": [
{
"name": "John",
"age": 42,
"extra": "test",
},
{
"name": "Doe",
"age": 33,
"extra": "test2",
},
],
}
def test_polymorph_inherit(self, api, client):
class Child1:
pass
class Child2:
pass
parent = api.model(
"Person",
{
"name": restx.fields.String,
"age": restx.fields.Integer,
},
)
child1 = api.inherit(
"Child1",
parent,
{
"extra1": restx.fields.String,
},
)
child2 = api.inherit(
"Child2",
parent,
{
"extra2": restx.fields.String,
},
)
mapping = {
Child1: child1,
Child2: child2,
}
output = api.model("Output", {"child": restx.fields.Polymorph(mapping)})
@api.route("/polymorph/")
class ModelAsDict(restx.Resource):
@api.marshal_with(output)
def get(self):
return {}
data = client.get_specs()
assert "definitions" in data
assert "Person" in data["definitions"]
assert "Child1" in data["definitions"]
assert "Child2" in data["definitions"]
assert "Output" in data["definitions"]
path = data["paths"]["/polymorph/"]
assert (
path["get"]["responses"]["200"]["schema"]["$ref"] == "#/definitions/Output"
)
def test_polymorph_inherit_list(self, api, client):
class Child1(object):
name = "Child1"
extra1 = "extra1"
class Child2(object):
name = "Child2"
extra2 = "extra2"
parent = api.model(
"Person",
{
"name": restx.fields.String,
},
)
child1 = api.inherit(
"Child1",
parent,
{
"extra1": restx.fields.String,
},
)
child2 = api.inherit(
"Child2",
parent,
{
"extra2": restx.fields.String,
},
)
mapping = {
Child1: child1,
Child2: child2,
}
output = api.model(
"Output", {"children": restx.fields.List(restx.fields.Polymorph(mapping))}
)
@api.route("/polymorph/")
class ModelAsDict(restx.Resource):
@api.marshal_with(output)
def get(self):
return {"children": [Child1(), Child2()]}
data = client.get_specs()
assert "definitions" in data
assert "Person" in data["definitions"]
assert "Child1" in data["definitions"]
assert "Child2" in data["definitions"]
assert "Output" in data["definitions"]
path = data["paths"]["/polymorph/"]
assert (
path["get"]["responses"]["200"]["schema"]["$ref"] == "#/definitions/Output"
)
data = client.get_json("/polymorph/")
assert data == {
"children": [
{
"name": "Child1",
"extra1": "extra1",
},
{
"name": "Child2",
"extra2": "extra2",
},
]
}
def test_expect_model(self, api, client):
person = api.model(
"Person",
{
"name": restx.fields.String,
"age": restx.fields.Integer,
"birthdate": restx.fields.DateTime,
},
)
@api.route("/model-as-dict/")
class ModelAsDict(restx.Resource):
@api.expect(person)
def post(self):
return {}
data = client.get_specs()
assert "definitions" in data
assert "Person" in data["definitions"]
assert data["definitions"]["Person"] == {
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"birthdate": {"type": "string", "format": "date-time"},
},
"type": "object",
}
op = data["paths"]["/model-as-dict/"]["post"]
assert len(op["parameters"]) == 1
parameter = op["parameters"][0]
assert parameter == {
"name": "payload",
"in": "body",
"required": True,
"schema": {"$ref": "#/definitions/Person"},
}
assert "description" not in parameter
def test_body_model_shortcut(self, api, client):
fields = api.model(
"Person",
{
"name": restx.fields.String,
"age": restx.fields.Integer,
"birthdate": restx.fields.DateTime,
},
)
@api.route("/model-as-dict/")
class ModelAsDict(restx.Resource):
@api.doc(model="Person")
@api.expect(fields)
def post(self):
return {}
data = client.get_specs()
assert "definitions" in data
assert "Person" in data["definitions"]
assert data["definitions"]["Person"] == {
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"birthdate": {"type": "string", "format": "date-time"},
},
"type": "object",
}
op = data["paths"]["/model-as-dict/"]["post"]
assert op["responses"]["200"]["schema"]["$ref"] == "#/definitions/Person"
assert len(op["parameters"]) == 1
parameter = op["parameters"][0]
assert parameter == {
"name": "payload",
"in": "body",
"required": True,
"schema": {"$ref": "#/definitions/Person"},
}
assert "description" not in parameter
def test_expect_model_list(self, api, client):
model = api.model(
"Person",
{
"name": restx.fields.String,
"age": restx.fields.Integer,
"birthdate": restx.fields.DateTime,
},
)
@api.route("/model-list/")
class ModelAsDict(restx.Resource):
@api.expect([model])
def post(self):
return {}
data = client.get_specs()
assert "definitions" in data
assert "Person" in data["definitions"]
assert data["definitions"]["Person"] == {
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"birthdate": {"type": "string", "format": "date-time"},
},
"type": "object",
}
op = data["paths"]["/model-list/"]["post"]
parameter = op["parameters"][0]
assert parameter == {
"name": "payload",
"in": "body",
"required": True,
"schema": {
"type": "array",
"items": {"$ref": "#/definitions/Person"},
},
}
def test_both_model_and_parser_from_expect(self, api, client):
parser = api.parser()
parser.add_argument("param", type=int, help="Some param")
person = api.model(
"Person",
{
"name": restx.fields.String,
"age": restx.fields.Integer,
"birthdate": restx.fields.DateTime,
},
)
@api.route("/with-parser/", endpoint="with-parser")
class WithParserResource(restx.Resource):
@api.expect(parser, person)
def get(self):
return {}
data = client.get_specs()
assert "definitions" in data
assert "Person" in data["definitions"]
assert data["definitions"]["Person"] == {
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"birthdate": {"type": "string", "format": "date-time"},
},
"type": "object",
}
assert "/with-parser/" in data["paths"]
op = data["paths"]["/with-parser/"]["get"]
assert len(op["parameters"]) == 2
parameters = dict((p["in"], p) for p in op["parameters"])
parameter = parameters["query"]
assert parameter["name"] == "param"
assert parameter["type"] == "integer"
assert parameter["in"] == "query"
assert parameter["description"] == "Some param"
parameter = parameters["body"]
assert parameter == {
"name": "payload",
"in": "body",
"required": True,
"schema": {"$ref": "#/definitions/Person"},
}
def test_expect_primitive_list(self, api, client):
@api.route("/model-list/")
class ModelAsDict(restx.Resource):
@api.expect([restx.fields.String])
def post(self):
return {}
data = client.get_specs()
op = data["paths"]["/model-list/"]["post"]
parameter = op["parameters"][0]
assert parameter == {
"name": "payload",
"in": "body",
"required": True,
"schema": {
"type": "array",
"items": {"type": "string"},
},
}
def test_body_model_list(self, api, client):
fields = api.model(
"Person",
{
"name": restx.fields.String,
"age": restx.fields.Integer,
"birthdate": restx.fields.DateTime,
},
)
@api.route("/model-list/")
class ModelAsDict(restx.Resource):
@api.expect([fields])
def post(self):
return {}
data = client.get_specs()
assert "definitions" in data
assert "Person" in data["definitions"]
assert data["definitions"]["Person"] == {
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"birthdate": {"type": "string", "format": "date-time"},
},
"type": "object",
}
op = data["paths"]["/model-list/"]["post"]
parameter = op["parameters"][0]
assert parameter == {
"name": "payload",
"in": "body",
"required": True,
"schema": {
"type": "array",
"items": {"$ref": "#/definitions/Person"},
},
}
def test_expect_model_with_description(self, api, client):
person = api.model(
"Person",
{
"name": restx.fields.String,
"age": restx.fields.Integer,
"birthdate": restx.fields.DateTime,
},
)
@api.route("/model-as-dict/")
class ModelAsDict(restx.Resource):
@api.expect((person, "Body description"))
def post(self):
return {}
data = client.get_specs()
assert "definitions" in data
assert "Person" in data["definitions"]
assert data["definitions"]["Person"] == {
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"birthdate": {"type": "string", "format": "date-time"},
},
"type": "object",
}
op = data["paths"]["/model-as-dict/"]["post"]
assert len(op["parameters"]) == 1
parameter = op["parameters"][0]
assert parameter == {
"name": "payload",
"in": "body",
"required": True,
"description": "Body description",
"schema": {"$ref": "#/definitions/Person"},
}
def test_authorizations(self, app, client):
restx.Api(
app,
authorizations={
"apikey": {"type": "apiKey", "in": "header", "name": "X-API"}
},
)
# @api.route('/authorizations/')
# class ModelAsDict(restx.Resource):
# def get(self):
# return {}
# def post(self):
# return {}
data = client.get_specs()
assert "securityDefinitions" in data
assert "security" not in data
# path = data['paths']['/authorizations/']
# assert 'security' not in path['get']
# assert path['post']['security'] == {'apikey': []}
def test_single_root_security_string(self, app, client):
api = restx.Api(
app,
security="apikey",
authorizations={
"apikey": {"type": "apiKey", "in": "header", "name": "X-API"}
},
)
@api.route("/authorizations/")
class ModelAsDict(restx.Resource):
def post(self):
return {}
data = client.get_specs()
assert data["securityDefinitions"] == {
"apikey": {"type": "apiKey", "in": "header", "name": "X-API"}
}
assert data["security"] == [{"apikey": []}]
op = data["paths"]["/authorizations/"]["post"]
assert "security" not in op
def test_single_root_security_object(self, app, client):
security_definitions = {
"oauth2": {
"type": "oauth2",
"flow": "accessCode",
"tokenUrl": "https://somewhere.com/token",
"scopes": {
"read": "Grant read-only access",
"write": "Grant read-write access",
},
},
"implicit": {
"type": "oauth2",
"flow": "implicit",
"tokenUrl": "https://somewhere.com/token",
"scopes": {
"read": "Grant read-only access",
"write": "Grant read-write access",
},
},
}
api = restx.Api(
app,
security={"oauth2": "read", "implicit": ["read", "write"]},
authorizations=security_definitions,
)
@api.route("/authorizations/")
class ModelAsDict(restx.Resource):
def post(self):
return {}
data = client.get_specs()
assert data["securityDefinitions"] == security_definitions
assert data["security"] == [{"oauth2": ["read"], "implicit": ["read", "write"]}]
op = data["paths"]["/authorizations/"]["post"]
assert "security" not in op
def test_root_security_as_list(self, app, client):
security_definitions = {
"apikey": {"type": "apiKey", "in": "header", "name": "X-API"},
"oauth2": {
"type": "oauth2",
"flow": "accessCode",
"tokenUrl": "https://somewhere.com/token",
"scopes": {
"read": "Grant read-only access",
"write": "Grant read-write access",
},
},
}
api = restx.Api(
app,
security=["apikey", {"oauth2": "read"}],
authorizations=security_definitions,
)
@api.route("/authorizations/")
class ModelAsDict(restx.Resource):
def post(self):
return {}
data = client.get_specs()
assert data["securityDefinitions"] == security_definitions
assert data["security"] == [{"apikey": []}, {"oauth2": ["read"]}]
op = data["paths"]["/authorizations/"]["post"]
assert "security" not in op
def test_method_security(self, app, client):
api = restx.Api(
app,
authorizations={
"apikey": {"type": "apiKey", "in": "header", "name": "X-API"}
},
)
@api.route("/authorizations/")
class ModelAsDict(restx.Resource):
@api.doc(security=["apikey"])
def get(self):
return {}
@api.doc(security="apikey")
def post(self):
return {}
data = client.get_specs()
assert data["securityDefinitions"] == {
"apikey": {"type": "apiKey", "in": "header", "name": "X-API"}
}
assert "security" not in data
path = data["paths"]["/authorizations/"]
for method in "get", "post":
assert path[method]["security"] == [{"apikey": []}]
def test_security_override(self, app, client):
security_definitions = {
"apikey": {"type": "apiKey", "in": "header", "name": "X-API"},
"oauth2": {
"type": "oauth2",
"flow": "accessCode",
"tokenUrl": "https://somewhere.com/token",
"scopes": {
"read": "Grant read-only access",
"write": "Grant read-write access",
},
},
}
api = restx.Api(
app,
security=["apikey", {"oauth2": "read"}],
authorizations=security_definitions,
)
@api.route("/authorizations/")
class ModelAsDict(restx.Resource):
@api.doc(security=[{"oauth2": ["read", "write"]}])
def get(self):
return {}
data = client.get_specs()
assert data["securityDefinitions"] == security_definitions
op = data["paths"]["/authorizations/"]["get"]
assert op["security"] == [{"oauth2": ["read", "write"]}]
def test_security_nullify(self, app, client):
security_definitions = {
"apikey": {"type": "apiKey", "in": "header", "name": "X-API"},
"oauth2": {
"type": "oauth2",
"flow": "accessCode",
"tokenUrl": "https://somewhere.com/token",
"scopes": {
"read": "Grant read-only access",
"write": "Grant read-write access",
},
},
}
api = restx.Api(
app,
security=["apikey", {"oauth2": "read"}],
authorizations=security_definitions,
)
@api.route("/authorizations/")
class ModelAsDict(restx.Resource):
@api.doc(security=[])
def get(self):
return {}
@api.doc(security=None)
def post(self):
return {}
data = client.get_specs()
assert data["securityDefinitions"] == security_definitions
path = data["paths"]["/authorizations/"]
for method in "get", "post":
assert path[method]["security"] == []
def test_hidden_resource(self, api, client):
@api.route("/test/", endpoint="test", doc=False)
class TestResource(restx.Resource):
def get(self):
"""
GET operation
"""
return {}
@api.hide
@api.route("/test2/", endpoint="test2")
class TestResource2(restx.Resource):
def get(self):
"""
GET operation
"""
return {}
@api.doc(False)
@api.route("/test3/", endpoint="test3")
class TestResource3(restx.Resource):
def get(self):
"""
GET operation
"""
return {}
data = client.get_specs()
for path in "/test/", "/test2/", "/test3/":
assert path not in data["paths"]
resp = client.get(path)
assert resp.status_code == 200
def test_hidden_resource_from_namespace(self, api, client):
ns = api.namespace("ns")
@ns.route("/test/", endpoint="test", doc=False)
class TestResource(restx.Resource):
def get(self):
"""
GET operation
"""
return {}
data = client.get_specs()
assert "/ns/test/" not in data["paths"]
resp = client.get("/ns/test/")
assert resp.status_code == 200
def test_hidden_methods(self, api, client):
@api.route("/test/", endpoint="test")
@api.doc(delete=False)
class TestResource(restx.Resource):
def get(self):
"""
GET operation
"""
return {}
@api.doc(False)
def post(self):
"""POST operation.
Should be ignored
"""
return {}
@api.hide
def put(self):
"""PUT operation. Should be ignored"""
return {}
def delete(self):
return {}
data = client.get_specs()
path = data["paths"]["/test/"]
assert "get" in path
assert "post" not in path
assert "put" not in path
for method in "GET", "POST", "PUT":
resp = client.open("/test/", method=method)
assert resp.status_code == 200
def test_produces_method(self, api, client):
@api.route("/test/", endpoint="test")
class TestResource(restx.Resource):
def get(self):
pass
@api.produces(["application/octet-stream"])
def post(self):
pass
data = client.get_specs()
get_operation = data["paths"]["/test/"]["get"]
assert "produces" not in get_operation
post_operation = data["paths"]["/test/"]["post"]
assert "produces" in post_operation
assert post_operation["produces"] == ["application/octet-stream"]
def test_deprecated_resource(self, api, client):
@api.deprecated
@api.route("/test/", endpoint="test")
class TestResource(restx.Resource):
def get(self):
pass
def post(self):
pass
data = client.get_specs()
resource = data["paths"]["/test/"]
for operation in resource.values():
assert "deprecated" in operation
assert operation["deprecated"] is True
def test_deprecated_method(self, api, client):
@api.route("/test/", endpoint="test")
class TestResource(restx.Resource):
def get(self):
pass
@api.deprecated
def post(self):
pass
data = client.get_specs()
get_operation = data["paths"]["/test/"]["get"]
assert "deprecated" not in get_operation
post_operation = data["paths"]["/test/"]["post"]
assert "deprecated" in post_operation
assert post_operation["deprecated"] is True
def test_vendor_as_kwargs(self, api, client):
@api.route("/vendor_fields", endpoint="vendor_fields")
class TestResource(restx.Resource):
@api.vendor(integration={"integration1": "1"})
def get(self):
return {}
data = client.get_specs()
assert "/vendor_fields" in data["paths"]
path = data["paths"]["/vendor_fields"]["get"]
assert "x-integration" in path
assert path["x-integration"] == {"integration1": "1"}
def test_vendor_as_dict(self, api, client):
@api.route("/vendor_fields", endpoint="vendor_fields")
class TestResource(restx.Resource):
@api.vendor(
{
"x-some-integration": {"integration1": "1"},
"another-integration": True,
},
{"third-integration": True},
)
def get(self, age):
return {}
data = client.get_specs()
assert "/vendor_fields" in data["paths"]
path = data["paths"]["/vendor_fields"]["get"]
assert "x-some-integration" in path
assert path["x-some-integration"] == {"integration1": "1"}
assert "x-another-integration" in path
assert path["x-another-integration"] is True
assert "x-third-integration" in path
assert path["x-third-integration"] is True
def test_method_restrictions(self, api, client):
@api.route("/foo/bar", endpoint="foo")
@api.route("/bar", methods=["GET"], endpoint="bar")
class TestResource(restx.Resource):
def get(self):
pass
def post(self):
pass
data = client.get_specs()
path = data["paths"]["/foo/bar"]
assert "get" in path
assert "post" in path
path = data["paths"]["/bar"]
assert "get" in path
assert "post" not in path
def test_multiple_routes_inherit_doc(self, api, client):
@api.route("/foo/bar")
@api.route("/bar")
@api.doc(description="an endpoint")
class TestResource(restx.Resource):
def get(self):
pass
data = client.get_specs()
path = data["paths"]["/foo/bar"]
assert path["get"]["description"] == "an endpoint"
path = data["paths"]["/bar"]
assert path["get"]["description"] == "an endpoint"
def test_multiple_routes_individual_doc(self, api, client):
@api.route("/foo/bar", doc={"description": "the same endpoint"})
@api.route("/bar", doc={"description": "an endpoint"})
class TestResource(restx.Resource):
def get(self):
pass
data = client.get_specs()
path = data["paths"]["/foo/bar"]
assert path["get"]["description"] == "the same endpoint"
path = data["paths"]["/bar"]
assert path["get"]["description"] == "an endpoint"
def test_multiple_routes_override_doc(self, api, client):
@api.route("/foo/bar", doc={"description": "the same endpoint"})
@api.route("/bar")
@api.doc(description="an endpoint")
class TestResource(restx.Resource):
def get(self):
pass
data = client.get_specs()
path = data["paths"]["/foo/bar"]
assert path["get"]["description"] == "the same endpoint"
path = data["paths"]["/bar"]
assert path["get"]["description"] == "an endpoint"
def test_multiple_routes_no_doc_same_operationIds(self, api, client):
@api.route("/foo/bar")
@api.route("/bar")
class TestResource(restx.Resource):
def get(self):
pass
data = client.get_specs()
expected_operation_id = "get_test_resource"
path = data["paths"]["/foo/bar"]
assert path["get"]["operationId"] == expected_operation_id
path = data["paths"]["/bar"]
assert path["get"]["operationId"] == expected_operation_id
def test_multiple_routes_with_doc_unique_operationIds(self, api, client):
@api.route(
"/foo/bar",
doc={"description": "I should be treated separately"},
)
@api.route("/bar")
class TestResource(restx.Resource):
def get(self):
pass
data = client.get_specs()
path = data["paths"]["/foo/bar"]
assert path["get"]["operationId"] == "get_test_resource_/foo/bar"
path = data["paths"]["/bar"]
assert path["get"]["operationId"] == "get_test_resource"
def test_mutltiple_routes_merge_doc(self, api, client):
@api.route("/foo/bar", doc={"description": "the same endpoint"})
@api.route("/bar", doc={"description": False})
@api.doc(security=[{"oauth2": ["read", "write"]}])
class TestResource(restx.Resource):
def get(self):
pass
data = client.get_specs()
path = data["paths"]["/foo/bar"]
assert path["get"]["description"] == "the same endpoint"
assert path["get"]["security"] == [{"oauth2": ["read", "write"]}]
path = data["paths"]["/bar"]
assert "description" not in path["get"]
assert path["get"]["security"] == [{"oauth2": ["read", "write"]}]
def test_multiple_routes_deprecation(self, api, client):
@api.route("/foo/bar", doc={"deprecated": True})
@api.route("/bar")
class TestResource(restx.Resource):
def get(self):
pass
data = client.get_specs()
path = data["paths"]["/foo/bar"]
assert path["get"]["deprecated"] is True
path = data["paths"]["/bar"]
assert "deprecated" not in path["get"]
@pytest.mark.parametrize("path_name", ["/name/{age}/", "/first-name/{age}/"])
def test_multiple_routes_explicit_parameters_override(self, path_name, api, client):
@api.route("/name/<int:age>/", endpoint="by-name")
@api.route("/first-name/<int:age>/")
@api.doc(
params={
"q": {
"type": "string",
"in": "query",
"description": "Overriden description",
},
"age": {"description": "An age"},
}
)
class ByNameResource(restx.Resource):
@api.doc(params={"q": {"description": "A query string"}})
def get(self, age):
return {}
def post(self, age):
pass
data = client.get_specs()
assert path_name in data["paths"]
path = data["paths"][path_name]
assert len(path["parameters"]) == 1
by_name = dict((p["name"], p) for p in path["parameters"])
parameter = by_name["age"]
assert parameter["name"] == "age"
assert parameter["type"] == "integer"
assert parameter["in"] == "path"
assert parameter["required"] is True
assert parameter["description"] == "An age"
# Don't duplicate parameters
assert "q" not in by_name
get = path["get"]
assert len(get["parameters"]) == 1
parameter = get["parameters"][0]
assert parameter["name"] == "q"
assert parameter["type"] == "string"
assert parameter["in"] == "query"
assert parameter["description"] == "A query string"
post = path["post"]
assert len(post["parameters"]) == 1
parameter = post["parameters"][0]
assert parameter["name"] == "q"
assert parameter["type"] == "string"
assert parameter["in"] == "query"
assert parameter["description"] == "Overriden description"
class SwaggerDeprecatedTest(object):
def test_doc_parser_parameters(self, api):
parser = api.parser()
parser.add_argument("param", type=int, help="Some param")
with pytest.warns(DeprecationWarning):
@api.route("/with-parser/")
class WithParserResource(restx.Resource):
@api.doc(parser=parser)
def get(self):
return {}
assert "parser" not in WithParserResource.get.__apidoc__
assert "expect" in WithParserResource.get.__apidoc__
doc_parser = WithParserResource.get.__apidoc__["expect"][0]
assert doc_parser.__schema__ == parser.__schema__
def test_doc_method_parser_on_class(self, api):
parser = api.parser()
parser.add_argument("param", type=int, help="Some param")
with pytest.warns(DeprecationWarning):
@api.route("/with-parser/")
@api.doc(get={"parser": parser})
class WithParserResource(restx.Resource):
def get(self):
return {}
def post(self):
return {}
assert "parser" not in WithParserResource.__apidoc__["get"]
assert "expect" in WithParserResource.__apidoc__["get"]
doc_parser = WithParserResource.__apidoc__["get"]["expect"][0]
assert doc_parser.__schema__ == parser.__schema__
def test_doc_body_as_tuple(self, api):
fields = api.model(
"Person",
{
"name": restx.fields.String,
"age": restx.fields.Integer,
"birthdate": restx.fields.DateTime,
},
)
with pytest.warns(DeprecationWarning):
@api.route("/model-as-dict/")
class ModelAsDict(restx.Resource):
@api.doc(body=(fields, "Body description"))
def post(self):
return {}
assert "body" not in ModelAsDict.post.__apidoc__
assert ModelAsDict.post.__apidoc__["expect"] == [(fields, "Body description")]
def test_build_request_body_parameters_schema(self):
parser = restx.reqparse.RequestParser()
parser.add_argument("test", type=int, location="headers")
parser.add_argument("test1", type=int, location="json")
parser.add_argument("test2", location="json")
body_params = [p for p in parser.__schema__ if p["in"] == "body"]
result = restx.swagger.build_request_body_parameters_schema(body_params)
assert result["name"] == "payload"
assert result["required"]
assert result["in"] == "body"
assert result["schema"]["type"] == "object"
assert result["schema"]["properties"]["test1"]["type"] == "integer"
assert result["schema"]["properties"]["test2"]["type"] == "string"
def test_expect_unused_model(self, app, api, client):
from flask_restx import fields
api.model(
"SomeModel",
{
"param": fields.String,
"count": fields.Integer,
},
)
@api.route("/with-parser/", endpoint="with-parser")
class WithParserResource(restx.Resource):
def get(self):
return {}
app.config["RESTX_INCLUDE_ALL_MODELS"] = True
data = client.get_specs()
assert "/with-parser/" in data["paths"]
path = data["paths"]["/with-parser/"]
assert "parameters" not in path
model = data["definitions"]["SomeModel"]
assert model == {
"properties": {"count": {"type": "integer"}, "param": {"type": "string"}},
"type": "object",
}
def test_not_expect_unused_model(self, app, api, client):
# This is the default configuration, RESTX_INCLUDE_ALL_MODELS=False
from flask_restx import fields
api.model(
"SomeModel",
{
"param": fields.String,
"count": fields.Integer,
},
)
@api.route("/with-parser/", endpoint="with-parser")
class WithParserResource(restx.Resource):
def get(self):
return {}
data = client.get_specs()
assert "/with-parser/" in data["paths"]
assert "definitions" not in data
path = data["paths"]["/with-parser/"]
assert "parameters" not in path
def test_nondefault_swagger_filename(self, app, client):
api = restx.Api(doc="/doc/test", default_swagger_filename="test.json")
ns = restx.Namespace("ns1")
@ns.route("/test1")
class Ns(restx.Resource):
@ns.doc("Docs")
def get(self):
pass
api.add_namespace(ns)
api.init_app(app)
resp = client.get("/test.json")
assert resp.status_code == 200
assert resp.content_type == "application/json"
resp = client.get("/doc/test")
assert resp.status_code == 200
assert resp.content_type == "text/html; charset=utf-8"
resp = client.get("/ns1/test1")
assert resp.status_code == 200