OpenAPI Metadata for API Endpoints (#1941)

- Updates the `/docs` and `/redoc` API endpoints to have better metadata,
including using Browsertrix favicon and our logo for the `/redoc` endpoint.
- add new logo file 'docs-logo.svg' to root

Based on info at:
https://fastapi.tiangolo.com/how-to/extending-openapi/
https://fastapi.tiangolo.com/tutorial/metadata/

---------
Co-authored-by: Henry Wilkinson <henry@wilkinson.graphics>
This commit is contained in:
Ilya Kreymer 2024-07-18 11:11:38 -07:00 committed by GitHub
parent 3bf7967754
commit b1ccdc4d16
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 94 additions and 7 deletions

View File

@ -11,6 +11,9 @@ from fastapi import FastAPI, HTTPException
from fastapi.responses import JSONResponse
from fastapi.routing import APIRouter
from fastapi.openapi.utils import get_openapi
from fastapi.openapi.docs import get_swagger_ui_html, get_redoc_html
from .db import init_db, await_db_and_migrations, update_and_prepare_db
from .emailsender import EmailSender
@ -34,18 +37,45 @@ from .subs import init_subs_api
from .crawlmanager import CrawlManager
from .utils import run_once_lock, register_exit_handler, is_bool
from .version import __version__
API_PREFIX = "/api"
app_root = FastAPI(
docs_url=API_PREFIX + "/docs",
redoc_url=API_PREFIX + "/redoc",
openapi_url=API_PREFIX + "/openapi.json",
)
OPENAPI_URL = API_PREFIX + "/openapi.json"
app_root = FastAPI(docs_url=None, redoc_url=None, OPENAPI_URL=OPENAPI_URL)
db_inited = {"inited": False}
# ============================================================================
def make_schema():
"""make custom openapi schema"""
schema = get_openapi(
title="Browsertrix",
description="""\
The Browsertrix API provides access to all aspects of the Browsertrix app.
See [https://docs.browsertrix.com/](https://docs.browsertrix.com/) for more info on deploying Browsertrix\
""",
summary="Browsertrix Crawling System API",
version=__version__,
terms_of_service="http://browsertrix.com/terms",
contact={
"name": "Browsertrix",
"url": "https://browsertrix.com/",
"email": "info@webrecorder.net",
},
license_info={
"name": "AGPL v3",
"url": "https://www.gnu.org/licenses/agpl-3.0.en.html",
},
routes=app_root.routes,
)
schema["info"]["x-logo"] = {"url": "/docs-logo.svg"}
return schema
# ============================================================================
# pylint: disable=too-many-locals, duplicate-code
def main():
@ -200,7 +230,6 @@ def main():
return settings
# internal routes
@app.get("/openapi.json", include_in_schema=False)
async def openapi() -> JSONResponse:
return JSONResponse(app_root.openapi())
@ -221,6 +250,31 @@ def main():
app_root.include_router(app, prefix=API_PREFIX)
# API Configurations -- needed to provide custom favicon
@app_root.get(API_PREFIX + "/docs", include_in_schema=False)
def overridden_swagger():
return get_swagger_ui_html(
openapi_url=OPENAPI_URL,
title="Browsertrix API",
swagger_favicon_url="/favicon.ico",
)
@app_root.get(API_PREFIX + "/redoc", include_in_schema=False)
def overridden_redoc():
return get_redoc_html(
openapi_url=OPENAPI_URL,
title="Browsertrix API",
redoc_favicon_url="/favicon.ico",
)
def get_api_schema():
if not app_root.openapi_schema:
app_root.openapi_schema = make_schema()
return app_root.openapi_schema
app_root.openapi = get_api_schema # type: ignore
# ============================================================================
@app_root.on_event("startup")

32
backend/test/test_api.py Normal file
View File

@ -0,0 +1,32 @@
import requests
from .conftest import API_PREFIX
def test_api_docs():
r = requests.get(f"{API_PREFIX}/docs")
assert r.status_code == 200
text = r.text
assert "<title>Browsertrix API</title>" in text
assert "/favicon.ico" in text
assert "/api/openapi.json" in text
def test_api_redoc():
r = requests.get(f"{API_PREFIX}/redoc")
assert r.status_code == 200
text = r.text
assert "<title>Browsertrix API</title>" in text
assert "/favicon.ico" in text
assert "/api/openapi.json" in text
def test_api_openapi():
r = requests.get(f"{API_PREFIX}/openapi.json")
assert r.status_code == 200
json = r.json()
assert json["info"]["title"] == "Browsertrix"
assert json["info"]["x-logo"]["url"] == "/docs-logo.svg"

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB