diff --git a/hyperglass/exceptions.py b/hyperglass/exceptions.py index a5cd450..126ba15 100644 --- a/hyperglass/exceptions.py +++ b/hyperglass/exceptions.py @@ -1,29 +1,30 @@ """Custom exceptions for hyperglass.""" -# Standard Library Imports +# Third Party Imports import ujson as _json # Project Imports +from hyperglass.constants import STATUS_CODE_MAP from hyperglass.util import log class HyperglassError(Exception): """hyperglass base exception.""" - def __init__(self, message="", alert="warning", keywords=None): + def __init__(self, message="", level="warning", keywords=None): """Initialize the hyperglass base exception class. Keyword Arguments: message {str} -- Error message (default: {""}) - alert {str} -- Error severity (default: {"warning"}) + level {str} -- Error severity (default: {"warning"}) keywords {list} -- 'Important' keywords (default: {None}) """ self._message = message - self._alert = alert + self._level = level self._keywords = keywords or [] - if self._alert == "warning": + if self._level == "warning": log.error(repr(self)) - elif self._alert == "danger": + elif self._level == "danger": log.critical(repr(self)) else: log.info(repr(self)) @@ -42,7 +43,7 @@ class HyperglassError(Exception): Returns: {str} -- Error message with code """ - return f"[{self.alert.upper()}] {self._message}" + return f"[{self.level.upper()}] {self._message}" def dict(self): """Return the instance's attributes as a dictionary. @@ -52,7 +53,7 @@ class HyperglassError(Exception): """ return { "message": self._message, - "alert": self._alert, + "level": self._level, "keywords": self._keywords, } @@ -74,13 +75,13 @@ class HyperglassError(Exception): return self._message @property - def alert(self): - """Return the instance's `alert` attribute. + def level(self): + """Return the instance's `level` attribute. Returns: {str} -- Alert name """ - return self._alert + return self._level @property def keywords(self): @@ -91,38 +92,47 @@ class HyperglassError(Exception): """ return self._keywords + @property + def status_code(self): + """Return HTTP status code based on level level. + + Returns: + {int} -- HTTP Status Code + """ + return STATUS_CODE_MAP.get(self._level, 500) + class _UnformattedHyperglassError(HyperglassError): """Base exception class for freeform error messages.""" - _alert = "warning" + _level = "warning" - def __init__(self, unformatted_msg="", alert=None, **kwargs): + def __init__(self, unformatted_msg="", level=None, **kwargs): """Format error message with keyword arguments. Keyword Arguments: message {str} -- Error message (default: {""}) - alert {str} -- Error severity (default: {"warning"}) + level {str} -- Error severity (default: {"warning"}) keywords {list} -- 'Important' keywords (default: {None}) """ self._message = unformatted_msg.format(**kwargs) - self._alert = alert or self._alert + self._level = level or self._level self._keywords = list(kwargs.values()) super().__init__( - message=self._message, alert=self._alert, keywords=self._keywords + message=self._message, level=self._level, keywords=self._keywords ) class _PredefinedHyperglassError(HyperglassError): _message = "undefined" - _alert = "warning" + _level = "warning" - def __init__(self, alert=None, **kwargs): + def __init__(self, level=None, **kwargs): self._fmt_msg = self._message.format(**kwargs) - self._alert = alert or self._alert + self._level = level or self._level self._keywords = list(kwargs.values()) super().__init__( - message=self._fmt_msg, alert=self._alert, keywords=self._keywords + message=self._fmt_msg, level=self._level, keywords=self._keywords ) @@ -148,25 +158,25 @@ class ConfigMissing(_PredefinedHyperglassError): class ScrapeError(_UnformattedHyperglassError): """Raised when a scrape/netmiko error occurs.""" - _alert = "danger" + _level = "danger" class AuthError(_UnformattedHyperglassError): """Raised when authentication to a device fails.""" - _alert = "danger" + _level = "danger" class RestError(_UnformattedHyperglassError): """Raised upon a rest API client error.""" - _alert = "danger" + _level = "danger" class DeviceTimeout(_UnformattedHyperglassError): """Raised when the connection to a device times out.""" - _alert = "danger" + _level = "danger" class InputInvalid(_UnformattedHyperglassError): diff --git a/hyperglass/execution/construct.py b/hyperglass/execution/construct.py index db93b21..2068c7b 100644 --- a/hyperglass/execution/construct.py +++ b/hyperglass/execution/construct.py @@ -37,7 +37,7 @@ class Construct: if not _device_vrf: raise HyperglassError( message="Unable to match query VRF to any configured VRFs", - alert="danger", + level="danger", keywords=[self.query_vrf], ) return _device_vrf diff --git a/hyperglass/execution/validate.py b/hyperglass/execution/validate.py index 994f82b..be59b95 100644 --- a/hyperglass/execution/validate.py +++ b/hyperglass/execution/validate.py @@ -169,7 +169,7 @@ def ip_access_list(query_data, device): if not vrf_acl: raise HyperglassError( message="Unable to match query VRF to any configured VRFs", - alert="danger", + level="danger", keywords=[query_data.query_vrf], ) diff --git a/hyperglass/models/response.py b/hyperglass/models/response.py index 71c2151..8d30609 100644 --- a/hyperglass/models/response.py +++ b/hyperglass/models/response.py @@ -8,9 +8,17 @@ from pydantic import StrictStr from pydantic import constr +class QueryError(BaseModel): + """Query response model.""" + + output: StrictStr + level: constr(regex=r"(success|warning|error|danger)") + keywords: List[StrictStr] + + class QueryResponse(BaseModel): """Query response model.""" output: StrictStr - alert: constr(regex=r"(warning|error|danger)") - keywords: List[StrictStr] + level: constr(regex=r"(success|warning|error|danger)") + keywords: List[StrictStr] = [] diff --git a/ui/components/Result.js b/ui/components/Result.js index 13cef34..e283d7e 100644 --- a/ui/components/Result.js +++ b/ui/components/Result.js @@ -51,7 +51,7 @@ const Result = React.forwardRef( const selectionBg = { dark: theme.colors.white, light: theme.colors.black }; const selectionColor = { dark: theme.colors.black, light: theme.colors.white }; const [{ data, loading, error }, refetch] = useAxios({ - url: "/api/query", + url: "/api/query/", method: "post", data: { query_location: queryLocation, @@ -73,6 +73,7 @@ const Result = React.forwardRef( (error && error.response?.data?.output) || (error && error.message) || config.messages.general; + error && console.log("ERROR", errorMsg); return ( diff --git a/ui/components/ResultHeader.js b/ui/components/ResultHeader.js index 1aebd80..d287dbe 100644 --- a/ui/components/ResultHeader.js +++ b/ui/components/ResultHeader.js @@ -36,7 +36,7 @@ export default React.forwardRef(({ title, loading, error }, ref) => { name="warning" color={ error.response - ? theme.colors[error.response?.data?.alert][warningColor[colorMode]] + ? theme.colors[error.response?.data?.level][warningColor[colorMode]] : defaultWarningColor[colorMode] } mr={4}