2020-05-29 17:47:53 -07:00
|
|
|
/** @jsx jsx */
|
2020-10-07 09:41:58 -07:00
|
|
|
import { jsx } from '@emotion/core';
|
|
|
|
import { forwardRef, useEffect, useState } from 'react';
|
2020-01-17 02:50:57 -07:00
|
|
|
import {
|
2020-04-18 12:09:16 -07:00
|
|
|
AccordionItem,
|
|
|
|
AccordionHeader,
|
|
|
|
AccordionPanel,
|
|
|
|
Alert,
|
|
|
|
Box,
|
|
|
|
ButtonGroup,
|
|
|
|
css,
|
|
|
|
Flex,
|
|
|
|
Tooltip,
|
|
|
|
Text,
|
2020-05-29 17:47:53 -07:00
|
|
|
useColorMode,
|
2020-10-07 09:41:58 -07:00
|
|
|
useTheme,
|
|
|
|
} from '@chakra-ui/core';
|
|
|
|
import { BsLightningFill } from '@meronex/icons/bs';
|
|
|
|
import styled from '@emotion/styled';
|
|
|
|
import useAxios from 'axios-hooks';
|
|
|
|
import strReplace from 'react-string-replace';
|
|
|
|
import format from 'string-format';
|
|
|
|
import { startCase } from 'lodash';
|
|
|
|
import { useConfig, useMedia } from 'app/context';
|
2020-09-28 11:54:00 -07:00
|
|
|
import {
|
|
|
|
BGPTable,
|
|
|
|
CacheTimeout,
|
|
|
|
CopyButton,
|
|
|
|
RequeryButton,
|
|
|
|
ResultHeader,
|
2020-10-07 09:41:58 -07:00
|
|
|
TextOutput,
|
|
|
|
} from 'app/components';
|
|
|
|
import { tableToString } from 'app/util';
|
2020-01-17 02:50:57 -07:00
|
|
|
|
2020-04-18 07:58:46 -07:00
|
|
|
format.extend(String.prototype, {});
|
|
|
|
|
2020-01-17 02:50:57 -07:00
|
|
|
const FormattedError = ({ keywords, message }) => {
|
2020-10-07 09:41:58 -07:00
|
|
|
const patternStr = keywords.map(kw => `(${kw})`).join('|');
|
|
|
|
const pattern = new RegExp(patternStr, 'gi');
|
2020-04-18 12:09:16 -07:00
|
|
|
let errorFmt;
|
|
|
|
try {
|
|
|
|
errorFmt = strReplace(message, pattern, match => (
|
|
|
|
<Text key={match} as="strong">
|
|
|
|
{match}
|
|
|
|
</Text>
|
|
|
|
));
|
|
|
|
} catch (err) {
|
|
|
|
errorFmt = <Text as="span">{message}</Text>;
|
|
|
|
}
|
|
|
|
return <Text as="span">{keywords.length !== 0 ? errorFmt : message}</Text>;
|
2020-01-17 02:50:57 -07:00
|
|
|
};
|
|
|
|
|
2020-01-20 00:37:04 -07:00
|
|
|
const AccordionHeaderWrapper = styled(Flex)`
|
2020-04-18 12:09:16 -07:00
|
|
|
justify-content: space-between;
|
|
|
|
&:hover {
|
|
|
|
background-color: ${props => props.hoverBg};
|
|
|
|
}
|
|
|
|
&:focus {
|
2020-10-07 09:41:58 -07:00
|
|
|
box-shadow: 'outline';
|
2020-04-18 12:09:16 -07:00
|
|
|
}
|
2020-01-20 00:37:04 -07:00
|
|
|
`;
|
|
|
|
|
2020-04-18 12:09:16 -07:00
|
|
|
const statusMap = {
|
2020-10-07 09:41:58 -07:00
|
|
|
success: 'success',
|
|
|
|
warning: 'warning',
|
|
|
|
error: 'warning',
|
|
|
|
danger: 'error',
|
2020-04-18 12:09:16 -07:00
|
|
|
};
|
2020-05-29 17:47:53 -07:00
|
|
|
|
2020-10-07 09:41:58 -07:00
|
|
|
const color = { dark: 'white', light: 'black' };
|
|
|
|
const scrollbar = { dark: 'whiteAlpha.300', light: 'blackAlpha.300' };
|
|
|
|
const scrollbarHover = { dark: 'whiteAlpha.400', light: 'blackAlpha.400' };
|
|
|
|
const scrollbarBg = { dark: 'whiteAlpha.50', light: 'blackAlpha.50' };
|
2020-01-26 02:24:12 -07:00
|
|
|
|
2020-09-28 11:54:00 -07:00
|
|
|
export const Result = forwardRef(
|
2020-04-18 12:09:16 -07:00
|
|
|
(
|
|
|
|
{
|
|
|
|
device,
|
|
|
|
timeout,
|
|
|
|
queryLocation,
|
|
|
|
queryType,
|
|
|
|
queryVrf,
|
|
|
|
queryTarget,
|
|
|
|
index,
|
|
|
|
resultsComplete,
|
2020-10-07 09:41:58 -07:00
|
|
|
setComplete,
|
2020-04-18 12:09:16 -07:00
|
|
|
},
|
2020-10-07 09:41:58 -07:00
|
|
|
ref,
|
2020-04-18 12:09:16 -07:00
|
|
|
) => {
|
|
|
|
const config = useConfig();
|
2020-05-29 17:47:53 -07:00
|
|
|
const theme = useTheme();
|
2020-04-18 12:09:16 -07:00
|
|
|
const { isSm } = useMedia();
|
|
|
|
const { colorMode } = useColorMode();
|
2020-04-19 09:50:31 -07:00
|
|
|
let [{ data, loading, error }, refetch] = useAxios({
|
2020-10-07 09:41:58 -07:00
|
|
|
url: '/api/query/',
|
|
|
|
method: 'post',
|
2020-04-18 12:09:16 -07:00
|
|
|
data: {
|
|
|
|
query_location: queryLocation,
|
|
|
|
query_type: queryType,
|
|
|
|
query_vrf: queryVrf,
|
2020-10-07 09:41:58 -07:00
|
|
|
query_target: queryTarget,
|
2020-04-18 12:09:16 -07:00
|
|
|
},
|
|
|
|
timeout: timeout,
|
2020-10-07 09:41:58 -07:00
|
|
|
useCache: false,
|
2020-04-18 12:09:16 -07:00
|
|
|
});
|
2020-03-22 11:43:14 -07:00
|
|
|
|
2020-04-18 12:09:16 -07:00
|
|
|
const [isOpen, setOpen] = useState(false);
|
|
|
|
const [hasOverride, setOverride] = useState(false);
|
2020-03-22 11:43:14 -07:00
|
|
|
|
2020-04-18 12:09:16 -07:00
|
|
|
const handleToggle = () => {
|
|
|
|
setOpen(!isOpen);
|
|
|
|
setOverride(true);
|
|
|
|
};
|
2020-01-17 02:50:57 -07:00
|
|
|
|
2020-04-18 12:09:16 -07:00
|
|
|
const errorKw = (error && error.response?.data?.keywords) || [];
|
2020-01-26 02:24:12 -07:00
|
|
|
|
2020-04-18 12:09:16 -07:00
|
|
|
let errorMsg;
|
|
|
|
if (error && error.response?.data?.output) {
|
|
|
|
errorMsg = error.response.data.output;
|
2020-10-07 09:41:58 -07:00
|
|
|
} else if (error && error.message.startsWith('timeout')) {
|
2020-04-18 12:09:16 -07:00
|
|
|
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;
|
|
|
|
}
|
2020-01-26 02:24:12 -07:00
|
|
|
|
2020-04-19 09:50:31 -07:00
|
|
|
error && console.error(error);
|
2020-01-27 21:28:27 -07:00
|
|
|
|
2020-04-18 12:09:16 -07:00
|
|
|
const errorLevel =
|
2020-10-07 09:41:58 -07:00
|
|
|
(error?.response?.data?.level && statusMap[error.response?.data?.level]) ?? 'error';
|
2020-01-26 02:24:12 -07:00
|
|
|
|
2020-09-28 11:54:00 -07:00
|
|
|
const structuredDataComponent = {
|
|
|
|
bgp_route: BGPTable,
|
|
|
|
bgp_aspath: BGPTable,
|
|
|
|
bgp_community: BGPTable,
|
|
|
|
ping: TextOutput,
|
2020-10-07 09:41:58 -07:00
|
|
|
traceroute: TextOutput,
|
2020-09-28 11:54:00 -07:00
|
|
|
};
|
|
|
|
|
2020-05-29 17:47:53 -07:00
|
|
|
let Output = TextOutput;
|
2020-07-19 15:45:34 -07:00
|
|
|
let copyValue = data?.output;
|
2020-09-28 11:54:00 -07:00
|
|
|
|
2020-10-07 09:41:58 -07:00
|
|
|
if (data?.format === 'application/json') {
|
2020-05-29 17:47:53 -07:00
|
|
|
Output = structuredDataComponent[queryType];
|
2020-07-19 15:45:34 -07:00
|
|
|
copyValue = tableToString(queryTarget, data, config);
|
2020-05-29 17:47:53 -07:00
|
|
|
}
|
|
|
|
|
2020-07-23 09:20:15 -07:00
|
|
|
if (error) {
|
|
|
|
copyValue = errorMsg;
|
|
|
|
}
|
|
|
|
|
2020-04-18 12:09:16 -07:00
|
|
|
useEffect(() => {
|
|
|
|
!loading && resultsComplete === null && setComplete(index);
|
|
|
|
}, [loading, resultsComplete]);
|
2020-03-22 11:43:14 -07:00
|
|
|
|
2020-04-18 12:09:16 -07:00
|
|
|
useEffect(() => {
|
|
|
|
resultsComplete === index && !hasOverride && setOpen(true);
|
|
|
|
}, [resultsComplete, index]);
|
2020-04-19 09:50:31 -07:00
|
|
|
|
2020-04-18 12:09:16 -07:00
|
|
|
return (
|
|
|
|
<AccordionItem
|
|
|
|
isOpen={isOpen}
|
|
|
|
isDisabled={loading}
|
|
|
|
ref={ref}
|
|
|
|
css={css({
|
2020-10-07 09:41:58 -07:00
|
|
|
'&:last-of-type': { borderBottom: 'none' },
|
|
|
|
'&:first-of-type': { borderTop: 'none' },
|
|
|
|
})(theme)}>
|
2020-04-18 12:09:16 -07:00
|
|
|
<AccordionHeaderWrapper hoverBg="blackAlpha.50">
|
|
|
|
<AccordionHeader
|
|
|
|
flex="1 0 auto"
|
|
|
|
py={2}
|
|
|
|
_hover={{}}
|
|
|
|
_focus={{}}
|
|
|
|
w="unset"
|
2020-10-07 09:41:58 -07:00
|
|
|
onClick={handleToggle}>
|
2020-04-18 12:09:16 -07:00
|
|
|
<ResultHeader
|
|
|
|
title={device.display_name}
|
|
|
|
loading={loading}
|
|
|
|
error={error}
|
|
|
|
errorMsg={errorMsg}
|
|
|
|
errorLevel={errorLevel}
|
|
|
|
runtime={data?.runtime}
|
|
|
|
/>
|
|
|
|
</AccordionHeader>
|
2020-05-29 17:47:53 -07:00
|
|
|
<ButtonGroup px={[1, 1, 3, 3]} py={2}>
|
2020-10-07 09:41:58 -07:00
|
|
|
<CopyButton copyValue={copyValue} variant="ghost" isDisabled={loading} />
|
|
|
|
<RequeryButton requery={refetch} variant="ghost" isDisabled={loading} />
|
2020-04-18 12:09:16 -07:00
|
|
|
</ButtonGroup>
|
|
|
|
</AccordionHeaderWrapper>
|
|
|
|
<AccordionPanel
|
|
|
|
pb={4}
|
|
|
|
overflowX="auto"
|
2020-05-29 17:47:53 -07:00
|
|
|
css={css({
|
2020-10-07 09:41:58 -07:00
|
|
|
WebkitOverflowScrolling: 'touch',
|
|
|
|
'&::-webkit-scrollbar': { height: '5px' },
|
|
|
|
'&::-webkit-scrollbar-track': {
|
|
|
|
backgroundColor: scrollbarBg[colorMode],
|
2020-05-29 17:47:53 -07:00
|
|
|
},
|
2020-10-07 09:41:58 -07:00
|
|
|
'&::-webkit-scrollbar-thumb': {
|
|
|
|
backgroundColor: scrollbar[colorMode],
|
2020-05-29 17:47:53 -07:00
|
|
|
},
|
2020-10-07 09:41:58 -07:00
|
|
|
'&::-webkit-scrollbar-thumb:hover': {
|
|
|
|
backgroundColor: scrollbarHover[colorMode],
|
2020-05-29 17:47:53 -07:00
|
|
|
},
|
|
|
|
|
2020-10-07 09:41:58 -07:00
|
|
|
'-ms-overflow-style': { display: 'none' },
|
|
|
|
})(theme)}>
|
2020-06-06 01:25:42 -07:00
|
|
|
<Flex direction="column" flexWrap="wrap">
|
2020-10-07 09:41:58 -07:00
|
|
|
<Flex direction="column" flex="1 0 auto" maxW={error ? '100%' : null}>
|
2020-05-29 17:47:53 -07:00
|
|
|
{!error && data && <Output>{data?.output}</Output>}
|
2020-04-18 12:09:16 -07:00
|
|
|
{error && (
|
|
|
|
<Alert rounded="lg" my={2} py={4} status={errorLevel}>
|
|
|
|
<FormattedError keywords={errorKw} message={errorMsg} />
|
|
|
|
</Alert>
|
|
|
|
)}
|
|
|
|
</Flex>
|
|
|
|
</Flex>
|
2020-03-23 01:10:27 -07:00
|
|
|
|
2020-04-18 12:09:16 -07:00
|
|
|
<Flex direction="row" flexWrap="wrap">
|
|
|
|
<Flex
|
|
|
|
px={3}
|
|
|
|
mt={2}
|
2020-10-07 09:41:58 -07:00
|
|
|
justifyContent={['flex-start', 'flex-start', 'flex-end', 'flex-end']}
|
|
|
|
flex="1 0 auto">
|
2020-04-19 09:50:31 -07:00
|
|
|
{config.cache.show_text && data && !error && (
|
|
|
|
<>
|
|
|
|
{!isSm && (
|
|
|
|
<CacheTimeout
|
|
|
|
timeout={config.cache.timeout}
|
|
|
|
text={config.web.text.cache_prefix}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
<Tooltip
|
2020-10-07 09:41:58 -07:00
|
|
|
display={data?.cached ? null : 'none'}
|
2020-04-19 09:50:31 -07:00
|
|
|
hasArrow
|
|
|
|
label={config.web.text.cache_icon.format({
|
2020-10-07 09:41:58 -07:00
|
|
|
time: data?.timestamp,
|
2020-04-19 09:50:31 -07:00
|
|
|
})}
|
2020-10-07 09:41:58 -07:00
|
|
|
placement="top">
|
|
|
|
<Box ml={1} display={data?.cached ? 'block' : 'none'}>
|
2020-09-28 11:59:36 -07:00
|
|
|
<BsLightningFill color={color[colorMode]} />
|
2020-04-19 09:50:31 -07:00
|
|
|
</Box>
|
|
|
|
</Tooltip>
|
|
|
|
{isSm && (
|
|
|
|
<CacheTimeout
|
|
|
|
timeout={config.cache.timeout}
|
|
|
|
text={config.web.text.cache_prefix}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
</>
|
|
|
|
)}
|
2020-04-18 12:09:16 -07:00
|
|
|
</Flex>
|
|
|
|
</Flex>
|
|
|
|
</AccordionPanel>
|
|
|
|
</AccordionItem>
|
|
|
|
);
|
2020-10-07 09:41:58 -07:00
|
|
|
},
|
2020-01-17 02:50:57 -07:00
|
|
|
);
|