diff --git a/ui/components/FormField.js b/ui/components/FormField.js
index 7abb83d..e86c309 100644
--- a/ui/components/FormField.js
+++ b/ui/components/FormField.js
@@ -1,8 +1,19 @@
import React from "react";
import { Flex, FormControl, FormLabel, FormErrorMessage, useColorMode } from "@chakra-ui/core";
-import HelpModal from "~/components/HelpModal";
-export default ({ label, name, error, hiddenLabels, helpIcon, children, ...props }) => {
+export default ({
+ label,
+ name,
+ error,
+ hiddenLabels,
+ helpIcon,
+ targetInfo,
+ setTarget,
+ labelAddOn,
+ fieldAddOn,
+ children,
+ ...props
+}) => {
const { colorMode } = useColorMode();
const labelColor = { dark: "whiteAlpha.600", light: "blackAlpha.600" };
return (
@@ -22,11 +33,20 @@ export default ({ label, name, error, hiddenLabels, helpIcon, children, ...props
color={labelColor[colorMode]}
pl={1}
opacity={hiddenLabels ? 0 : null}
+ display="flex"
+ alignItems="center"
+ justifyContent="space-between"
+ pr={0}
>
{label}
- {helpIcon?.enable && }
+ {labelAddOn || null}
{children}
+ {fieldAddOn && (
+
+ {fieldAddOn}
+
+ )}
{error && error.message}
diff --git a/ui/components/HyperglassForm.js b/ui/components/HyperglassForm.js
index 60496d2..fe562cf 100644
--- a/ui/components/HyperglassForm.js
+++ b/ui/components/HyperglassForm.js
@@ -5,10 +5,12 @@ import lodash from "lodash";
import * as yup from "yup";
import format from "string-format";
import FormField from "~/components/FormField";
+import HelpModal from "~/components/HelpModal";
import QueryLocation from "~/components/QueryLocation";
import QueryType from "~/components/QueryType";
import QueryTarget from "~/components/QueryTarget";
import QueryVrf from "~/components/QueryVrf";
+import ResolvedTarget from "~/components/ResolvedTarget";
import SubmitButton from "~/components/SubmitButton";
import useConfig from "~/components/HyperglassProvider";
@@ -49,11 +51,16 @@ const HyperglassForm = React.forwardRef(
const { handleSubmit, register, setValue, errors } = useForm({
validationSchema: formSchema(config)
});
+
const [queryLocation, setQueryLocation] = useState([]);
const [queryType, setQueryType] = useState("");
const [queryVrf, setQueryVrf] = useState("");
+ const [queryTarget, setQueryTarget] = useState("");
const [availVrfs, setAvailVrfs] = useState([]);
+ const [fqdnTarget, setFqdnTarget] = useState("");
+ const [displayTarget, setDisplayTarget] = useState("");
const onSubmit = values => {
+ console.log(values);
setFormData(values);
setSubmitting(true);
};
@@ -81,17 +88,28 @@ const HyperglassForm = React.forwardRef(
? setQueryType(e.value)
: e.field === "query_vrf"
? setQueryVrf(e.value)
+ : e.field === "query_target"
+ ? setQueryTarget(e.value)
: null;
};
+ const vrfContent = config.content.vrf[queryVrf]?.[queryType];
+ const validFqdnQueryType =
+ ["ping", "traceroute", "bgp_route"].includes(queryType) &&
+ fqdnTarget &&
+ queryVrf === "default"
+ ? fqdnTarget
+ : null;
+
useEffect(() => {
register({ name: "query_location" });
+ register({ name: "query_target" });
register({ name: "query_type" });
register({ name: "query_vrf" });
});
return (
+ }
>
@@ -136,10 +156,28 @@ const HyperglassForm = React.forwardRef(
label={config.branding.text.query_target}
name="query_target"
error={errors.query_target}
+ fieldAddOn={
+ validFqdnQueryType && (
+
+ )
+ }
>
diff --git a/ui/components/QueryTarget.js b/ui/components/QueryTarget.js
index 07f7cdb..01e9b63 100644
--- a/ui/components/QueryTarget.js
+++ b/ui/components/QueryTarget.js
@@ -1,6 +1,6 @@
-import React from "react";
+import React, { useState } from "react";
import styled from "@emotion/styled";
-import { Input, useColorMode, useTheme } from "@chakra-ui/core";
+import { Input, useColorMode } from "@chakra-ui/core";
const StyledInput = styled(Input)`
&::placeholder {
@@ -8,26 +8,63 @@ const StyledInput = styled(Input)`
}
`;
-export default ({ placeholder, register }) => {
- const theme = useTheme();
+const fqdnPattern = /(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{1,63}(? {
const { colorMode } = useColorMode();
- const bg = colorMode === "dark" ? theme.colors.whiteAlpha[100] : theme.colors.white;
- const color = colorMode === "dark" ? theme.colors.whiteAlpha[800] : theme.colors.gray[400];
- const border = colorMode === "dark" ? theme.colors.whiteAlpha[50] : theme.colors.gray[100];
- const borderRadius = theme.space[1];
- const placeholderColor =
- colorMode === "dark" ? theme.colors.whiteAlpha[400] : theme.colors.gray[400];
+
+ const handleBlur = () => {
+ if (resolveTarget && displayValue && fqdnPattern.test(displayValue)) {
+ setFqdn(displayValue);
+ } else if (resolveTarget && !displayValue) {
+ setFqdn(false);
+ }
+ };
+ const handleChange = e => {
+ setDisplayValue(e.target.value);
+ setValue({ field: name, value: e.target.value });
+ };
+ const handleKeyDown = e => {
+ if ([9, 13].includes(e.keyCode)) {
+ handleBlur();
+ }
+ };
return (
-
+ <>
+
+
+ >
);
};
+
+QueryTarget.displayName = "QueryTarget";
+export default QueryTarget;
diff --git a/ui/components/ResolvedTarget.js b/ui/components/ResolvedTarget.js
new file mode 100644
index 0000000..9a48ab3
--- /dev/null
+++ b/ui/components/ResolvedTarget.js
@@ -0,0 +1,132 @@
+import React, { useEffect, useState } from "react";
+import { Button, Icon, Spinner, Stack, Tag, Text, Tooltip, useColorMode } from "@chakra-ui/core";
+import useAxios from "axios-hooks";
+import format from "string-format";
+import useConfig from "~/components/HyperglassProvider";
+
+format.extend(String.prototype, {});
+
+const labelBg = { dark: "secondary", light: "secondary" };
+const labelBgSuccess = { dark: "success", light: "success" };
+
+const ResolvedTarget = React.forwardRef(({ target, setTarget, formQueryTarget }, ref) => {
+ const { colorMode } = useColorMode();
+ const config = useConfig();
+ const labelBgStatus = { true: labelBgSuccess[colorMode], false: labelBg[colorMode] };
+ const params4 = {
+ url: "https://cloudflare-dns.com/dns-query",
+ params: { name: target, type: "A" },
+ headers: { accept: "application/dns-json" },
+ timeout: 1000
+ };
+ const params6 = {
+ url: "https://cloudflare-dns.com/dns-query",
+ params: { name: target, type: "AAAA" },
+ headers: { accept: "application/dns-json" },
+ timeout: 1000
+ };
+
+ const [{ data: data4, loading: loading4, error: error4 }] = useAxios(params4);
+ const [{ data: data6, loading: loading6, error: error6 }] = useAxios(params6);
+
+ const [data, setData] = useState("");
+
+ data && setTarget({ field: "query_target", value: data });
+
+ const handleOverride = overridden => {
+ setData(overridden);
+ setTarget({ field: "query_target", value: overridden });
+ };
+
+ const isSelected = value => {
+ console.log("value: ", value, "formQuerytarget: ", formQueryTarget, "target: ", target);
+ return labelBgStatus[value === formQueryTarget];
+ };
+
+ useEffect(() => {
+ if (data6 && data6.Answer && data6.Answer[0].type === 28 && data === "") {
+ handleOverride(data6.Answer[0].data);
+ }
+ }, [data6, data]);
+ useEffect(() => {
+ if (data4 && data4.Answer && data4.Answer[0].type === 28 && data === "") {
+ handleOverride(data4.Answer[0].data);
+ }
+ }, [data4, data]);
+ return (
+
+ {loading4 ||
+ error4 ||
+ (data4?.Answer?.[0] && (
+
+
+
+
+ {loading4 && }
+ {error4 && }
+ {data4?.Answer?.[0] && (
+
+ {data4.Answer[0].data}
+
+ )}
+
+ ))}
+ {loading6 ||
+ error6 ||
+ (data6?.Answer?.[0] && (
+
+
+
+
+ {loading6 && }
+ {error6 && }
+ {data6?.Answer?.[0] && (
+
+ {data6.Answer[0].data}
+
+ )}
+
+ ))}
+
+ );
+});
+
+ResolvedTarget.displayName = "ResolvedTarget";
+export default ResolvedTarget;
diff --git a/ui/components/Result.js b/ui/components/Result.js
index aec4936..99b76d3 100644
--- a/ui/components/Result.js
+++ b/ui/components/Result.js
@@ -4,7 +4,6 @@ import {
AccordionHeader,
AccordionPanel,
Alert,
- AlertDescription,
Box,
ButtonGroup,
css,
@@ -30,7 +29,7 @@ const FormattedError = ({ keywords, message }) => {
{match}
));
- return {errorFmt};
+ return {keywords.length !== 0 ? errorFmt : message};
};
const AccordionHeaderWrapper = styled(Flex)`
@@ -79,12 +78,16 @@ const Result = React.forwardRef(
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";