2020-01-21 17:27:57 -07:00
|
|
|
"""hyperglass REST API & Web UI."""
|
|
|
|
|
|
|
|
# Standard Library Imports
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
# Third Party Imports
|
|
|
|
from fastapi import FastAPI
|
2020-01-26 02:14:42 -07:00
|
|
|
from fastapi.exceptions import RequestValidationError
|
2020-01-21 17:27:57 -07:00
|
|
|
from fastapi.openapi.utils import get_openapi
|
|
|
|
from starlette.exceptions import HTTPException as StarletteHTTPException
|
|
|
|
from starlette.middleware.cors import CORSMiddleware
|
|
|
|
from starlette.responses import UJSONResponse
|
|
|
|
from starlette.staticfiles import StaticFiles
|
|
|
|
|
|
|
|
# Project Imports
|
|
|
|
from hyperglass.api.error_handlers import app_handler
|
2020-01-26 02:14:42 -07:00
|
|
|
from hyperglass.api.error_handlers import default_handler
|
2020-01-21 17:27:57 -07:00
|
|
|
from hyperglass.api.error_handlers import http_handler
|
2020-01-26 02:14:42 -07:00
|
|
|
from hyperglass.api.error_handlers import validation_handler
|
2020-01-21 17:27:57 -07:00
|
|
|
from hyperglass.api.events import on_shutdown
|
|
|
|
from hyperglass.api.events import on_startup
|
|
|
|
from hyperglass.api.routes import docs
|
|
|
|
from hyperglass.api.routes import query
|
|
|
|
from hyperglass.configuration import URL_DEV
|
|
|
|
from hyperglass.configuration import params
|
|
|
|
from hyperglass.constants import __version__
|
|
|
|
from hyperglass.exceptions import HyperglassError
|
|
|
|
from hyperglass.models.response import QueryResponse
|
|
|
|
from hyperglass.util import log
|
|
|
|
|
|
|
|
STATIC_DIR = Path(__file__).parent.parent / "static"
|
|
|
|
UI_DIR = STATIC_DIR / "ui"
|
|
|
|
IMAGES_DIR = STATIC_DIR / "images"
|
|
|
|
|
|
|
|
ASGI_PARAMS = {
|
2020-01-28 08:59:27 -07:00
|
|
|
"host": str(params.listen_address),
|
|
|
|
"port": params.listen_port,
|
|
|
|
"debug": params.debug,
|
2020-01-21 17:27:57 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
# Main App Definition
|
|
|
|
app = FastAPI(
|
2020-01-28 08:59:27 -07:00
|
|
|
debug=params.debug,
|
|
|
|
title=params.site_title,
|
|
|
|
description=params.site_description,
|
2020-01-21 17:27:57 -07:00
|
|
|
version=__version__,
|
|
|
|
default_response_class=UJSONResponse,
|
|
|
|
docs_url=None,
|
|
|
|
redoc_url=None,
|
2020-01-28 08:59:27 -07:00
|
|
|
openapi_url=params.docs.openapi_url,
|
2020-01-21 17:27:57 -07:00
|
|
|
)
|
|
|
|
|
2020-01-21 20:03:47 -07:00
|
|
|
# Add Event Handlers
|
|
|
|
for startup in on_startup:
|
|
|
|
app.add_event_handler("startup", startup)
|
|
|
|
|
|
|
|
for shutdown in on_shutdown:
|
|
|
|
app.add_event_handler("shutdown", shutdown)
|
|
|
|
|
2020-01-21 17:27:57 -07:00
|
|
|
# HTTP Error Handler
|
|
|
|
app.add_exception_handler(StarletteHTTPException, http_handler)
|
|
|
|
|
|
|
|
# Backend Application Error Handler
|
|
|
|
app.add_exception_handler(HyperglassError, app_handler)
|
|
|
|
|
2020-01-26 02:14:42 -07:00
|
|
|
# Validation Error Handler
|
|
|
|
app.add_exception_handler(RequestValidationError, validation_handler)
|
|
|
|
|
|
|
|
# Uncaught Error Handler
|
|
|
|
app.add_exception_handler(Exception, default_handler)
|
|
|
|
|
2020-01-21 17:27:57 -07:00
|
|
|
|
|
|
|
def _custom_openapi():
|
|
|
|
"""Generate custom OpenAPI config."""
|
|
|
|
openapi_schema = get_openapi(
|
2020-01-28 08:59:27 -07:00
|
|
|
title=params.site_title,
|
2020-01-21 17:27:57 -07:00
|
|
|
version=__version__,
|
2020-01-28 08:59:27 -07:00
|
|
|
description=params.site_description,
|
2020-01-21 17:27:57 -07:00
|
|
|
routes=app.routes,
|
|
|
|
)
|
|
|
|
app.openapi_schema = openapi_schema
|
|
|
|
return app.openapi_schema
|
|
|
|
|
|
|
|
|
|
|
|
app.openapi = _custom_openapi
|
|
|
|
|
2020-01-28 08:59:27 -07:00
|
|
|
if params.docs.enable:
|
2020-01-21 17:27:57 -07:00
|
|
|
log.debug(f"API Docs config: {app.openapi()}")
|
|
|
|
|
2020-01-28 08:59:27 -07:00
|
|
|
CORS_ORIGINS = params.cors_origins.copy()
|
|
|
|
if params.developer_mode:
|
2020-01-21 17:27:57 -07:00
|
|
|
CORS_ORIGINS.append(URL_DEV)
|
|
|
|
|
|
|
|
# CORS Configuration
|
|
|
|
app.add_middleware(
|
|
|
|
CORSMiddleware,
|
|
|
|
allow_origins=CORS_ORIGINS,
|
|
|
|
allow_methods=["GET", "POST", "OPTIONS"],
|
|
|
|
allow_headers=["*"],
|
|
|
|
)
|
|
|
|
|
|
|
|
app.add_api_route(
|
|
|
|
path="/api/query/",
|
|
|
|
endpoint=query,
|
|
|
|
methods=["POST"],
|
2020-01-28 08:59:27 -07:00
|
|
|
summary=params.docs.endpoint_summary,
|
|
|
|
description=params.docs.endpoint_description,
|
2020-01-21 17:27:57 -07:00
|
|
|
response_model=QueryResponse,
|
2020-01-28 08:59:27 -07:00
|
|
|
tags=[params.docs.group_title],
|
2020-01-21 17:27:57 -07:00
|
|
|
response_class=UJSONResponse,
|
|
|
|
)
|
|
|
|
app.add_api_route(path="api/docs", endpoint=docs, include_in_schema=False)
|
|
|
|
app.mount("/images", StaticFiles(directory=IMAGES_DIR), name="images")
|
|
|
|
app.mount("/", StaticFiles(directory=UI_DIR, html=True), name="ui")
|
|
|
|
|
|
|
|
|
|
|
|
def start():
|
|
|
|
"""Start the web server with Uvicorn ASGI."""
|
|
|
|
import uvicorn
|
|
|
|
|
|
|
|
uvicorn.run(app, **ASGI_PARAMS)
|