diff --git a/hyperglass/configuration/models/web.py b/hyperglass/configuration/models/web.py index 0272690..269886d 100644 --- a/hyperglass/configuration/models/web.py +++ b/hyperglass/configuration/models/web.py @@ -139,7 +139,7 @@ class Text(HyperglassModel): query_vrf: StrictStr = "Routing Table" fqdn_tooltip: StrictStr = "Use {protocol}" # Formatted by Javascript cache_prefix: StrictStr = "Results cached for " - cache_icon: StrictStr = "Cached Response from {time}" # Formatted by Javascript + cache_icon: StrictStr = "Cached Response from {time} UTC" # Formatted by Javascript complete_time: StrictStr = "Completed in {seconds}" # Formatted by Javascript @validator("title_mode") diff --git a/hyperglass/constants.py b/hyperglass/constants.py index 74f56ad..e3e30ba 100644 --- a/hyperglass/constants.py +++ b/hyperglass/constants.py @@ -4,7 +4,7 @@ from datetime import datetime __name__ = "hyperglass" -__version__ = "1.0.0-beta.26" +__version__ = "1.0.0-beta.27" __author__ = "Matt Love" __copyright__ = f"Copyright {datetime.now().year} Matthew Love" __license__ = "BSD 3-Clause Clear License" diff --git a/hyperglass/ui/components/Result.js b/hyperglass/ui/components/Result.js index cd2aef1..4907e7f 100644 --- a/hyperglass/ui/components/Result.js +++ b/hyperglass/ui/components/Result.js @@ -1,16 +1,16 @@ import React, { useEffect, useState } from "react"; import { - AccordionItem, - AccordionHeader, - AccordionPanel, - Alert, - Box, - ButtonGroup, - css, - Flex, - Tooltip, - Text, - useColorMode, + AccordionItem, + AccordionHeader, + AccordionPanel, + Alert, + Box, + ButtonGroup, + css, + Flex, + Tooltip, + Text, + useColorMode } from "@chakra-ui/core"; import styled from "@emotion/styled"; import LightningBolt from "~/components/icons/LightningBolt"; @@ -28,230 +28,301 @@ import CacheTimeout from "~/components/CacheTimeout"; format.extend(String.prototype, {}); const FormattedError = ({ keywords, message }) => { - const patternStr = keywords.map((kw) => `(${kw})`).join("|"); - const pattern = new RegExp(patternStr, "gi"); - let errorFmt; - try { - errorFmt = strReplace(message, pattern, (match) => ( - - {match} - - )); - } catch (err) { - errorFmt = {message}; - } - return {keywords.length !== 0 ? errorFmt : message}; + const patternStr = keywords.map(kw => `(${kw})`).join("|"); + const pattern = new RegExp(patternStr, "gi"); + let errorFmt; + try { + errorFmt = strReplace(message, pattern, match => ( + + {match} + + )); + } catch (err) { + errorFmt = {message}; + } + return {keywords.length !== 0 ? errorFmt : message}; }; const AccordionHeaderWrapper = styled(Flex)` - justify-content: space-between; - &:hover { - background-color: ${(props) => props.hoverBg}; - } - &:focus { - box-shadow: "outline"; - } + justify-content: space-between; + &:hover { + background-color: ${props => props.hoverBg}; + } + &:focus { + box-shadow: "outline"; + } `; -const statusMap = { success: "success", warning: "warning", error: "warning", danger: "error" }; +const statusMap = { + success: "success", + warning: "warning", + error: "warning", + danger: "error" +}; const bg = { dark: "gray.800", light: "blackAlpha.100" }; const color = { dark: "white", light: "black" }; const selectionBg = { dark: "white", light: "black" }; const selectionColor = { dark: "black", light: "white" }; const Result = React.forwardRef( - ( - { - device, - timeout, - queryLocation, - queryType, - queryVrf, - queryTarget, - index, - resultsComplete, - setComplete, - }, - ref - ) => { - const config = useConfig(); - const { isSm } = useMedia(); - const { colorMode } = useColorMode(); - const [{ data, loading, error }, refetch] = useAxios({ - url: "/api/query/", - method: "post", - data: { - query_location: queryLocation, - query_type: queryType, - query_vrf: queryVrf, - query_target: queryTarget, - }, - timeout: timeout, - useCache: false, - }); + ( + { + device, + timeout, + queryLocation, + queryType, + queryVrf, + queryTarget, + index, + resultsComplete, + setComplete + }, + ref + ) => { + const config = useConfig(); + const { isSm } = useMedia(); + const { colorMode } = useColorMode(); + const [{ data, loading, error }, refetch] = useAxios({ + url: "/api/query/", + method: "post", + data: { + query_location: queryLocation, + query_type: queryType, + query_vrf: queryVrf, + query_target: queryTarget + }, + timeout: timeout, + useCache: false + }); - const [isOpen, setOpen] = useState(false); - const [hasOverride, setOverride] = useState(false); + const [isOpen, setOpen] = useState(false); + const [hasOverride, setOverride] = useState(false); - const handleToggle = () => { - setOpen(!isOpen); - setOverride(true); - }; - const cleanOutput = - data && - data.output - .split("\\n") - .join("\n") - .replace(/\n\n/g, "\n"); + const handleToggle = () => { + setOpen(!isOpen); + setOverride(true); + }; + const cleanOutput = + data && + data.output + .split("\\n") + .join("\n") + .replace(/\n\n/g, "\n"); - const errorKw = (error && error.response?.data?.keywords) || []; + const errorKw = (error && error.response?.data?.keywords) || []; - let errorMsg; - if (error && error.response?.data?.output) { - errorMsg = error.response.data.output; - } else if (error && error.message.startsWith("timeout")) { - errorMsg = config.messages.request_timeout; - } else if (error?.response?.statusText) { - errorMsg = startCase(error.response.statusText); - } else if (error && error.message) { - errorMsg = startCase(error.message); - } else { - errorMsg = config.messages.general; - } - - error && console.dir(error); - - const errorLevel = - (error?.response?.data?.level && statusMap[error.response?.data?.level]) ?? "error"; - - const cacheLg = ( - <> - - {data?.cached && ( - - - - - - )} - - ); - const cacheSm = ( - <> - {data?.cached && ( - - - - - - )} - - - ); - - const cacheData = isSm ? cacheSm : cacheLg; - - useEffect(() => { - !loading && resultsComplete === null && setComplete(index); - }, [loading, resultsComplete]); - - useEffect(() => { - resultsComplete === index && !hasOverride && setOpen(true); - }, [resultsComplete, index]); - return ( - - - - - - - - - - - - - - {data && !error && ( - - {cleanOutput} - - )} - {error && ( - - - - )} - - - - - - {data && !error && config.cache.show_text && cacheData} - - - - - ); + let errorMsg; + if (error && error.response?.data?.output) { + errorMsg = error.response.data.output; + } else if (error && error.message.startsWith("timeout")) { + errorMsg = config.messages.request_timeout; + } else if (error?.response?.statusText) { + errorMsg = startCase(error.response.statusText); + } else if (error && error.message) { + errorMsg = startCase(error.message); + } else { + errorMsg = config.messages.general; } + + error && console.dir(error); + + const errorLevel = + (error?.response?.data?.level && + statusMap[error.response?.data?.level]) ?? + "error"; + + const cacheLg = ( + <> + + {data?.cached && ( + + + + + + )} + + ); + const cacheSm = ( + <> + {data?.cached && ( + + + + + + )} + + + ); + + const cacheData = isSm ? cacheSm : cacheLg; + + useEffect(() => { + !loading && resultsComplete === null && setComplete(index); + }, [loading, resultsComplete]); + + useEffect(() => { + resultsComplete === index && !hasOverride && setOpen(true); + }, [resultsComplete, index]); + useEffect(() => { + data && console.log(data); + }); + return ( + + + + + + + + + + + + + + {data && !error && ( + + {cleanOutput} + + )} + {error && ( + + + + )} + + + + + + {data && !error && config.cache.show_text && isSm ? ( + <> + + + + + + + + ) : data && !error && config.cache.show_text ? ( + <> + + + + + + + + ) : null} + + + + + ); + } ); Result.displayName = "HyperglassQueryResult"; diff --git a/hyperglass/ui/components/ResultHeader.js b/hyperglass/ui/components/ResultHeader.js index 764d0f5..9e0845c 100644 --- a/hyperglass/ui/components/ResultHeader.js +++ b/hyperglass/ui/components/ResultHeader.js @@ -1,55 +1,70 @@ import React from "react"; -import { AccordionIcon, Icon, Spinner, Stack, Text, Tooltip, useColorMode } from "@chakra-ui/core"; +import { + AccordionIcon, + Icon, + Spinner, + Stack, + Text, + Tooltip, + useColorMode +} from "@chakra-ui/core"; import format from "string-format"; import useConfig from "~/components/HyperglassProvider"; format.extend(String.prototype, {}); const runtimeText = (runtime, text) => { - let unit; - if (runtime === 1) { - unit = "seconds"; - } else { - unit = "second"; - } - const fmt = text.format({ seconds: runtime }); - return `${fmt} ${unit}`; + let unit; + if (runtime === 1) { + unit = "second"; + } else { + unit = "seconds"; + } + const fmt = text.format({ seconds: runtime }); + return `${fmt} ${unit}`; }; const statusColor = { dark: "primary.300", light: "primary.500" }; const warningColor = { dark: 300, light: 500 }; const defaultStatusColor = { - dark: "success.300", - light: "success.500", + dark: "success.300", + light: "success.500" }; -export default React.forwardRef(({ title, loading, error, errorMsg, errorLevel, runtime }, ref) => { +export default React.forwardRef( + ({ title, loading, error, errorMsg, errorLevel, runtime }, ref) => { const { colorMode } = useColorMode(); const config = useConfig(); return ( - - {loading ? ( - - ) : error ? ( - - - - ) : ( - - - - )} - {title} - - + + {loading ? ( + + ) : error ? ( + + + + ) : ( + + + + )} + {title} + + ); -}); + } +); diff --git a/install.sh b/install.sh index 6f93636..c3da15a 100755 --- a/install.sh +++ b/install.sh @@ -2,7 +2,7 @@ set -e -HYPERGLASS_VERSION="1.0.0b26" +HYPERGLASS_VERSION="1.0.0b27" MIN_PYTHON_MAJOR="3" MIN_PYTHON_MINOR="6" diff --git a/pyproject.toml b/pyproject.toml index 7fbfe7f..4079d49 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.masonry.api" [tool.poetry] name = "hyperglass" -version = "1.0.0-beta.26" +version = "1.0.0-beta.27" description = "hyperglass is the modern network looking glass that tries to make the internet better." authors = ["Matt Love "] readme = "README.md"