mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-01 04:52:03 +08:00
Use fetch fix (#995)
* feat: useFetch in tag value is removed and moved to use query * feat: useFetch in all channels is removed and moved to use query * feat: useFetch in edit rule is removed and moved to use query * feat: useFetch in general settings is removed and moved to use query * feat: useFetch in all alerts is changed into use query
This commit is contained in:
parent
844ca57686
commit
93638d5615
323
frontend/src/container/GeneralSettings/GeneralSettings.tsx
Normal file
323
frontend/src/container/GeneralSettings/GeneralSettings.tsx
Normal file
@ -0,0 +1,323 @@
|
|||||||
|
import { Button, Col, Modal, notification, Row, Typography } from 'antd';
|
||||||
|
import setRetentionApi from 'api/settings/setRetention';
|
||||||
|
import TextToolTip from 'components/TextToolTip';
|
||||||
|
import find from 'lodash-es/find';
|
||||||
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import {
|
||||||
|
IDiskType,
|
||||||
|
PayloadProps as GetDisksPayload,
|
||||||
|
} from 'types/api/disks/getDisks';
|
||||||
|
import { PayloadProps as GetRetentionPayload } from 'types/api/settings/getRetention';
|
||||||
|
|
||||||
|
import Retention from './Retention';
|
||||||
|
import { ButtonContainer, ErrorText, ErrorTextContainer } from './styles';
|
||||||
|
|
||||||
|
type NumberOrNull = number | null;
|
||||||
|
|
||||||
|
function GeneralSettings({
|
||||||
|
ttlValuesPayload,
|
||||||
|
getAvailableDiskPayload,
|
||||||
|
}: GeneralSettingsProps): JSX.Element {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [modal, setModal] = useState<boolean>(false);
|
||||||
|
const [postApiLoading, setPostApiLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const [availableDisks] = useState<IDiskType[]>(getAvailableDiskPayload);
|
||||||
|
|
||||||
|
const [currentTTLValues, setCurrentTTLValues] = useState(ttlValuesPayload);
|
||||||
|
|
||||||
|
const [
|
||||||
|
metricsTotalRetentionPeriod,
|
||||||
|
setMetricsTotalRetentionPeriod,
|
||||||
|
] = useState<NumberOrNull>(null);
|
||||||
|
const [
|
||||||
|
metricsS3RetentionPeriod,
|
||||||
|
setMetricsS3RetentionPeriod,
|
||||||
|
] = useState<NumberOrNull>(null);
|
||||||
|
const [
|
||||||
|
tracesTotalRetentionPeriod,
|
||||||
|
setTracesTotalRetentionPeriod,
|
||||||
|
] = useState<NumberOrNull>(null);
|
||||||
|
const [
|
||||||
|
tracesS3RetentionPeriod,
|
||||||
|
setTracesS3RetentionPeriod,
|
||||||
|
] = useState<NumberOrNull>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (currentTTLValues) {
|
||||||
|
setMetricsTotalRetentionPeriod(currentTTLValues.metrics_ttl_duration_hrs);
|
||||||
|
setMetricsS3RetentionPeriod(
|
||||||
|
currentTTLValues.metrics_move_ttl_duration_hrs
|
||||||
|
? currentTTLValues.metrics_move_ttl_duration_hrs
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
setTracesTotalRetentionPeriod(currentTTLValues.traces_ttl_duration_hrs);
|
||||||
|
setTracesS3RetentionPeriod(
|
||||||
|
currentTTLValues.traces_move_ttl_duration_hrs
|
||||||
|
? currentTTLValues.traces_move_ttl_duration_hrs
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [currentTTLValues]);
|
||||||
|
|
||||||
|
const onModalToggleHandler = (): void => {
|
||||||
|
setModal((modal) => !modal);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClickSaveHandler = useCallback(() => {
|
||||||
|
onModalToggleHandler();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const s3Enabled = useMemo(
|
||||||
|
() => !!find(availableDisks, (disks: IDiskType) => disks?.type === 's3'),
|
||||||
|
[availableDisks],
|
||||||
|
);
|
||||||
|
|
||||||
|
const renderConfig = [
|
||||||
|
{
|
||||||
|
name: 'Metrics',
|
||||||
|
retentionFields: [
|
||||||
|
{
|
||||||
|
name: t('settings.total_retention_period'),
|
||||||
|
value: metricsTotalRetentionPeriod,
|
||||||
|
setValue: setMetricsTotalRetentionPeriod,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: t('settings.move_to_s3'),
|
||||||
|
value: metricsS3RetentionPeriod,
|
||||||
|
setValue: setMetricsS3RetentionPeriod,
|
||||||
|
hide: !s3Enabled,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Traces',
|
||||||
|
retentionFields: [
|
||||||
|
{
|
||||||
|
name: t('settings.total_retention_period'),
|
||||||
|
value: tracesTotalRetentionPeriod,
|
||||||
|
setValue: setTracesTotalRetentionPeriod,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: t('settings.move_to_s3'),
|
||||||
|
value: tracesS3RetentionPeriod,
|
||||||
|
setValue: setTracesS3RetentionPeriod,
|
||||||
|
hide: !s3Enabled,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
].map((category): JSX.Element | null => {
|
||||||
|
if (
|
||||||
|
Array.isArray(category.retentionFields) &&
|
||||||
|
category.retentionFields.length > 0
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<Col flex="40%" style={{ minWidth: 475 }} key={category.name}>
|
||||||
|
<Typography.Title level={3}>{category.name}</Typography.Title>
|
||||||
|
|
||||||
|
{category.retentionFields.map((retentionField) => (
|
||||||
|
<Retention
|
||||||
|
key={retentionField.name}
|
||||||
|
text={retentionField.name}
|
||||||
|
retentionValue={retentionField.value}
|
||||||
|
setRetentionValue={retentionField.setValue}
|
||||||
|
hide={!!retentionField.hide}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Col>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||||
|
const onOkHandler = async (): Promise<void> => {
|
||||||
|
try {
|
||||||
|
setPostApiLoading(true);
|
||||||
|
const apiCalls = [];
|
||||||
|
|
||||||
|
if (
|
||||||
|
!(
|
||||||
|
currentTTLValues?.metrics_move_ttl_duration_hrs ===
|
||||||
|
metricsS3RetentionPeriod &&
|
||||||
|
currentTTLValues.metrics_ttl_duration_hrs === metricsTotalRetentionPeriod
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
apiCalls.push(() =>
|
||||||
|
setRetentionApi({
|
||||||
|
type: 'metrics',
|
||||||
|
totalDuration: `${metricsTotalRetentionPeriod || -1}h`,
|
||||||
|
coldStorage: s3Enabled ? 's3' : null,
|
||||||
|
toColdDuration: `${metricsS3RetentionPeriod || -1}h`,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
apiCalls.push(() => Promise.resolve(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!(
|
||||||
|
currentTTLValues?.traces_move_ttl_duration_hrs ===
|
||||||
|
tracesS3RetentionPeriod &&
|
||||||
|
currentTTLValues.traces_ttl_duration_hrs === tracesTotalRetentionPeriod
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
apiCalls.push(() =>
|
||||||
|
setRetentionApi({
|
||||||
|
type: 'traces',
|
||||||
|
totalDuration: `${tracesTotalRetentionPeriod || -1}h`,
|
||||||
|
coldStorage: s3Enabled ? 's3' : null,
|
||||||
|
toColdDuration: `${tracesS3RetentionPeriod || -1}h`,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
apiCalls.push(() => Promise.resolve(null));
|
||||||
|
}
|
||||||
|
const apiCallSequence = ['metrics', 'traces'];
|
||||||
|
const apiResponses = await Promise.all(apiCalls.map((api) => api()));
|
||||||
|
|
||||||
|
apiResponses.forEach((apiResponse, idx) => {
|
||||||
|
const name = apiCallSequence[idx];
|
||||||
|
if (apiResponse) {
|
||||||
|
if (apiResponse.statusCode === 200) {
|
||||||
|
notification.success({
|
||||||
|
message: 'Success!',
|
||||||
|
placement: 'topRight',
|
||||||
|
|
||||||
|
description: t('settings.retention_success_message', { name }),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
notification.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: t('settings.retention_error_message', { name }),
|
||||||
|
placement: 'topRight',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
onModalToggleHandler();
|
||||||
|
setPostApiLoading(false);
|
||||||
|
} catch (error) {
|
||||||
|
notification.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: t('settings.retention_failed_message'),
|
||||||
|
placement: 'topRight',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Updates the currentTTL Values in order to avoid pushing the same values.
|
||||||
|
setCurrentTTLValues({
|
||||||
|
metrics_ttl_duration_hrs: metricsTotalRetentionPeriod || -1,
|
||||||
|
metrics_move_ttl_duration_hrs: metricsS3RetentionPeriod || -1,
|
||||||
|
traces_ttl_duration_hrs: tracesTotalRetentionPeriod || -1,
|
||||||
|
traces_move_ttl_duration_hrs: tracesS3RetentionPeriod || -1,
|
||||||
|
});
|
||||||
|
|
||||||
|
setModal(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||||
|
const [isDisabled, errorText] = useMemo((): [boolean, string] => {
|
||||||
|
// Various methods to return dynamic error message text.
|
||||||
|
const messages = {
|
||||||
|
compareError: (name: string | number): string =>
|
||||||
|
t('settings.retention_comparison_error', { name }),
|
||||||
|
nullValueError: (name: string | number): string =>
|
||||||
|
t('settings.retention_null_value_error', { name }),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Defaults to button not disabled and empty error message text.
|
||||||
|
let isDisabled = false;
|
||||||
|
let errorText = '';
|
||||||
|
|
||||||
|
if (s3Enabled) {
|
||||||
|
if (
|
||||||
|
(metricsTotalRetentionPeriod || metricsS3RetentionPeriod) &&
|
||||||
|
Number(metricsTotalRetentionPeriod) <= Number(metricsS3RetentionPeriod)
|
||||||
|
) {
|
||||||
|
isDisabled = true;
|
||||||
|
errorText = messages.compareError('metrics');
|
||||||
|
} else if (
|
||||||
|
(tracesTotalRetentionPeriod || tracesS3RetentionPeriod) &&
|
||||||
|
Number(tracesTotalRetentionPeriod) <= Number(tracesS3RetentionPeriod)
|
||||||
|
) {
|
||||||
|
isDisabled = true;
|
||||||
|
errorText = messages.compareError('traces');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!metricsTotalRetentionPeriod || !tracesTotalRetentionPeriod) {
|
||||||
|
isDisabled = true;
|
||||||
|
if (!metricsTotalRetentionPeriod && !tracesTotalRetentionPeriod) {
|
||||||
|
errorText = messages.nullValueError('metrics and traces');
|
||||||
|
} else if (!metricsTotalRetentionPeriod) {
|
||||||
|
errorText = messages.nullValueError('metrics');
|
||||||
|
} else if (!tracesTotalRetentionPeriod) {
|
||||||
|
errorText = messages.nullValueError('traces');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
currentTTLValues?.metrics_ttl_duration_hrs === metricsTotalRetentionPeriod &&
|
||||||
|
currentTTLValues.metrics_move_ttl_duration_hrs ===
|
||||||
|
metricsS3RetentionPeriod &&
|
||||||
|
currentTTLValues.traces_ttl_duration_hrs === tracesTotalRetentionPeriod &&
|
||||||
|
currentTTLValues.traces_move_ttl_duration_hrs === tracesS3RetentionPeriod
|
||||||
|
) {
|
||||||
|
isDisabled = true;
|
||||||
|
}
|
||||||
|
return [isDisabled, errorText];
|
||||||
|
}, [
|
||||||
|
currentTTLValues,
|
||||||
|
metricsS3RetentionPeriod,
|
||||||
|
metricsTotalRetentionPeriod,
|
||||||
|
s3Enabled,
|
||||||
|
t,
|
||||||
|
tracesS3RetentionPeriod,
|
||||||
|
tracesTotalRetentionPeriod,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Col xs={24} md={22} xl={20} xxl={18} style={{ margin: 'auto' }}>
|
||||||
|
{Element}
|
||||||
|
<ErrorTextContainer>
|
||||||
|
<TextToolTip
|
||||||
|
{...{
|
||||||
|
text: `More details on how to set retention period`,
|
||||||
|
url: 'https://signoz.io/docs/userguide/retention-period/',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{errorText && <ErrorText>{errorText}</ErrorText>}
|
||||||
|
</ErrorTextContainer>
|
||||||
|
|
||||||
|
<Row justify="space-around">{renderConfig}</Row>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
title={t('settings.retention_confirmation')}
|
||||||
|
focusTriggerAfterClose
|
||||||
|
forceRender
|
||||||
|
destroyOnClose
|
||||||
|
closable
|
||||||
|
onCancel={onModalToggleHandler}
|
||||||
|
onOk={onOkHandler}
|
||||||
|
centered
|
||||||
|
visible={modal}
|
||||||
|
confirmLoading={postApiLoading}
|
||||||
|
>
|
||||||
|
<Typography>{t('settings.retention_confirmation_description')}</Typography>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<ButtonContainer>
|
||||||
|
<Button onClick={onClickSaveHandler} disabled={isDisabled} type="primary">
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</ButtonContainer>
|
||||||
|
</Col>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GeneralSettingsProps {
|
||||||
|
ttlValuesPayload: GetRetentionPayload;
|
||||||
|
getAvailableDiskPayload: GetDisksPayload;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GeneralSettings;
|
@ -1,331 +1,52 @@
|
|||||||
/* eslint-disable sonarjs/cognitive-complexity */
|
import { Typography } from 'antd';
|
||||||
import { Button, Col, Modal, notification, Row, Typography } from 'antd';
|
|
||||||
import getDisks from 'api/disks/getDisks';
|
import getDisks from 'api/disks/getDisks';
|
||||||
import getRetentionPeriodApi from 'api/settings/getRetention';
|
import getRetentionPeriodApi from 'api/settings/getRetention';
|
||||||
import setRetentionApi from 'api/settings/setRetention';
|
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
import TextToolTip from 'components/TextToolTip';
|
import React from 'react';
|
||||||
import useFetch from 'hooks/useFetch';
|
|
||||||
import { find } from 'lodash-es';
|
|
||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { IDiskType } from 'types/api/disks/getDisks';
|
import { useQueries } from 'react-query';
|
||||||
import { PayloadProps } from 'types/api/settings/getRetention';
|
|
||||||
|
|
||||||
import Retention from './Retention';
|
import GeneralSettingsContainer from './GeneralSettings';
|
||||||
import { ButtonContainer, ErrorText, ErrorTextContainer } from './styles';
|
|
||||||
|
|
||||||
function GeneralSettings(): JSX.Element {
|
function GeneralSettings(): JSX.Element {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation('common');
|
||||||
const [notifications, Element] = notification.useNotification();
|
const [getRetentionPeriodApiResponse, getDisksResponse] = useQueries([
|
||||||
const [modal, setModal] = useState<boolean>(false);
|
|
||||||
const [postApiLoading, setPostApiLoading] = useState<boolean>(false);
|
|
||||||
|
|
||||||
const [availableDisks, setAvailableDisks] = useState<IDiskType[] | null>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getDisks().then((response) => setAvailableDisks(response.payload));
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const { payload, loading, error, errorMessage } = useFetch<
|
|
||||||
PayloadProps,
|
|
||||||
undefined
|
|
||||||
>(getRetentionPeriodApi, undefined);
|
|
||||||
|
|
||||||
const [currentTTLValues, setCurrentTTLValues] = useState(payload);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setCurrentTTLValues(payload);
|
|
||||||
}, [payload]);
|
|
||||||
|
|
||||||
const [metricsTotalRetentionPeriod, setMetricsTotalRetentionPeriod] = useState<
|
|
||||||
number | null
|
|
||||||
>(null);
|
|
||||||
const [metricsS3RetentionPeriod, setMetricsS3RetentionPeriod] = useState<
|
|
||||||
number | null
|
|
||||||
>(null);
|
|
||||||
const [tracesTotalRetentionPeriod, setTracesTotalRetentionPeriod] = useState<
|
|
||||||
number | null
|
|
||||||
>(null);
|
|
||||||
const [tracesS3RetentionPeriod, setTracesS3RetentionPeriod] = useState<
|
|
||||||
number | null
|
|
||||||
>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (currentTTLValues) {
|
|
||||||
setMetricsTotalRetentionPeriod(currentTTLValues.metrics_ttl_duration_hrs);
|
|
||||||
setMetricsS3RetentionPeriod(
|
|
||||||
currentTTLValues.metrics_move_ttl_duration_hrs
|
|
||||||
? currentTTLValues.metrics_move_ttl_duration_hrs
|
|
||||||
: null,
|
|
||||||
);
|
|
||||||
setTracesTotalRetentionPeriod(currentTTLValues.traces_ttl_duration_hrs);
|
|
||||||
setTracesS3RetentionPeriod(
|
|
||||||
currentTTLValues.traces_move_ttl_duration_hrs
|
|
||||||
? currentTTLValues.traces_move_ttl_duration_hrs
|
|
||||||
: null,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
console.log({ changed: currentTTLValues });
|
|
||||||
}, [currentTTLValues]);
|
|
||||||
|
|
||||||
const onModalToggleHandler = (): void => {
|
|
||||||
setModal((modal) => !modal);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onClickSaveHandler = useCallback(() => {
|
|
||||||
onModalToggleHandler();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const s3Enabled = useMemo(
|
|
||||||
() => !!find(availableDisks, (disks: IDiskType) => disks?.type === 's3'),
|
|
||||||
[availableDisks],
|
|
||||||
);
|
|
||||||
|
|
||||||
const renderConfig = [
|
|
||||||
{
|
{
|
||||||
name: 'Metrics',
|
queryFn: getRetentionPeriodApi,
|
||||||
retentionFields: [
|
queryKey: 'getRetentionPeriodApi',
|
||||||
{
|
|
||||||
name: t('settings.total_retention_period'),
|
|
||||||
value: metricsTotalRetentionPeriod,
|
|
||||||
setValue: setMetricsTotalRetentionPeriod,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: t('settings.move_to_s3'),
|
|
||||||
value: metricsS3RetentionPeriod,
|
|
||||||
setValue: setMetricsS3RetentionPeriod,
|
|
||||||
hide: !s3Enabled,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Traces',
|
queryFn: getDisks,
|
||||||
retentionFields: [
|
queryKey: 'getDisks',
|
||||||
{
|
|
||||||
name: t('settings.total_retention_period'),
|
|
||||||
value: tracesTotalRetentionPeriod,
|
|
||||||
setValue: setTracesTotalRetentionPeriod,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: t('settings.move_to_s3'),
|
|
||||||
value: tracesS3RetentionPeriod,
|
|
||||||
setValue: setTracesS3RetentionPeriod,
|
|
||||||
hide: !s3Enabled,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
].map((category): JSX.Element | null => {
|
|
||||||
if (
|
|
||||||
Array.isArray(category.retentionFields) &&
|
|
||||||
category.retentionFields.length > 0
|
|
||||||
) {
|
|
||||||
return (
|
|
||||||
<Col flex="40%" style={{ minWidth: 475 }} key={category.name}>
|
|
||||||
<Typography.Title level={3}>{category.name}</Typography.Title>
|
|
||||||
|
|
||||||
{category.retentionFields.map((retentionField) => (
|
|
||||||
<Retention
|
|
||||||
key={retentionField.name}
|
|
||||||
text={retentionField.name}
|
|
||||||
retentionValue={retentionField.value}
|
|
||||||
setRetentionValue={retentionField.setValue}
|
|
||||||
hide={!!retentionField.hide}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</Col>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
const onOkHandler = async (): Promise<void> => {
|
|
||||||
try {
|
|
||||||
setPostApiLoading(true);
|
|
||||||
const apiCalls = [];
|
|
||||||
|
|
||||||
if (
|
|
||||||
!(
|
|
||||||
currentTTLValues?.metrics_move_ttl_duration_hrs ===
|
|
||||||
metricsS3RetentionPeriod &&
|
|
||||||
currentTTLValues.metrics_ttl_duration_hrs === metricsTotalRetentionPeriod
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
apiCalls.push(() =>
|
|
||||||
setRetentionApi({
|
|
||||||
type: 'metrics',
|
|
||||||
totalDuration: `${metricsTotalRetentionPeriod || -1}h`,
|
|
||||||
coldStorage: s3Enabled ? 's3' : null,
|
|
||||||
toColdDuration: `${metricsS3RetentionPeriod || -1}h`,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
apiCalls.push(() => Promise.resolve(null));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
!(
|
|
||||||
currentTTLValues?.traces_move_ttl_duration_hrs ===
|
|
||||||
tracesS3RetentionPeriod &&
|
|
||||||
currentTTLValues.traces_ttl_duration_hrs === tracesTotalRetentionPeriod
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
apiCalls.push(() =>
|
|
||||||
setRetentionApi({
|
|
||||||
type: 'traces',
|
|
||||||
totalDuration: `${tracesTotalRetentionPeriod || -1}h`,
|
|
||||||
coldStorage: s3Enabled ? 's3' : null,
|
|
||||||
toColdDuration: `${tracesS3RetentionPeriod || -1}h`,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
apiCalls.push(() => Promise.resolve(null));
|
|
||||||
}
|
|
||||||
const apiCallSequence = ['metrics', 'traces'];
|
|
||||||
const apiResponses = await Promise.all(apiCalls.map((api) => api()));
|
|
||||||
|
|
||||||
apiResponses.forEach((apiResponse, idx) => {
|
|
||||||
const name = apiCallSequence[idx];
|
|
||||||
if (apiResponse) {
|
|
||||||
if (apiResponse.statusCode === 200) {
|
|
||||||
notifications.success({
|
|
||||||
message: 'Success!',
|
|
||||||
placement: 'topRight',
|
|
||||||
|
|
||||||
description: t('settings.retention_success_message', { name }),
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
notifications.error({
|
|
||||||
message: 'Error',
|
|
||||||
description: t('settings.retention_error_message', { name }),
|
|
||||||
placement: 'topRight',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
onModalToggleHandler();
|
|
||||||
setPostApiLoading(false);
|
|
||||||
} catch (error) {
|
|
||||||
notifications.error({
|
|
||||||
message: 'Error',
|
|
||||||
description: t('settings.retention_failed_message'),
|
|
||||||
placement: 'topRight',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Updates the currentTTL Values in order to avoid pushing the same values.
|
|
||||||
setCurrentTTLValues({
|
|
||||||
metrics_ttl_duration_hrs: metricsTotalRetentionPeriod || -1,
|
|
||||||
metrics_move_ttl_duration_hrs: metricsS3RetentionPeriod || -1,
|
|
||||||
traces_ttl_duration_hrs: tracesTotalRetentionPeriod || -1,
|
|
||||||
traces_move_ttl_duration_hrs: tracesS3RetentionPeriod || -1,
|
|
||||||
});
|
|
||||||
|
|
||||||
setModal(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const [isDisabled, errorText] = useMemo((): [boolean, string] => {
|
|
||||||
// Various methods to return dynamic error message text.
|
|
||||||
const messages = {
|
|
||||||
compareError: (name: string | number): string =>
|
|
||||||
t('settings.retention_comparison_error', { name }),
|
|
||||||
nullValueError: (name: string | number): string =>
|
|
||||||
t('settings.retention_null_value_error', { name }),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Defaults to button not disabled and empty error message text.
|
|
||||||
let isDisabled = false;
|
|
||||||
let errorText = '';
|
|
||||||
|
|
||||||
if (s3Enabled) {
|
|
||||||
if (
|
|
||||||
(metricsTotalRetentionPeriod || metricsS3RetentionPeriod) &&
|
|
||||||
Number(metricsTotalRetentionPeriod) <= Number(metricsS3RetentionPeriod)
|
|
||||||
) {
|
|
||||||
isDisabled = true;
|
|
||||||
errorText = messages.compareError('metrics');
|
|
||||||
} else if (
|
|
||||||
(tracesTotalRetentionPeriod || tracesS3RetentionPeriod) &&
|
|
||||||
Number(tracesTotalRetentionPeriod) <= Number(tracesS3RetentionPeriod)
|
|
||||||
) {
|
|
||||||
isDisabled = true;
|
|
||||||
errorText = messages.compareError('traces');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!metricsTotalRetentionPeriod || !tracesTotalRetentionPeriod) {
|
|
||||||
isDisabled = true;
|
|
||||||
if (!metricsTotalRetentionPeriod && !tracesTotalRetentionPeriod) {
|
|
||||||
errorText = messages.nullValueError('metrics and traces');
|
|
||||||
} else if (!metricsTotalRetentionPeriod) {
|
|
||||||
errorText = messages.nullValueError('metrics');
|
|
||||||
} else if (!tracesTotalRetentionPeriod) {
|
|
||||||
errorText = messages.nullValueError('traces');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
currentTTLValues?.metrics_ttl_duration_hrs === metricsTotalRetentionPeriod &&
|
|
||||||
currentTTLValues.metrics_move_ttl_duration_hrs ===
|
|
||||||
metricsS3RetentionPeriod &&
|
|
||||||
currentTTLValues.traces_ttl_duration_hrs === tracesTotalRetentionPeriod &&
|
|
||||||
currentTTLValues.traces_move_ttl_duration_hrs === tracesS3RetentionPeriod
|
|
||||||
) {
|
|
||||||
isDisabled = true;
|
|
||||||
}
|
|
||||||
return [isDisabled, errorText];
|
|
||||||
}, [
|
|
||||||
currentTTLValues,
|
|
||||||
metricsS3RetentionPeriod,
|
|
||||||
metricsTotalRetentionPeriod,
|
|
||||||
s3Enabled,
|
|
||||||
t,
|
|
||||||
tracesS3RetentionPeriod,
|
|
||||||
tracesTotalRetentionPeriod,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (error) {
|
if (getRetentionPeriodApiResponse.isError || getDisksResponse.isError) {
|
||||||
return <Typography>{errorMessage}</Typography>;
|
return (
|
||||||
|
<Typography>
|
||||||
|
{getRetentionPeriodApiResponse.data?.error ||
|
||||||
|
getDisksResponse.data?.error ||
|
||||||
|
t('something_went_wrong')}
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loading || currentTTLValues === undefined) {
|
if (
|
||||||
|
getRetentionPeriodApiResponse.isLoading ||
|
||||||
|
getDisksResponse.isLoading ||
|
||||||
|
!getDisksResponse.data?.payload ||
|
||||||
|
!getRetentionPeriodApiResponse.data?.payload
|
||||||
|
) {
|
||||||
return <Spinner tip="Loading.." height="70vh" />;
|
return <Spinner tip="Loading.." height="70vh" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Col xs={24} md={22} xl={20} xxl={18} style={{ margin: 'auto' }}>
|
<GeneralSettingsContainer
|
||||||
{Element}
|
{...{
|
||||||
<ErrorTextContainer>
|
getAvailableDiskPayload: getDisksResponse.data?.payload,
|
||||||
<TextToolTip
|
ttlValuesPayload: getRetentionPeriodApiResponse.data?.payload,
|
||||||
{...{
|
}}
|
||||||
text: `More details on how to set retention period`,
|
/>
|
||||||
url: 'https://signoz.io/docs/userguide/retention-period/',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{errorText && <ErrorText>{errorText}</ErrorText>}
|
|
||||||
</ErrorTextContainer>
|
|
||||||
|
|
||||||
<Row justify="space-around">{renderConfig}</Row>
|
|
||||||
|
|
||||||
<Modal
|
|
||||||
title={t('settings.retention_confirmation')}
|
|
||||||
focusTriggerAfterClose
|
|
||||||
forceRender
|
|
||||||
destroyOnClose
|
|
||||||
closable
|
|
||||||
onCancel={onModalToggleHandler}
|
|
||||||
onOk={onOkHandler}
|
|
||||||
centered
|
|
||||||
visible={modal}
|
|
||||||
confirmLoading={postApiLoading}
|
|
||||||
>
|
|
||||||
<Typography>{t('settings.retention_confirmation_description')}</Typography>
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
<ButtonContainer>
|
|
||||||
<Button onClick={onClickSaveHandler} disabled={isDisabled} type="primary">
|
|
||||||
Save
|
|
||||||
</Button>
|
|
||||||
</ButtonContainer>
|
|
||||||
</Col>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,29 +1,29 @@
|
|||||||
import getAll from 'api/alerts/getAll';
|
import getAll from 'api/alerts/getAll';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
import useFetch from 'hooks/useFetch';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { PayloadProps } from 'types/api/alerts/getAll';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useQuery } from 'react-query';
|
||||||
|
|
||||||
import ListAlert from './ListAlert';
|
import ListAlert from './ListAlert';
|
||||||
|
|
||||||
function ListAlertRules(): JSX.Element {
|
function ListAlertRules(): JSX.Element {
|
||||||
const { loading, payload, error, errorMessage } = useFetch<
|
const { t } = useTranslation('common');
|
||||||
PayloadProps,
|
const { data, isError, isLoading } = useQuery('allAlerts', {
|
||||||
undefined
|
queryFn: getAll,
|
||||||
>(getAll);
|
});
|
||||||
|
|
||||||
if (error) {
|
if (isError) {
|
||||||
return <div>{errorMessage}</div>;
|
return <div>{data?.error || t('something_went_wrong')}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loading || payload === undefined) {
|
if (isLoading || !data?.payload) {
|
||||||
return <Spinner height="75vh" tip="Loading Rules..." />;
|
return <Spinner height="75vh" tip="Loading Rules..." />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListAlert
|
<ListAlert
|
||||||
{...{
|
{...{
|
||||||
allAlertRules: payload,
|
allAlertRules: data.payload,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import { Select } from 'antd';
|
import { Select } from 'antd';
|
||||||
import getTagValue from 'api/trace/getTagValue';
|
import getTagValue from 'api/trace/getTagValue';
|
||||||
import useFetch from 'hooks/useFetch';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { useQuery } from 'react-query';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { PayloadProps, Props } from 'types/api/trace/getTagValue';
|
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
import { TraceReducer } from 'types/reducer/trace';
|
import { TraceReducer } from 'types/reducer/trace';
|
||||||
|
|
||||||
@ -22,11 +21,17 @@ function TagValue(props: TagValueProps): JSX.Element {
|
|||||||
(state) => state.globalTime,
|
(state) => state.globalTime,
|
||||||
);
|
);
|
||||||
|
|
||||||
const valueSuggestion = useFetch<PayloadProps, Props>(getTagValue, {
|
const { isLoading, data } = useQuery(
|
||||||
end: globalReducer.maxTime,
|
['tagKey', globalReducer.minTime, globalReducer.maxTime, tagKey],
|
||||||
start: globalReducer.minTime,
|
{
|
||||||
tagKey,
|
queryFn: () =>
|
||||||
});
|
getTagValue({
|
||||||
|
end: globalReducer.maxTime,
|
||||||
|
start: globalReducer.minTime,
|
||||||
|
tagKey,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SelectComponent
|
<SelectComponent
|
||||||
@ -44,10 +49,11 @@ function TagValue(props: TagValueProps): JSX.Element {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
loading={valueSuggestion.loading || false}
|
loading={isLoading || false}
|
||||||
>
|
>
|
||||||
{valueSuggestion.payload &&
|
{data &&
|
||||||
valueSuggestion.payload.map((suggestion) => (
|
data.payload &&
|
||||||
|
data.payload.map((suggestion) => (
|
||||||
<Select.Option key={suggestion.tagValues} value={suggestion.tagValues}>
|
<Select.Option key={suggestion.tagValues} value={suggestion.tagValues}>
|
||||||
{suggestion.tagValues}
|
{suggestion.tagValues}
|
||||||
</Select.Option>
|
</Select.Option>
|
||||||
|
@ -8,32 +8,33 @@ import {
|
|||||||
WebhookType,
|
WebhookType,
|
||||||
} from 'container/CreateAlertChannels/config';
|
} from 'container/CreateAlertChannels/config';
|
||||||
import EditAlertChannels from 'container/EditAlertChannels';
|
import EditAlertChannels from 'container/EditAlertChannels';
|
||||||
import useFetch from 'hooks/useFetch';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useQuery } from 'react-query';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { PayloadProps, Props } from 'types/api/channels/get';
|
|
||||||
|
|
||||||
function ChannelsEdit(): JSX.Element {
|
function ChannelsEdit(): JSX.Element {
|
||||||
const { id } = useParams<Params>();
|
const { id } = useParams<Params>();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const { errorMessage, payload, error, loading } = useFetch<
|
const { isLoading, isError, data } = useQuery(['getChannel', id], {
|
||||||
PayloadProps,
|
queryFn: () =>
|
||||||
Props
|
get({
|
||||||
>(get, {
|
id,
|
||||||
id,
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (error) {
|
if (isError) {
|
||||||
return <Typography>{errorMessage}</Typography>;
|
return <Typography>{data?.error || t('something_went_wrong')}</Typography>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loading || payload === undefined) {
|
if (isLoading || !data?.payload) {
|
||||||
return <Spinner tip="Loading Channels..." />;
|
return <Spinner tip="Loading Channels..." />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data } = payload;
|
const { data: ChannelData } = data.payload;
|
||||||
|
|
||||||
const value = JSON.parse(data);
|
const value = JSON.parse(ChannelData);
|
||||||
let type = '';
|
let type = '';
|
||||||
let channel: SlackChannel & WebhookChannel = { name: '' };
|
let channel: SlackChannel & WebhookChannel = { name: '' };
|
||||||
|
|
||||||
@ -57,7 +58,7 @@ function ChannelsEdit(): JSX.Element {
|
|||||||
}
|
}
|
||||||
type = WebhookType;
|
type = WebhookType;
|
||||||
}
|
}
|
||||||
console.log('channel:', channel);
|
|
||||||
return (
|
return (
|
||||||
<EditAlertChannels
|
<EditAlertChannels
|
||||||
{...{
|
{...{
|
||||||
|
@ -1,30 +1,31 @@
|
|||||||
import get from 'api/alerts/get';
|
import get from 'api/alerts/get';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
import EditRulesContainer from 'container/EditRules';
|
import EditRulesContainer from 'container/EditRules';
|
||||||
import useFetch from 'hooks/useFetch';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useQuery } from 'react-query';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { PayloadProps, Props } from 'types/api/alerts/get';
|
|
||||||
|
|
||||||
function EditRules(): JSX.Element {
|
function EditRules(): JSX.Element {
|
||||||
const { ruleId } = useParams<EditRulesParam>();
|
const { ruleId } = useParams<EditRulesParam>();
|
||||||
|
const { t } = useTranslation('common');
|
||||||
|
|
||||||
const { loading, error, payload, errorMessage } = useFetch<
|
const { isLoading, data, isError } = useQuery(['ruleId', ruleId], {
|
||||||
PayloadProps,
|
queryFn: () =>
|
||||||
Props
|
get({
|
||||||
>(get, {
|
id: parseInt(ruleId, 10),
|
||||||
id: parseInt(ruleId, 10),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (error) {
|
if (isError) {
|
||||||
return <div>{errorMessage}</div>;
|
return <div>{data?.error || t('something_went_wrong')}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loading || payload === undefined) {
|
if (isLoading || !data?.payload) {
|
||||||
return <Spinner tip="Loading Rules..." />;
|
return <Spinner tip="Loading Rules..." />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <EditRulesContainer ruleId={ruleId} initialData={payload.data} />;
|
return <EditRulesContainer ruleId={ruleId} initialData={data.payload.data} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EditRulesParam {
|
interface EditRulesParam {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user