mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
vmanomaly: Add Download Config button
This commit is contained in:
parent
a5f81f67fd
commit
7bac53379d
2 changed files with 52 additions and 4 deletions
|
@ -47,7 +47,7 @@ const AnomalyLayout: FC = () => {
|
||||||
className={classNames({
|
className={classNames({
|
||||||
"vm-container-body": true,
|
"vm-container-body": true,
|
||||||
"vm-container-body_mobile": isMobile,
|
"vm-container-body_mobile": isMobile,
|
||||||
"vm-container-body_app": appModeEnable
|
"vm-container-body_app": appModeEnable,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Outlet/>
|
<Outlet/>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { FC, useMemo, useRef, useState } from "preact/compat";
|
import React, { FC, useMemo, useRef, useState } from "preact/compat";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import useDeviceDetect from "../../hooks/useDeviceDetect";
|
import useDeviceDetect from "../../hooks/useDeviceDetect";
|
||||||
import { ForecastType } from "../../types";
|
import { ErrorTypes, ForecastType } from "../../types";
|
||||||
import { useSetQueryParams } from "../CustomPanel/hooks/useSetQueryParams";
|
import { useSetQueryParams } from "../CustomPanel/hooks/useSetQueryParams";
|
||||||
import QueryConfigurator from "../CustomPanel/QueryConfigurator/QueryConfigurator";
|
import QueryConfigurator from "../CustomPanel/QueryConfigurator/QueryConfigurator";
|
||||||
import "../CustomPanel/style.scss";
|
import "../CustomPanel/style.scss";
|
||||||
|
@ -15,6 +15,8 @@ import GraphTab from "../CustomPanel/CustomPanelTabs/GraphTab";
|
||||||
import { extractFields, isForecast } from "../../utils/uplot";
|
import { extractFields, isForecast } from "../../utils/uplot";
|
||||||
import { MetricResult } from "../../api/types";
|
import { MetricResult } from "../../api/types";
|
||||||
import { promValueToNumber } from "../../utils/metric";
|
import { promValueToNumber } from "../../utils/metric";
|
||||||
|
import Button from "../../components/Main/Button/Button";
|
||||||
|
import { useAppState } from "../../state/common/StateContext";
|
||||||
|
|
||||||
// Hardcoded to 1.0 for now; consider adding a UI slider for threshold adjustment in the future.
|
// Hardcoded to 1.0 for now; consider adding a UI slider for threshold adjustment in the future.
|
||||||
const ANOMALY_SCORE_THRESHOLD = 1;
|
const ANOMALY_SCORE_THRESHOLD = 1;
|
||||||
|
@ -22,6 +24,7 @@ const ANOMALY_SCORE_THRESHOLD = 1;
|
||||||
const ExploreAnomaly: FC = () => {
|
const ExploreAnomaly: FC = () => {
|
||||||
useSetQueryParams();
|
useSetQueryParams();
|
||||||
const { isMobile } = useDeviceDetect();
|
const { isMobile } = useDeviceDetect();
|
||||||
|
const { serverUrl } = useAppState();
|
||||||
|
|
||||||
const { query } = useQueryState();
|
const { query } = useQueryState();
|
||||||
const { customStep } = useGraphState();
|
const { customStep } = useGraphState();
|
||||||
|
@ -44,7 +47,7 @@ const ExploreAnomaly: FC = () => {
|
||||||
visible: true,
|
visible: true,
|
||||||
customStep,
|
customStep,
|
||||||
hideQuery,
|
hideQuery,
|
||||||
showAllSeries
|
showAllSeries,
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = useMemo(() => {
|
const data = useMemo(() => {
|
||||||
|
@ -63,7 +66,7 @@ const ExploreAnomaly: FC = () => {
|
||||||
if (!anomalyScoreDataByLabels) return false;
|
if (!anomalyScoreDataByLabels) return false;
|
||||||
const anomalyScore = anomalyScoreDataByLabels.values.find(([tMax]) => tMax === t) as [number, string];
|
const anomalyScore = anomalyScoreDataByLabels.values.find(([tMax]) => tMax === t) as [number, string];
|
||||||
return anomalyScore && promValueToNumber(anomalyScore[1]) > ANOMALY_SCORE_THRESHOLD;
|
return anomalyScore && promValueToNumber(anomalyScore[1]) > ANOMALY_SCORE_THRESHOLD;
|
||||||
})
|
}),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const filterData = detectedData.filter(d => (d.value !== ForecastType.anomaly) && d.value) as MetricResult[];
|
const filterData = detectedData.filter(d => (d.value !== ForecastType.anomaly) && d.value) as MetricResult[];
|
||||||
|
@ -74,6 +77,30 @@ const ExploreAnomaly: FC = () => {
|
||||||
setHideError(false);
|
setHideError(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const downloadLinkRef = React.createRef<HTMLAnchorElement>();
|
||||||
|
const downloadConfig = async () => {
|
||||||
|
const response = await fetch(`${serverUrl}/api/vmanomaly/config.yaml`, { });
|
||||||
|
if (!response.ok) {
|
||||||
|
// const errorType = resp.errorType ? `${resp.errorType}\r\n` : "";
|
||||||
|
// const msg = `${errorType}${resp?.error || resp?.message || "unknown error"}`;
|
||||||
|
const msg = `Cannot download config: ${response.status} ${response.statusText}`;
|
||||||
|
console.error(msg);
|
||||||
|
// TODO: show error to user
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const blob = await response.blob();
|
||||||
|
// const blob = new Blob([], { type: "application/yaml" });
|
||||||
|
const url = window.URL.createObjectURL(blob);
|
||||||
|
const a = downloadLinkRef.current;
|
||||||
|
if (a == null) {
|
||||||
|
throw Error("Hidden <a> is not present");
|
||||||
|
}
|
||||||
|
a.href = url;
|
||||||
|
a.download = "config.yaml";
|
||||||
|
a.click();
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classNames({
|
className={classNames({
|
||||||
|
@ -98,6 +125,27 @@ const ExploreAnomaly: FC = () => {
|
||||||
onChange={setShowAllSeries}
|
onChange={setShowAllSeries}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
<div
|
||||||
|
className={classNames({
|
||||||
|
"vm-custom-panel-body": true,
|
||||||
|
"vm-custom-panel-body_mobile": isMobile,
|
||||||
|
"vm-block": true,
|
||||||
|
"vm-block_mobile": isMobile,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
variant="contained"
|
||||||
|
onClick={downloadConfig}
|
||||||
|
>
|
||||||
|
Download Config
|
||||||
|
</Button>
|
||||||
|
{/*eslint-disable-next-line*/}
|
||||||
|
<a className="hidden" ref={downloadLinkRef} href="#"/>
|
||||||
|
|
||||||
|
{error && <Alert variant="error">{error}</Alert>}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={classNames({
|
className={classNames({
|
||||||
"vm-custom-panel-body": true,
|
"vm-custom-panel-body": true,
|
||||||
|
|
Loading…
Reference in a new issue