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//", 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//", 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//", 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//", 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//") @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//", 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//", 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//", 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//", 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//", 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//", 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//", endpoint="by-name") @api.route("/first-name//") @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