From be83514de1a53ef872b8ea4ebd60fe8f40643ef6 Mon Sep 17 00:00:00 2001 From: Alan Orth Date: Wed, 23 Dec 2020 13:25:17 +0200 Subject: [PATCH] Re-work Swagger UI configuration It turns out that Swagger UI mostly does the "right" thing for our use cases here, but it assumes that API paths are relative to the root of the host where it is being served. This works in the local development environment because we are serving on "/", but it does not work in production where the API is deployed beneath the DSpace REST API, for example at "/rest/statistics". The solution here is to allow configuration of the DSpace Statistics API path and use that when registering the Swagger UI as well as in a new "server" block in the OpenAPI JSON schema. By default it is configured to work out of the box in a development environment. Set the DSPACE_STATISTICS_API_URL environment variable to something like "/rest/statistics" when running in production. --- dspace_statistics_api/app.py | 42 ++++++++++++++++++++++++++------- dspace_statistics_api/config.py | 12 +++++----- 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/dspace_statistics_api/app.py b/dspace_statistics_api/app.py index 4e50d90..00722f5 100644 --- a/dspace_statistics_api/app.py +++ b/dspace_statistics_api/app.py @@ -1,13 +1,13 @@ +import json + import falcon import psycopg2.extras from falcon_swagger_ui import register_swaggerui_app +from .config import DSPACE_STATISTICS_API_URL, VERSION from .database import DatabaseManager from .stats import get_downloads, get_views from .util import set_statistics_scope, validate_post_parameters -from .config import VERSION -from .config import SWAGGERUI_URL -from .config import SWAGGERUI_SCHEMA_URL class RootResource: @@ -31,7 +31,20 @@ class OpenAPIJSONResource: 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() + # Load the openapi.json schema + data = json.load(f) + + # Swagger assumes your API is at the root of the current host unless + # you configure a "servers" block in the schema. The problem is that + # I want this to work in both development and production, so we need + # to make this configurable. + # + # If the DSPACE_STATISTICS_API_URL is configured then we will add a + # server entry to the openapi.json schema before sending it. + if DSPACE_STATISTICS_API_URL != "": + data["servers"] = [{"url": DSPACE_STATISTICS_API_URL}] + + resp.body = json.dumps(data) class AllStatisticsResource: @@ -199,16 +212,29 @@ api.add_route("/community/{id_:uuid}", SingleStatisticsResource()) api.add_route("/collections", AllStatisticsResource()) api.add_route("/collection/{id_:uuid}", SingleStatisticsResource()) -# Swagger configuration -api.add_route(SWAGGERUI_SCHEMA_URL, OpenAPIJSONResource()) +# Route to the Swagger UI OpenAPI schema +api.add_route("/docs/openapi.json", OpenAPIJSONResource()) + +# Path to host the Swagger UI. Keep in mind that Falcon will add a route for +# this automatically when we register Swagger and the path will be relative +# to the Falcon app like all other routes, not the absolute root. +SWAGGERUI_PATH = "/swagger" + +# The *absolute* path to the OpenJSON schema. This must be absolute because +# it will be requested by the client and must resolve absolutely. Note: the +# name of this variable is misleading because it is actually the schema URL +# but we pass it into the register_swaggerui_app() function as the api_url +# parameter. +SWAGGERUI_API_URL = f"{DSPACE_STATISTICS_API_URL}/docs/openapi.json" register_swaggerui_app( api, - SWAGGERUI_URL, - SWAGGERUI_SCHEMA_URL, + SWAGGERUI_PATH, + SWAGGERUI_API_URL, config={ "supportedSubmitMethods": ["get", "post"], }, + uri_prefix=DSPACE_STATISTICS_API_URL, ) # vim: set sw=4 ts=4 expandtab: diff --git a/dspace_statistics_api/config.py b/dspace_statistics_api/config.py index f397be4..2365b7a 100644 --- a/dspace_statistics_api/config.py +++ b/dspace_statistics_api/config.py @@ -9,12 +9,12 @@ DATABASE_PASS = os.environ.get("DATABASE_PASS", "dspacestatistics") DATABASE_HOST = os.environ.get("DATABASE_HOST", "localhost") DATABASE_PORT = os.environ.get("DATABASE_PORT", "5432") -# SwaggerUI configuration - -# URI path where the Swagger UI should be available (without trailing slash) -SWAGGERUI_URL = os.environ.get("SWAGGERUI_URL", "/swagger") -# URI path to the OpenAPI JSON schema -SWAGGERUI_SCHEMA_URL = os.environ.get("SWAGGERUI_SCHEMA_URL", "/docs/openapi.json") +# URL to DSpace Statistics API, which will be used as a prefix to API calls in +# the Swagger UI. An empty string will allow this to work out of the box in a +# local development environment, but for production it should be set to a value +# like "/rest/statistics", assuming that the statistics API is deployed next to +# the vanilla DSpace REST API. +DSPACE_STATISTICS_API_URL = os.environ.get("DSPACE_STATISTICS_API_URL", "") VERSION = "1.4.0-dev"