From a35ecf2394ddaeaa109b795f71b393c6e848c051 Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Tue, 22 Dec 2020 11:18:47 +0200 Subject: [PATCH] Add Swagger UI on /swagger This includes a Swagger UI with an OpenAPI 3.0 JSON schema for easy interactive demonstration and testing of the API. The JSON schema was created with the standalone swagger-editor. Includes tests to make sure that the /swagger and /docs/openapi.json paths are acce- ssible. --- dspace_statistics_api/app.py | 23 + dspace_statistics_api/docs/openapi.json | 586 ++++++++++++++++++++++++ poetry.lock | 78 +++- pyproject.toml | 1 + tests/test_api_docs.py | 18 + 5 files changed, 705 insertions(+), 1 deletion(-) create mode 100644 dspace_statistics_api/docs/openapi.json diff --git a/dspace_statistics_api/app.py b/dspace_statistics_api/app.py index b8aa59e..d0052e3 100644 --- a/dspace_statistics_api/app.py +++ b/dspace_statistics_api/app.py @@ -1,5 +1,6 @@ import falcon import psycopg2.extras +from falcon_swagger_ui import register_swaggerui_app from .database import DatabaseManager from .stats import get_downloads, get_views @@ -14,6 +15,14 @@ class RootResource: resp.body = f.read() +class OpenAPIJSONResource: + def on_get(self, req, resp): + resp.status = falcon.HTTP_200 + resp.content_type = "text/html" + with open("dspace_statistics_api/docs/openapi.json", "r") as f: + resp.body = f.read() + + class AllStatisticsResource: @falcon.before(set_statistics_scope) def on_get(self, req, resp): @@ -178,4 +187,18 @@ api.add_route("/community/{id_:uuid}", SingleStatisticsResource()) api.add_route("/collections", AllStatisticsResource()) api.add_route("/collection/{id_:uuid}", SingleStatisticsResource()) +# Swagger configuration +SWAGGERUI_URL = "/swagger" # without trailing slash +SCHEMA_URL = "/docs/openapi.json" +api.add_route("/docs/openapi.json", OpenAPIJSONResource()) + +register_swaggerui_app( + api, + SWAGGERUI_URL, + SCHEMA_URL, + config={ + "supportedSubmitMethods": ["get", "post"], + }, +) + # vim: set sw=4 ts=4 expandtab: diff --git a/dspace_statistics_api/docs/openapi.json b/dspace_statistics_api/docs/openapi.json new file mode 100644 index 0000000..4906f0b --- /dev/null +++ b/dspace_statistics_api/docs/openapi.json @@ -0,0 +1,586 @@ +{ + "openapi": "3.0.3", + "info": { + "version": "1.4.0-dev", + "title": "DSpace Statistics API", + "description": "A [Falcon-based](https://falcon.readthedocs.io/) web application to make DSpace's item, community, and collection statistics available via a simple REST API. This Swagger interface is powered by [falcon-swagger-ui](https://github.com/rdidyk/falcon-swagger-ui).", + "license": { + "name": "GPLv3.0", + "url": "https://www.gnu.org/licenses/gpl-3.0.en.html" + } + }, + "paths": { + "/item/{item_uuid}": { + "get": { + "summary": "Statistics for a specific item", + "operationId": "getItem", + "tags": [ + "item" + ], + "parameters": [ + { + "name": "item_uuid", + "in": "path", + "required": true, + "description": "The UUID of the item to retrieve", + "schema": { + "type": "string", + "format": "uuid", + "example": "9596aeff-0b90-47d3-9fec-02d578920507" + } + } + ], + "responses": { + "200": { + "description": "Expected response to a valid request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SingleElementResponse" + } + } + } + }, + "404": { + "description": "Item not found" + } + } + } + }, + "/items": { + "get": { + "summary": "Get statistics for all items", + "operationId": "getItems", + "tags": [ + "items" + ], + "parameters": [ + { + "name": "limit", + "in": "query", + "description": "How many items to return at once (optional)", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "minimum": 1, + "maximum": 100, + "default": 100, + "example": 100 + } + }, + { + "name": "page", + "in": "query", + "description": "Page of results to start on (optional)", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "minimum": 0, + "default": 0, + "example": 0 + } + } + ], + "responses": { + "200": { + "description": "A paged array of items", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SingleElementResponse" + } + } + } + }, + "400": { + "description": "Bad request" + } + } + }, + "post": { + "summary": "Get statistics for a list of items with an optional date range", + "operationId": "postItems", + "tags": [ + "items" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "limit": { + "type": "integer", + "format": "int32", + "minimum": 1, + "maximum": 100, + "default": 100 + }, + "page": { + "type": "integer", + "format": "int32", + "minimum": 0, + "default": 0 + }, + "dateFrom": { + "type": "string", + "format": "date" + }, + "dateTo": { + "type": "string", + "format": "date" + }, + "items": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + }, + "example": { + "limit": 100, + "page": 5, + "dateFrom": "2020-01-01T00:00:00Z", + "dateTo": "2020-12-31T00:00:00Z", + "items": [ + "f44cf173-2344-4eb2-8f00-ee55df32c76f", + "2324aa41-e9de-4a2b-bc36-16241464683e", + "8542f9da-9ce1-4614-abf4-f2e3fdb4b305", + "0fe573e7-042a-4240-a4d9-753b61233908" + ] + } + } + } + } + }, + "responses": { + "200": { + "description": "Expected response to a valid request", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "currentPage": { + "type": "integer", + "format": "int32" + }, + "limit": { + "type": "integer", + "format": "int32" + }, + "totalPages": { + "type": "integer", + "format": "int32" + }, + "statistics": { + "$ref": "#/components/schemas/ListOfElements" + } + } + } + } + } + }, + "400": { + "description": "Bad request" + } + } + } + }, + "/community/{community_uuid}": { + "get": { + "summary": "Statistics for a specific community", + "operationId": "getCommunity", + "tags": [ + "community" + ], + "parameters": [ + { + "name": "community_uuid", + "in": "path", + "required": true, + "description": "The UUID of the community to retrieve", + "schema": { + "type": "string", + "format": "uuid", + "example": "bde7139c-d321-46bb-aef6-ae70799e5edb" + } + } + ], + "responses": { + "200": { + "description": "Expected response to a valid request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SingleElementResponse" + } + } + } + }, + "404": { + "description": "Community not found" + } + } + } + }, + "/communities": { + "get": { + "summary": "Get statistics for all communities", + "operationId": "getCommunities", + "tags": [ + "communities" + ], + "parameters": [ + { + "name": "limit", + "in": "query", + "description": "How many communities to return at once (optional)", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "minimum": 1, + "maximum": 100, + "default": 100, + "example": 100 + } + }, + { + "name": "page", + "in": "query", + "description": "Zero-based page of results to start on (optional)", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "minimum": 0, + "default": 0, + "example": 0 + } + } + ], + "responses": { + "200": { + "description": "A paged array of communities", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SingleElementResponse" + } + } + } + }, + "400": { + "description": "Bad request" + } + } + }, + "post": { + "summary": "Get statistics for a list of communities with an optional date range", + "operationId": "postCommunities", + "tags": [ + "communities" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "limit": { + "type": "integer", + "format": "int32", + "minimum": 1, + "maximum": 100, + "default": 100 + }, + "page": { + "type": "integer", + "format": "int32", + "minimum": 0, + "default": 0 + }, + "dateFrom": { + "type": "string", + "format": "date" + }, + "dateTo": { + "type": "string", + "format": "date" + }, + "communities": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + }, + "example": { + "limit": 100, + "page": 0, + "dateFrom": "2020-01-01T00:00:00Z", + "dateTo": "2020-12-31T00:00:00Z", + "communities": [ + "bde7139c-d321-46bb-aef6-ae70799e5edb", + "8a8aeed1-077e-4360-bdf8-a5f3020193b1", + "47d0498a-203c-407d-afb8-1d44bf29badc", + "d3fe99a9-e27d-4035-9339-084c93228c82" + ] + } + } + } + } + }, + "responses": { + "200": { + "description": "Expected response to a valid request", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "currentPage": { + "type": "integer", + "format": "int32" + }, + "limit": { + "type": "integer", + "format": "int32" + }, + "totalPages": { + "type": "integer", + "format": "int32" + }, + "statistics": { + "$ref": "#/components/schemas/ListOfElements" + } + } + } + } + } + }, + "400": { + "description": "Bad request" + } + } + } + }, + "/collection/{collection_uuid}": { + "get": { + "summary": "Statistics for a specific collection", + "operationId": "getCollection", + "tags": [ + "collection" + ], + "parameters": [ + { + "name": "collection_uuid", + "in": "path", + "required": true, + "description": "The UUID of the collection to retrieve", + "schema": { + "type": "string", + "format": "uuid", + "example": "49dc95d8-bf2f-4e68-b30f-41ea266c37ae" + } + } + ], + "responses": { + "200": { + "description": "Expected response to a valid request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SingleElementResponse" + } + } + } + }, + "404": { + "description": "Collection not found" + } + } + } + }, + "/collections": { + "get": { + "summary": "Get statistics for all collections", + "operationId": "getCollections", + "tags": [ + "collections" + ], + "parameters": [ + { + "name": "limit", + "in": "query", + "description": "How many collections to return at once (optional)", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "minimum": 1, + "maximum": 100, + "default": 100, + "example": 100 + } + }, + { + "name": "page", + "in": "query", + "description": "Zero-based page of results to start on (optional)", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "minimum": 0, + "default": 0, + "example": 0 + } + } + ], + "responses": { + "200": { + "description": "A paged array of collections", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SingleElementResponse" + } + } + } + }, + "400": { + "description": "Bad request" + } + } + }, + "post": { + "summary": "Get statistics for a list of collections with an optional date range", + "operationId": "postCollections", + "tags": [ + "collections" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "limit": { + "type": "integer", + "format": "int32", + "minimum": 1, + "maximum": 100, + "default": 100 + }, + "page": { + "type": "integer", + "format": "int32", + "minimum": 0, + "default": 0 + }, + "dateFrom": { + "type": "string", + "format": "date" + }, + "dateTo": { + "type": "string", + "format": "date" + }, + "collections": { + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + }, + "example": { + "limit": 100, + "page": 2, + "dateFrom": "2020-01-01T00:00:00Z", + "dateTo": "2020-12-31T00:00:00Z", + "collections": [ + "5eeef6cf-b91b-42d0-9549-ea61bc8a758f", + "6aac3269-b4a9-4924-a24d-9e6ee2b410d2", + "551698dd-cd2b-4327-948e-54b5eb6deda5", + "39358713-bbaf-4149-a453-e2b18c09fd5d" + ] + } + } + } + } + }, + "responses": { + "200": { + "description": "Expected response to a valid request", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "currentPage": { + "type": "integer", + "format": "int32" + }, + "limit": { + "type": "integer", + "format": "int32" + }, + "totalPages": { + "type": "integer", + "format": "int32" + }, + "statistics": { + "$ref": "#/components/schemas/ListOfElements" + } + } + } + } + } + }, + "400": { + "description": "Bad request" + } + } + } + } + }, + "components": { + "schemas": { + "SingleElementResponse": { + "type": "object", + "required": [ + "id", + "views", + "downloads" + ], + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "views": { + "type": "integer", + "example": 450 + }, + "downloads": { + "type": "integer", + "example": 1337 + } + } + }, + "ListOfElements": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SingleElementResponse" + } + } + } + } +} \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index f99ef13..6342437 100644 --- a/poetry.lock +++ b/poetry.lock @@ -132,6 +132,18 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +[[package]] +name = "falcon-swagger-ui" +version = "1.2.1" +description = "Swagger UI Application for Falcon" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +falcon = "*" +Jinja2 = "*" + [[package]] name = "flake8" version = "3.8.4" @@ -273,6 +285,28 @@ testing = ["Django (<3.1)", "colorama", "docopt", "pytest (>=3.9.0,<5.0.0)"] [package.dependencies] parso = ">=0.7.0,<0.8.0" +[[package]] +name = "jinja2" +version = "2.11.2" +description = "A very fast and expressive template engine." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.extras] +i18n = ["Babel (>=0.8)"] + +[package.dependencies] +MarkupSafe = ">=0.23" + +[[package]] +name = "markupsafe" +version = "1.1.1" +description = "Safely add untrusted strings to HTML/XML markup." +category = "main" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" + [[package]] name = "mccabe" version = "0.6.1" @@ -555,7 +589,7 @@ testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2.3)", "pyt [metadata] lock-version = "1.0" python-versions = "^3.6" -content-hash = "3cd45aacbfab0e85f74c7a010443432f4d6bf0f6cd3bbb84e11c8c7ea20a4613" +content-hash = "1d56758c9e3aa4586109e8aaf4def576d3506385bc749b97e8518f936f2e91ca" [metadata.files] appdirs = [ @@ -621,6 +655,9 @@ falcon = [ {file = "falcon-2.0.0-py2.py3-none-any.whl", hash = "sha256:54f2cb4b687035b2a03206dbfc538055cc48b59a953187b0458aa1b574d47b53"}, {file = "falcon-2.0.0.tar.gz", hash = "sha256:eea593cf466b9c126ce667f6d30503624ef24459f118c75594a69353b6c3d5fc"}, ] +falcon-swagger-ui = [ + {file = "falcon_swagger_ui-1.2.1-py3-none-any.whl", hash = "sha256:2514e6cb403e87e49a1527764cf090c82885185cc650b7ab5cefa8ebe89af8b8"}, +] flake8 = [ {file = "flake8-3.8.4-py2.py3-none-any.whl", hash = "sha256:749dbbd6bfd0cf1318af27bf97a14e28e5ff548ef8e5b1566ccfb25a11e7c839"}, {file = "flake8-3.8.4.tar.gz", hash = "sha256:aadae8761ec651813c24be05c6f7b4680857ef6afaae4651a4eccaef97ce6c3b"}, @@ -657,6 +694,45 @@ jedi = [ {file = "jedi-0.17.2-py2.py3-none-any.whl", hash = "sha256:98cc583fa0f2f8304968199b01b6b4b94f469a1f4a74c1560506ca2a211378b5"}, {file = "jedi-0.17.2.tar.gz", hash = "sha256:86ed7d9b750603e4ba582ea8edc678657fb4007894a12bcf6f4bb97892f31d20"}, ] +jinja2 = [ + {file = "Jinja2-2.11.2-py2.py3-none-any.whl", hash = "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"}, + {file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"}, +] +markupsafe = [ + {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"}, + {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"}, + {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183"}, + {file = "MarkupSafe-1.1.1-cp27-cp27m-win32.whl", hash = "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b"}, + {file = "MarkupSafe-1.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e"}, + {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f"}, + {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-macosx_10_6_intel.whl", hash = "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-win32.whl", hash = "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21"}, + {file = "MarkupSafe-1.1.1-cp34-cp34m-win_amd64.whl", hash = "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"}, + {file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"}, + {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, +] mccabe = [ {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, diff --git a/pyproject.toml b/pyproject.toml index 4c4bcd0..73981f4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,7 @@ gunicorn = "^20.0.4" falcon = "^2.0.0" psycopg2-binary = "^2.8.6" requests = "^2.24.0" +falcon-swagger-ui = "^1.2.1" [tool.poetry.dev-dependencies] ipython = { version = "^7.18.1", python = "^3.7" } diff --git a/tests/test_api_docs.py b/tests/test_api_docs.py index 84ca461..0878911 100644 --- a/tests/test_api_docs.py +++ b/tests/test_api_docs.py @@ -16,3 +16,21 @@ def test_get_docs(client): assert isinstance(response.content, bytes) assert response.status_code == 200 + + +def test_get_openapi_json(client): + """Test requesting the OpenAPI JSON schema.""" + + response = client.simulate_get("/docs/openapi.json") + + assert isinstance(response.content, bytes) + assert response.status_code == 200 + + +def test_get_swagger_ui(client): + """Test requesting the Swagger UI.""" + + response = client.simulate_get("/swagger") + + assert isinstance(response.content, bytes) + assert response.status_code == 200