Skip to content

Commit

Permalink
Redirect special OpenAPI endpoints for custom base URLs (#72)
Browse files Browse the repository at this point in the history
Redirect all non-major version prefix URLs to major-version prefix URLs
for the special OpenAPI docs endpoints:
- docs_url: /extensions/docs
- redoc_url: /extensions/redoc
- openapi_url: /extensions/openapi.json
  • Loading branch information
CasperWA committed May 4, 2020
1 parent 1f52b6d commit c674879
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 5 deletions.
11 changes: 7 additions & 4 deletions aiida_optimade/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,24 @@
from optimade.server.middleware import EnsureQueryParamIntegrity
from optimade.server.routers.utils import BASE_URL_PREFIXES

from aiida_optimade.middleware import RedirectOpenApiDocs
from aiida_optimade.routers import (
info,
structures,
)
from aiida_optimade.utils import get_custom_base_url_path, OPEN_API_ENDPOINTS


if CONFIG.debug: # pragma: no cover
print("DEBUG MODE")


# Load AiiDA profile
PROFILE_NAME = os.getenv("AIIDA_PROFILE")
load_profile(PROFILE_NAME)
if CONFIG.debug: # pragma: no cover
print(f"AiiDA Profile: {PROFILE_NAME}")

DOCS_ENDPOINT_PREFIX = f"{get_custom_base_url_path()}{BASE_URL_PREFIXES['major']}"
APP = FastAPI(
title="OPTIMADE API for AiiDA",
description=(
Expand All @@ -43,15 +45,16 @@
"reproducible."
),
version=__api_version__,
docs_url=f"{BASE_URL_PREFIXES['major']}/extensions/docs",
redoc_url=f"{BASE_URL_PREFIXES['major']}/extensions/redoc",
openapi_url=f"{BASE_URL_PREFIXES['major']}/extensions/openapi.json",
docs_url=f"{DOCS_ENDPOINT_PREFIX}{OPEN_API_ENDPOINTS['docs']}",
redoc_url=f"{DOCS_ENDPOINT_PREFIX}{OPEN_API_ENDPOINTS['redoc']}",
openapi_url=f"{DOCS_ENDPOINT_PREFIX}{OPEN_API_ENDPOINTS['openapi']}",
)


# Add various middleware
APP.add_middleware(CORSMiddleware, allow_origins=["*"])
APP.add_middleware(EnsureQueryParamIntegrity)
APP.add_middleware(RedirectOpenApiDocs)


# Add various exception handlers
Expand Down
36 changes: 36 additions & 0 deletions aiida_optimade/middleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import urllib.parse

from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import RedirectResponse

from optimade.server.routers.utils import BASE_URL_PREFIXES

from aiida_optimade.utils import OPEN_API_ENDPOINTS


class RedirectOpenApiDocs(BaseHTTPMiddleware):
"""Redirect URLs from non-major version prefix URLs to major-version prefix URLs
This is relevant for the OpenAPI JSON, Docs, and ReDocs URLs.
"""

async def dispatch(self, request: Request, call_next):
parsed_url = urllib.parse.urlsplit(str(request.url))
for endpoint in OPEN_API_ENDPOINTS.values():
# Important to start with the longest (or full) URL prefix first.
for version_prefix in [
BASE_URL_PREFIXES["patch"],
BASE_URL_PREFIXES["minor"],
]:
if parsed_url.path.endswith(f"{version_prefix}{endpoint}"):
new_path = parsed_url.path.replace(
f"{version_prefix}", f"{BASE_URL_PREFIXES['major']}"
)
redirect_url = (
f"{parsed_url.scheme}://{parsed_url.netloc}{new_path}"
f"?{parsed_url.query}"
)
return RedirectResponse(redirect_url)
response = await call_next(request)
return response
23 changes: 23 additions & 0 deletions aiida_optimade/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
from typing import Tuple
import urllib.parse

from optimade.server.config import CONFIG


OPEN_API_ENDPOINTS = {
"docs": "/extensions/docs",
"redoc": "/extensions/redoc",
"openapi": "/extensions/openapi.json",
}


def retrieve_queryable_properties(
Expand Down Expand Up @@ -30,3 +40,16 @@ def retrieve_queryable_properties(
properties[name][extra_key] = value[extra_key]

return properties, all_properties


def get_custom_base_url_path():
"""Return path part of custom base URL"""
if CONFIG.base_url is not None:
res = urllib.parse.urlparse(CONFIG.base_url).path
else:
res = urllib.parse.urlparse(CONFIG.base_url).path.decode()

if res.endswith("/"):
res = res[:-1]

return res
2 changes: 1 addition & 1 deletion tests/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

# Use specific AiiDA profile
if os.getenv("AIIDA_PROFILE", None) is None:
os.environ["AIIDA_PROFILE"] = "optimade_v1_aiida_sqla"
os.environ["AIIDA_PROFILE"] = "optimade_sqla"

from optimade.models import (
ResponseMeta,
Expand Down

0 comments on commit c674879

Please sign in to comment.