From 7ff8fa317de6945668306cffea0be4bb486a8071 Mon Sep 17 00:00:00 2001 From: checktheroads Date: Mon, 3 Feb 2020 02:34:50 -0700 Subject: [PATCH] add API docs examples --- hyperglass/api/__init__.py | 134 +++++++++++++++++++------- hyperglass/api/error_handlers.py | 6 +- hyperglass/api/events.py | 28 +++--- hyperglass/api/examples/devices.py | 6 ++ hyperglass/api/examples/devices.sh | 1 + hyperglass/api/examples/queries.py | 6 ++ hyperglass/api/examples/queries.sh | 1 + hyperglass/api/examples/query.py | 13 +++ hyperglass/api/examples/query.sh | 7 ++ hyperglass/api/models/__init__.py | 8 +- hyperglass/api/models/query.py | 63 +++++++++---- hyperglass/api/models/response.py | 140 +++++++++++----------------- hyperglass/api/models/rfc8522.py | 15 +-- hyperglass/api/models/types.py | 2 +- hyperglass/api/models/validators.py | 11 +-- hyperglass/api/routes.py | 19 ++-- 16 files changed, 273 insertions(+), 187 deletions(-) create mode 100644 hyperglass/api/examples/devices.py create mode 100644 hyperglass/api/examples/devices.sh create mode 100644 hyperglass/api/examples/queries.py create mode 100644 hyperglass/api/examples/queries.sh create mode 100644 hyperglass/api/examples/query.py create mode 100644 hyperglass/api/examples/query.sh diff --git a/hyperglass/api/__init__.py b/hyperglass/api/__init__.py index 57d823c..bf7856b 100644 --- a/hyperglass/api/__init__.py +++ b/hyperglass/api/__init__.py @@ -1,46 +1,63 @@ """hyperglass REST API & Web UI.""" -# Standard Library Imports +# Standard Library +from typing import List from pathlib import Path -# Third Party Imports +# Third Party from fastapi import FastAPI from fastapi.exceptions import RequestValidationError -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.exceptions import HTTPException as StarletteHTTPException +from fastapi.openapi.utils import get_openapi from starlette.staticfiles import StaticFiles +from starlette.middleware.cors import CORSMiddleware -# Project Imports -from hyperglass.api.error_handlers import app_handler -from hyperglass.api.error_handlers import default_handler -from hyperglass.api.error_handlers import http_handler -from hyperglass.api.error_handlers import validation_handler -from hyperglass.api.events import on_shutdown -from hyperglass.api.events import on_startup -from hyperglass.api.models.response import QueryResponse -from hyperglass.api.models.response import RoutersResponse -from hyperglass.api.models.response import SupportedQueryResponse -from hyperglass.api.routes import docs -from hyperglass.api.routes import queries -from hyperglass.api.routes import query -from hyperglass.api.routes import routers -from hyperglass.configuration import URL_DEV -from hyperglass.configuration import params -from hyperglass.constants import __version__ -from hyperglass.exceptions import HyperglassError +# Project from hyperglass.util import log +from hyperglass.constants import __version__ +from hyperglass.api.events import on_startup, on_shutdown +from hyperglass.api.routes import docs, query, queries, routers +from hyperglass.exceptions import HyperglassError +from hyperglass.configuration import URL_DEV, params +from hyperglass.api.error_handlers import ( + app_handler, + http_handler, + default_handler, + validation_handler, +) +from hyperglass.api.models.response import ( + QueryError, + QueryResponse, + RoutersResponse, + SupportedQueryResponse, +) -STATIC_DIR = Path(__file__).parent.parent / "static" +WORKING_DIR = Path(__file__).parent +STATIC_DIR = WORKING_DIR.parent / "static" UI_DIR = STATIC_DIR / "ui" IMAGES_DIR = STATIC_DIR / "images" +EXAMPLES_DIR = WORKING_DIR / "examples" + +EXAMPLE_DEVICES_PY = EXAMPLES_DIR / "devices.py" +EXAMPLE_QUERIES_PY = EXAMPLES_DIR / "queries.py" +EXAMPLE_QUERY_PY = EXAMPLES_DIR / "query.py" +EXAMPLE_DEVICES_CURL = EXAMPLES_DIR / "devices.sh" +EXAMPLE_QUERIES_CURL = EXAMPLES_DIR / "queries.sh" +EXAMPLE_QUERY_CURL = EXAMPLES_DIR / "query.sh" ASGI_PARAMS = { "host": str(params.listen_address), "port": params.listen_port, "debug": params.debug, } +DOCS_PARAMS = {} +if params.docs.enable: + DOCS_PARAMS.update({"openapi_url": params.docs.openapi_uri}) + if params.docs.mode == "redoc": + DOCS_PARAMS.update({"docs_url": None, "redoc_url": params.docs.uri}) + elif params.docs.mode == "swagger": + DOCS_PARAMS.update({"docs_url": params.docs.uri, "redoc_url": None}) # Main App Definition app = FastAPI( @@ -49,9 +66,7 @@ app = FastAPI( description=params.site_description, version=__version__, default_response_class=UJSONResponse, - docs_url=None, - redoc_url=None, - openapi_url=params.docs.openapi_uri, + **DOCS_PARAMS, ) # Add Event Handlers @@ -77,20 +92,60 @@ app.add_exception_handler(Exception, default_handler) def _custom_openapi(): """Generate custom OpenAPI config.""" openapi_schema = get_openapi( - title=params.site_title, + title=params.docs.title.format(site_title=params.site_title), version=__version__, - description=params.site_description, + description=params.docs.description, routes=app.routes, ) + openapi_schema["info"]["x-logo"] = {"url": str(params.web.logo.light)} + + query_samples = [] + queries_samples = [] + devices_samples = [] + + with EXAMPLE_QUERY_CURL.open("r") as e: + example = e.read() + query_samples.append( + {"lang": "cURL", "source": example % str(params.docs.base_url)} + ) + + with EXAMPLE_QUERY_PY.open("r") as e: + example = e.read() + query_samples.append( + {"lang": "Python", "source": example % str(params.docs.base_url)} + ) + + with EXAMPLE_DEVICES_CURL.open("r") as e: + example = e.read() + queries_samples.append( + {"lang": "cURL", "source": example % str(params.docs.base_url)} + ) + with EXAMPLE_DEVICES_PY.open("r") as e: + example = e.read() + queries_samples.append( + {"lang": "Python", "source": example % str(params.docs.base_url)} + ) + + with EXAMPLE_QUERIES_CURL.open("r") as e: + example = e.read() + devices_samples.append( + {"lang": "cURL", "source": example % str(params.docs.base_url)} + ) + + with EXAMPLE_QUERIES_PY.open("r") as e: + example = e.read() + devices_samples.append( + {"lang": "Python", "source": example % str(params.docs.base_url)} + ) + + openapi_schema["paths"]["/api/query/"]["post"]["x-code-samples"] = query_samples + openapi_schema["paths"]["/api/devices"]["get"]["x-code-samples"] = devices_samples + openapi_schema["paths"]["/api/queries"]["get"]["x-code-samples"] = queries_samples + app.openapi_schema = openapi_schema return app.openapi_schema -app.openapi = _custom_openapi - -if params.docs.enable: - log.debug(f"API Docs config: {app.openapi()}") - CORS_ORIGINS = params.cors_origins.copy() if params.developer_mode: CORS_ORIGINS.append(URL_DEV) @@ -107,7 +162,7 @@ app.add_api_route( path="/api/devices", endpoint=routers, methods=["GET"], - response_model=RoutersResponse, + response_model=List[RoutersResponse], response_class=UJSONResponse, summary=params.docs.devices.summary, description=params.docs.devices.description, @@ -118,7 +173,7 @@ app.add_api_route( endpoint=queries, methods=["GET"], response_class=UJSONResponse, - response_model=SupportedQueryResponse, + response_model=List[SupportedQueryResponse], summary=params.docs.queries.summary, description=params.docs.queries.description, tags=[params.docs.queries.title], @@ -129,6 +184,11 @@ app.add_api_route( methods=["POST"], summary=params.docs.query.summary, description=params.docs.query.description, + responses={ + 400: {"model": QueryError, "description": "Request Content Error"}, + 422: {"model": QueryError, "description": "Request Format Error"}, + 500: {"model": QueryError, "description": "Server Error"}, + }, response_model=QueryResponse, tags=[params.docs.query.title], response_class=UJSONResponse, @@ -136,6 +196,8 @@ app.add_api_route( if params.docs.enable: app.add_api_route(path=params.docs.uri, endpoint=docs, include_in_schema=False) + app.openapi = _custom_openapi + log.debug(f"API Docs config: {app.openapi()}") app.mount("/images", StaticFiles(directory=IMAGES_DIR), name="images") app.mount("/", StaticFiles(directory=UI_DIR, html=True), name="ui") diff --git a/hyperglass/api/error_handlers.py b/hyperglass/api/error_handlers.py index 00bdfc5..5e3e168 100644 --- a/hyperglass/api/error_handlers.py +++ b/hyperglass/api/error_handlers.py @@ -1,9 +1,9 @@ """API Error Handlers.""" -# Third Party Imports +# Third Party from starlette.responses import UJSONResponse -# Project Imports +# Project from hyperglass.configuration import params @@ -36,5 +36,5 @@ async def validation_handler(request, exc): error = exc.errors()[0] return UJSONResponse( {"output": error["msg"], "level": "error", "keywords": error["loc"]}, - status_code=400, + status_code=422, ) diff --git a/hyperglass/api/events.py b/hyperglass/api/events.py index 32cf24b..5a7dac7 100644 --- a/hyperglass/api/events.py +++ b/hyperglass/api/events.py @@ -1,19 +1,23 @@ """API Events.""" -# Third Party Imports +# Third Party from starlette.exceptions import HTTPException -# Project Imports -from hyperglass.configuration import REDIS_CONFIG -from hyperglass.configuration import URL_DEV -from hyperglass.configuration import URL_PROD -from hyperglass.configuration import frontend_params -from hyperglass.configuration import params +# Project +from hyperglass.util import ( + log, + check_redis, + check_python, + build_frontend, + clear_redis_cache, +) from hyperglass.exceptions import HyperglassError -from hyperglass.util import build_frontend -from hyperglass.util import check_python -from hyperglass.util import check_redis -from hyperglass.util import clear_redis_cache -from hyperglass.util import log +from hyperglass.configuration import ( + URL_DEV, + URL_PROD, + REDIS_CONFIG, + params, + frontend_params, +) async def check_python_version(): diff --git a/hyperglass/api/examples/devices.py b/hyperglass/api/examples/devices.py new file mode 100644 index 0000000..e6ec88d --- /dev/null +++ b/hyperglass/api/examples/devices.py @@ -0,0 +1,6 @@ +# Third Party +import httpx + +request = httpx.get("%s/api/devices") + +print(request.json()) diff --git a/hyperglass/api/examples/devices.sh b/hyperglass/api/examples/devices.sh new file mode 100644 index 0000000..cf94cb2 --- /dev/null +++ b/hyperglass/api/examples/devices.sh @@ -0,0 +1 @@ +curl %s/api/devices \ No newline at end of file diff --git a/hyperglass/api/examples/queries.py b/hyperglass/api/examples/queries.py new file mode 100644 index 0000000..b44b0fa --- /dev/null +++ b/hyperglass/api/examples/queries.py @@ -0,0 +1,6 @@ +# Third Party +import httpx + +request = httpx.get("%s/api/queries") + +print(request.json()) diff --git a/hyperglass/api/examples/queries.sh b/hyperglass/api/examples/queries.sh new file mode 100644 index 0000000..b89af31 --- /dev/null +++ b/hyperglass/api/examples/queries.sh @@ -0,0 +1 @@ +curl %s/api/queries \ No newline at end of file diff --git a/hyperglass/api/examples/query.py b/hyperglass/api/examples/query.py new file mode 100644 index 0000000..3b75ddf --- /dev/null +++ b/hyperglass/api/examples/query.py @@ -0,0 +1,13 @@ +# Third Party +import httpx + +query = { + "query_location": "router01", + "query_type": "bgp_route", + "query_vrf": "default", + "query_target": "1.1.1.0/24", +} + +request = httpx.post("%s/api/query/", data=query) + +print(request.json().get("output")) diff --git a/hyperglass/api/examples/query.sh b/hyperglass/api/examples/query.sh new file mode 100644 index 0000000..ec41f6c --- /dev/null +++ b/hyperglass/api/examples/query.sh @@ -0,0 +1,7 @@ +curl -X POST %s/api/query/ -d \ + '{ + "query_location": "router01", + "query_type": "bgp_route", + "query_vrf": "default", + "query_target": "1.1.1.0/24" + }' \ No newline at end of file diff --git a/hyperglass/api/models/__init__.py b/hyperglass/api/models/__init__.py index b3e8ce4..e8b2f39 100644 --- a/hyperglass/api/models/__init__.py +++ b/hyperglass/api/models/__init__.py @@ -1,10 +1,6 @@ """Query & Response Validation Models.""" -# Project Imports -from hyperglass.api.models import query -from hyperglass.api.models import response -from hyperglass.api.models import rfc8522 -from hyperglass.api.models import types -from hyperglass.api.models import validators +# Project +from hyperglass.api.models import query, types, rfc8522, response, validators # flake8: noqa: F401 diff --git a/hyperglass/api/models/query.py b/hyperglass/api/models/query.py index 5041672..271c2b4 100644 --- a/hyperglass/api/models/query.py +++ b/hyperglass/api/models/query.py @@ -1,22 +1,21 @@ """Input query validation model.""" -# Standard Library Imports +# Standard Library import hashlib -# Third Party Imports -from pydantic import BaseModel -from pydantic import StrictStr -from pydantic import validator +# Third Party +from pydantic import BaseModel, StrictStr, validator -# Project Imports -from hyperglass.api.models.types import SupportedQuery -from hyperglass.api.models.validators import validate_aspath -from hyperglass.api.models.validators import validate_community -from hyperglass.api.models.validators import validate_ip -from hyperglass.configuration import devices -from hyperglass.configuration import params -from hyperglass.configuration.models.vrfs import Vrf +# Project +from hyperglass.util import log from hyperglass.exceptions import InputInvalid +from hyperglass.configuration import params, devices +from hyperglass.api.models.types import SupportedQuery +from hyperglass.api.models.validators import ( + validate_ip, + validate_aspath, + validate_community, +) def get_vrf_object(vrf_name): @@ -51,14 +50,43 @@ class Query(BaseModel): query_location: StrictStr query_type: SupportedQuery - query_vrf: Vrf + query_vrf: StrictStr query_target: StrictStr + class Config: + """Pydantic model configuration.""" + + fields = { + "query_location": { + "title": params.web.text.query_location, + "description": "Router/Location Name", + "example": "router01", + }, + "query_type": { + "title": params.web.text.query_type, + "description": "Type of Query to Execute", + "example": "bgp_route", + }, + "query_vrf": { + "title": params.web.text.query_vrf, + "description": "Routing Table/VRF", + "example": "default", + }, + "query_target": { + "title": params.web.text.query_target, + "description": "IP Address, Community, or AS Path", + "example": "1.1.1.0/24", + }, + } + schema_extra = { + "x-code-samples": [{"lang": "Python", "source": "print('stuff')"}] + } + def digest(self): """Create SHA256 hash digest of model representation.""" return hashlib.sha256(repr(self).encode()).hexdigest() - @validator("query_location", pre=True, always=True) + @validator("query_location") def validate_query_location(cls, value): """Ensure query_location is defined. @@ -80,7 +108,7 @@ class Query(BaseModel): ) return value - @validator("query_vrf", always=True, pre=True) + @validator("query_vrf") def validate_query_vrf(cls, value, values): """Ensure query_vrf is defined. @@ -108,10 +136,11 @@ class Query(BaseModel): ) return device_vrf - @validator("query_target", always=True) + @validator("query_target") def validate_query_target(cls, value, values): """Validate query target value based on query_type.""" + log.debug(values) query_type = values["query_type"] # Use relevant function based on query_type. diff --git a/hyperglass/api/models/response.py b/hyperglass/api/models/response.py index 1f69314..f06565f 100644 --- a/hyperglass/api/models/response.py +++ b/hyperglass/api/models/response.py @@ -1,20 +1,20 @@ """Response model.""" -# Standard Library Imports +# Standard Library from typing import List -# Third Party Imports -from pydantic import BaseModel -from pydantic import StrictBool -from pydantic import StrictStr -from pydantic import constr +# Third Party +from pydantic import BaseModel, StrictStr, StrictBool, constr + +# Project +from hyperglass.configuration import params class QueryError(BaseModel): """Query response model.""" - output: StrictStr - level: constr(regex=r"(success|warning|error|danger)") - keywords: List[StrictStr] + output: StrictStr = params.messages.general + level: constr(regex=r"(success|warning|error|danger)") = "danger" + keywords: List[StrictStr] = [] class Config: """Pydantic model configuration.""" @@ -23,6 +23,23 @@ class QueryError(BaseModel): description = ( "Response received when there is an error executing the requested query." ) + fields = { + "output": { + "title": "Output", + "description": "Error Details", + "example": "192.0.2.1/32 is not allowed.", + }, + "level": { + "title": "Level", + "description": "Error Severity", + "example": "danger", + }, + "keywords": { + "title": "Keywords", + "description": "Relevant keyword values contained in the `output` field, which can be used for formatting.", + "example": ["192.0.2.1/32"], + }, + } schema_extra = { "examples": [ { @@ -38,7 +55,7 @@ class QueryResponse(BaseModel): """Query response model.""" output: StrictStr - level: constr(regex=r"(success|warning|error|danger)") + level: constr(regex=r"success") = "success" keywords: List[StrictStr] = [] class Config: @@ -46,66 +63,45 @@ class QueryResponse(BaseModel): title = "Query Response" description = "Looking glass response" + fields = { + "level": {"title": "Level", "description": "Severity"}, + "keywords": { + "title": "Keywords", + "description": "Relevant keyword values contained in the `output` field, which can be used for formatting.", + "example": ["1.1.1.0/24", "best #1"], + }, + "output": { + "title": "Output", + "description": "Looking Glass Response", + "example": """ +BGP routing table entry for 1.1.1.0/24, version 224184946 +BGP Bestpath: deterministic-med +Paths: (12 available, best #1, table default) + Advertised to update-groups: + 1 40 + 13335, (aggregated by 13335 172.68.129.1), (received & used) + 192.0.2.1 (metric 51) from 192.0.2.1 (192.0.2.1) + Origin IGP, metric 0, localpref 250, valid, internal + Community: 65000:1 65000:2 + """, + }, + } schema_extra = { "examples": [ { "output": """ BGP routing table entry for 1.1.1.0/24, version 224184946 BGP Bestpath: deterministic-med -Paths: (12 available, best #9, table default) +Paths: (12 available, best #1, table default) Advertised to update-groups: 1 40 13335, (aggregated by 13335 172.68.129.1), (received & used) - 199.34.92.5 (metric 51) from 199.34.92.5 (199.34.92.5) + 192.0.2.1 (metric 51) from 192.0.2.1 (192.0.2.1) Origin IGP, metric 0, localpref 250, valid, internal - Community: 14525:1021 14525:2840 14525:3003 14525:4003 14525:5200 14525:5300 14525:5306 - 13335, (aggregated by 13335 172.68.129.1), (received & used) - 199.34.92.6 (metric 51) from 199.34.92.6 (199.34.92.6) - Origin IGP, metric 0, localpref 250, valid, internal - Community: 14525:1021 14525:2840 14525:3003 14525:4003 14525:5200 14525:5300 14525:5306 - 1299 13335, (aggregated by 13335 162.158.140.1) - 62.115.171.124 from 62.115.171.124 (2.255.254.51) - Origin IGP, metric 0, localpref 100, weight 200, valid, external - Community: 14525:0 14525:1021 14525:2840 14525:3001 14525:4001 14525:5100 14525:5103 - 1299 13335, (aggregated by 13335 162.158.140.1), (received-only) - 62.115.171.124 from 62.115.171.124 (2.255.254.51) - Origin IGP, localpref 100, valid, external - Community: 1299:35000 - 174 13335, (aggregated by 13335 108.162.239.1) - 199.34.92.7 (metric 1100) from 199.34.92.7 (199.34.92.7) - Origin IGP, metric 0, localpref 100, weight 125, valid, internal - Community: 14525:0 14525:840 14525:1021 14525:3004 14525:4004 14525:5100 14525:5101 - 174 13335, (aggregated by 13335 108.162.239.1), (received-only) - 199.34.92.7 (metric 1100) from 199.34.92.7 (199.34.92.7) - Origin IGP, metric 0, localpref 100, valid, internal - Community: 14525:0 14525:840 14525:1021 14525:3004 14525:4004 14525:5100 14525:5101 - 174 13335, (aggregated by 13335 162.158.140.1), (Received from a RR-client) - 199.34.92.2 (metric 26) from 199.34.92.2 (199.34.92.2) - Origin IGP, metric 0, localpref 100, weight 200, valid, internal - Community: 14525:0 14525:1021 14525:2840 14525:3001 14525:4001 14525:5100 14525:5101 - 174 13335, (aggregated by 13335 162.158.140.1), (Received from a RR-client), (received-only) - 199.34.92.2 (metric 26) from 199.34.92.2 (199.34.92.2) - Origin IGP, metric 0, localpref 100, valid, internal - Community: 14525:0 14525:1021 14525:2840 14525:3001 14525:4001 14525:5100 14525:5101 - 174 13335, (aggregated by 13335 162.158.140.1) - 38.140.141.25 from 38.140.141.25 (154.26.6.194) - Origin IGP, metric 0, localpref 100, weight 200, valid, external, best - Community: 14525:0 14525:1021 14525:2840 14525:3001 14525:4001 14525:5100 14525:5101 - 174 13335, (aggregated by 13335 162.158.140.1), (received-only) - 38.140.141.25 from 38.140.141.25 (154.26.6.194) - Origin IGP, metric 2020, localpref 100, valid, external - Community: 174:21001 174:22013 - 3257 13335, (aggregated by 13335 141.101.72.1) - 199.34.92.3 (metric 200) from 199.34.92.3 (199.34.92.3) - Origin IGP, metric 0, localpref 100, weight 200, valid, internal - Community: 14525:0 14525:840 14525:1021 14525:3002 14525:4002 14525:5100 14525:5104 - 3257 13335, (aggregated by 13335 141.101.72.1), (received-only) - 199.34.92.3 (metric 200) from 199.34.92.3 (199.34.92.3) - Origin IGP, metric 0, localpref 100, valid, internal - Community: 14525:0 14525:840 14525:1021 14525:3002 14525:4002 14525:5100 14525:5104 + Community: 65000:1 65000:2 """, "level": "success", - "keywords": ["1.1.1.0/24", "best #9"], + "keywords": ["1.1.1.0/24", "best #1"], } ] } @@ -144,7 +140,7 @@ class Network(BaseModel): schema_extra = {"examples": [{"name": "primary", "display_name": "AS65000"}]} -class Router(BaseModel): +class RoutersResponse(BaseModel): """Response model for /api/devices list items.""" name: StrictStr @@ -169,19 +165,7 @@ class Router(BaseModel): } -class RoutersResponse(BaseModel): - """Response model for /api/devices endpoint.""" - - __root__: List[Router] - - class Config: - """Pydantic model configuration.""" - - title = "Devices" - description = "List of all devices" - - -class SupportedQuery(BaseModel): +class SupportedQueryResponse(BaseModel): """Response model for /api/queries list items.""" name: StrictStr @@ -198,15 +182,3 @@ class SupportedQuery(BaseModel): {"name": "bgp_route", "display_name": "BGP Route", "enable": True} ] } - - -class SupportedQueryResponse(BaseModel): - """Response model for /api/queries endpoint.""" - - __root__: List[SupportedQuery] - - class Config: - """Pydantic model configuration.""" - - title = "Supported Query Types" - description = "Enabled query type attributes." diff --git a/hyperglass/api/models/rfc8522.py b/hyperglass/api/models/rfc8522.py index 1ba18dc..ae4d55c 100644 --- a/hyperglass/api/models/rfc8522.py +++ b/hyperglass/api/models/rfc8522.py @@ -1,22 +1,15 @@ """Response model.""" -# Standard Library Imports +# Standard Library # flake8: noqa import math import secrets +from typing import List, Union, Optional from datetime import datetime -from typing import List -from typing import Optional -from typing import Union -# Third Party Imports +# Third Party import ujson -from pydantic import BaseModel -from pydantic import StrictFloat -from pydantic import StrictInt -from pydantic import StrictStr -from pydantic import constr -from pydantic import validator +from pydantic import BaseModel, StrictInt, StrictStr, StrictFloat, constr, validator """Patterns: diff --git a/hyperglass/api/models/types.py b/hyperglass/api/models/types.py index e54d6d2..e815b1e 100644 --- a/hyperglass/api/models/types.py +++ b/hyperglass/api/models/types.py @@ -1,6 +1,6 @@ """Custom validation types.""" -# Project Imports +# Project from hyperglass.constants import SUPPORTED_QUERY_TYPES diff --git a/hyperglass/api/models/validators.py b/hyperglass/api/models/validators.py index af8c0d3..5ee0889 100644 --- a/hyperglass/api/models/validators.py +++ b/hyperglass/api/models/validators.py @@ -1,15 +1,14 @@ """Input validation functions for submitted queries.""" -# Standard Library Imports -import operator +# Standard Library import re +import operator from ipaddress import ip_network -# Project Imports -from hyperglass.configuration import params -from hyperglass.exceptions import InputInvalid -from hyperglass.exceptions import InputNotAllowed +# Project from hyperglass.util import log +from hyperglass.exceptions import InputInvalid, InputNotAllowed +from hyperglass.configuration import params def _member_of(target, network): diff --git a/hyperglass/api/routes.py b/hyperglass/api/routes.py index 607ff0e..1279ddf 100644 --- a/hyperglass/api/routes.py +++ b/hyperglass/api/routes.py @@ -1,23 +1,20 @@ """API Routes.""" -# Standard Library Imports +# Standard Library import time -# Third Party Imports +# Third Party import aredis from fastapi import HTTPException -from fastapi.openapi.docs import get_redoc_html -from fastapi.openapi.docs import get_swagger_ui_html from starlette.requests import Request +from fastapi.openapi.docs import get_redoc_html, get_swagger_ui_html -# Project Imports -from hyperglass.api.models.query import Query -from hyperglass.configuration import REDIS_CONFIG -from hyperglass.configuration import devices -from hyperglass.configuration import params -from hyperglass.exceptions import HyperglassError -from hyperglass.execution.execute import Execute +# Project from hyperglass.util import log +from hyperglass.exceptions import HyperglassError +from hyperglass.configuration import REDIS_CONFIG, params, devices +from hyperglass.api.models.query import Query +from hyperglass.execution.execute import Execute Cache = aredis.StrictRedis(db=params.cache.database, **REDIS_CONFIG)