forked from mirrors/thatmattlove-hyperglass
improve api docs
This commit is contained in:
parent
52c2489658
commit
94c35faac0
3 changed files with 68 additions and 27 deletions
|
|
@ -5,9 +5,11 @@ import tempfile
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
# Third Party Imports
|
# Third Party Imports
|
||||||
from fastapi import BackgroundTasks
|
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
|
from fastapi.openapi.docs import get_redoc_html
|
||||||
|
from fastapi.openapi.docs import get_swagger_ui_html
|
||||||
|
from fastapi.openapi.utils import get_openapi
|
||||||
from prometheus_client import CONTENT_TYPE_LATEST
|
from prometheus_client import CONTENT_TYPE_LATEST
|
||||||
from prometheus_client import CollectorRegistry
|
from prometheus_client import CollectorRegistry
|
||||||
from prometheus_client import Counter
|
from prometheus_client import Counter
|
||||||
|
|
@ -33,6 +35,7 @@ from hyperglass.exceptions import ResponseEmpty
|
||||||
from hyperglass.exceptions import RestError
|
from hyperglass.exceptions import RestError
|
||||||
from hyperglass.exceptions import ScrapeError
|
from hyperglass.exceptions import ScrapeError
|
||||||
from hyperglass.models.query import Query
|
from hyperglass.models.query import Query
|
||||||
|
from hyperglass.models.response import QueryResponse
|
||||||
from hyperglass.query import REDIS_CONFIG
|
from hyperglass.query import REDIS_CONFIG
|
||||||
from hyperglass.query import handle_query
|
from hyperglass.query import handle_query
|
||||||
from hyperglass.util import check_python
|
from hyperglass.util import check_python
|
||||||
|
|
@ -50,32 +53,24 @@ STATIC_FILES = "\n".join([str(STATIC_DIR), str(UI_DIR), str(IMAGES_DIR), str(NEX
|
||||||
|
|
||||||
log.debug(f"Static Files: {STATIC_FILES}")
|
log.debug(f"Static Files: {STATIC_FILES}")
|
||||||
|
|
||||||
docs_mode_map = {"swagger": "docs_url", "redoc": "redoc_url"}
|
|
||||||
|
|
||||||
docs_config = {"docs_url": None, "redoc_url": None}
|
|
||||||
|
|
||||||
if params.general.docs.enable:
|
|
||||||
if params.general.docs.mode == "swagger":
|
|
||||||
docs_config["docs_url"] = params.general.docs.uri
|
|
||||||
docs_config["redoc_url"] = None
|
|
||||||
elif params.general.docs.mode == "redoc":
|
|
||||||
docs_config["docs_url"] = None
|
|
||||||
docs_config["redoc_url"] = params.general.docs.uri
|
|
||||||
|
|
||||||
|
|
||||||
# Main App Definition
|
# Main App Definition
|
||||||
app = FastAPI(
|
app = FastAPI(
|
||||||
debug=params.general.debug,
|
debug=params.general.debug,
|
||||||
title=params.general.site_title,
|
title=params.general.site_title,
|
||||||
description=params.general.site_description,
|
description=params.general.site_description,
|
||||||
version=__version__,
|
version=__version__,
|
||||||
**docs_config,
|
default_response_class=UJSONResponse,
|
||||||
|
docs_url=None,
|
||||||
|
redoc_url=None,
|
||||||
)
|
)
|
||||||
app.mount("/ui", StaticFiles(directory=UI_DIR), name="ui")
|
app.mount("/ui", StaticFiles(directory=UI_DIR), name="ui")
|
||||||
app.mount("/_next", StaticFiles(directory=NEXT_DIR), name="_next")
|
app.mount("/_next", StaticFiles(directory=NEXT_DIR), name="_next")
|
||||||
app.mount("/images", StaticFiles(directory=IMAGES_DIR), name="images")
|
app.mount("/images", StaticFiles(directory=IMAGES_DIR), name="images")
|
||||||
app.mount("/ui/images", StaticFiles(directory=IMAGES_DIR), name="ui/images")
|
app.mount("/ui/images", StaticFiles(directory=IMAGES_DIR), name="ui/images")
|
||||||
|
|
||||||
|
if params.general.docs.enable:
|
||||||
|
log.debug(f"API Docs config: {app.openapi()}")
|
||||||
|
|
||||||
DEV_URL = f"http://localhost:{str(params.general.listen_port)}/api/"
|
DEV_URL = f"http://localhost:{str(params.general.listen_port)}/api/"
|
||||||
PROD_URL = "/api/"
|
PROD_URL = "/api/"
|
||||||
|
|
||||||
|
|
@ -91,6 +86,21 @@ app.add_middleware(
|
||||||
allow_headers=["*"],
|
allow_headers=["*"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def custom_openapi():
|
||||||
|
"""Generate custom OpenAPI config."""
|
||||||
|
openapi_schema = get_openapi(
|
||||||
|
title=params.general.site_title,
|
||||||
|
version=__version__,
|
||||||
|
description=params.general.site_description,
|
||||||
|
routes=app.routes,
|
||||||
|
)
|
||||||
|
app.openapi_schema = openapi_schema
|
||||||
|
return app.openapi_schema
|
||||||
|
|
||||||
|
|
||||||
|
app.openapi = custom_openapi
|
||||||
|
|
||||||
ASGI_PARAMS = {
|
ASGI_PARAMS = {
|
||||||
"host": str(params.general.listen_address),
|
"host": str(params.general.listen_address),
|
||||||
"port": params.general.listen_port,
|
"port": params.general.listen_port,
|
||||||
|
|
@ -199,7 +209,7 @@ tempdir = tempfile.TemporaryDirectory(prefix="hyperglass_")
|
||||||
os.environ["prometheus_multiproc_dir"] = tempdir.name
|
os.environ["prometheus_multiproc_dir"] = tempdir.name
|
||||||
|
|
||||||
|
|
||||||
@app.get("/metrics")
|
@app.get("/metrics", include_in_schema=False)
|
||||||
async def metrics(request):
|
async def metrics(request):
|
||||||
"""Serve Prometheus metrics."""
|
"""Serve Prometheus metrics."""
|
||||||
registry = CollectorRegistry()
|
registry = CollectorRegistry()
|
||||||
|
|
@ -214,7 +224,7 @@ async def metrics(request):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/api/config")
|
@app.get("/api/config", include_in_schema=False)
|
||||||
async def frontend_config():
|
async def frontend_config():
|
||||||
"""Provide validated user/default config for front end consumption.
|
"""Provide validated user/default config for front end consumption.
|
||||||
|
|
||||||
|
|
@ -224,16 +234,15 @@ async def frontend_config():
|
||||||
return UJSONResponse(frontend_params, status_code=200)
|
return UJSONResponse(frontend_params, status_code=200)
|
||||||
|
|
||||||
|
|
||||||
@app.post("/api/query/")
|
@app.post(
|
||||||
async def hyperglass_main(
|
"/api/query/",
|
||||||
query_data: Query, request: Request, background_tasks: BackgroundTasks
|
summary=params.general.docs.endpoint_summary,
|
||||||
):
|
description=params.general.docs.endpoint_description,
|
||||||
"""Process XHR POST data.
|
response_model=QueryResponse,
|
||||||
|
tags=[params.general.docs.group_title],
|
||||||
Ingests XHR POST data from
|
)
|
||||||
form submit, passes it to the backend application to perform the
|
async def query(query_data: Query, request: Request):
|
||||||
filtering/lookups.
|
"""Ingest request data pass it to the backend application to perform the query."""
|
||||||
"""
|
|
||||||
|
|
||||||
# Get client IP address for Prometheus logging & rate limiting
|
# Get client IP address for Prometheus logging & rate limiting
|
||||||
client_addr = request.client.host
|
client_addr = request.client.host
|
||||||
|
|
@ -259,6 +268,17 @@ async def hyperglass_main(
|
||||||
return UJSONResponse({"output": response}, status_code=200)
|
return UJSONResponse({"output": response}, status_code=200)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/api/docs", include_in_schema=False)
|
||||||
|
async def docs():
|
||||||
|
"""Serve custom docs."""
|
||||||
|
if params.general.docs.enable:
|
||||||
|
docs_func_map = {"swagger": get_swagger_ui_html, "redoc": get_redoc_html}
|
||||||
|
docs_func = docs_func_map[params.general.docs.mode]
|
||||||
|
return docs_func(openapi_url=app.openapi_url, title=app.title + " - API Docs")
|
||||||
|
else:
|
||||||
|
raise HTTPException(detail="Not found", status_code=404)
|
||||||
|
|
||||||
|
|
||||||
def start():
|
def start():
|
||||||
"""Start the web server with Uvicorn ASGI."""
|
"""Start the web server with Uvicorn ASGI."""
|
||||||
import uvicorn
|
import uvicorn
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
|
"""Configuration for API docs feature."""
|
||||||
# Third Party Imports
|
# Third Party Imports
|
||||||
from pydantic import StrictBool
|
from pydantic import StrictBool
|
||||||
|
from pydantic import StrictStr
|
||||||
from pydantic import constr
|
from pydantic import constr
|
||||||
|
|
||||||
# Project Imports
|
# Project Imports
|
||||||
|
|
@ -13,3 +15,6 @@ class Docs(HyperglassModel):
|
||||||
enable: StrictBool = True
|
enable: StrictBool = True
|
||||||
mode: constr(regex=r"(swagger|redoc)") = "swagger"
|
mode: constr(regex=r"(swagger|redoc)") = "swagger"
|
||||||
uri: AnyUri = "/docs"
|
uri: AnyUri = "/docs"
|
||||||
|
endpoint_summary: StrictStr = "Query Endpoint"
|
||||||
|
endpoint_description: StrictStr = "Request a query response per-location."
|
||||||
|
group_title: StrictStr = "Queries"
|
||||||
|
|
|
||||||
16
hyperglass/models/response.py
Normal file
16
hyperglass/models/response.py
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
"""Response model."""
|
||||||
|
# Standard Library Imports
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
# Third Party Imports
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from pydantic import StrictStr
|
||||||
|
from pydantic import constr
|
||||||
|
|
||||||
|
|
||||||
|
class QueryResponse(BaseModel):
|
||||||
|
"""Query response model."""
|
||||||
|
|
||||||
|
output: StrictStr
|
||||||
|
alert: constr(regex=r"(warning|error|danger)")
|
||||||
|
keywords: List[StrictStr]
|
||||||
Loading…
Add table
Reference in a new issue