mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-07-31 04:41:57 +08:00
Merge pull request #949 from pranshuchittora/pranshuchittora/feat/ttl-s3
feat(FE): TTL/s3 integration
This commit is contained in:
commit
6e446dc0ab
@ -1,3 +1,18 @@
|
||||
{
|
||||
"monitor_signup": "Monitor your applications. Find what is causing issues."
|
||||
"monitor_signup": "Monitor your applications. Find what is causing issues.",
|
||||
"routes": {
|
||||
"general": "General",
|
||||
"alert_channels": "Alert Channels"
|
||||
},
|
||||
"settings": {
|
||||
"total_retention_period": "Total Retention Period",
|
||||
"move_to_s3": "Move to S3\n(should be lower than total retention period)",
|
||||
"retention_success_message": "Congrats. The retention periods for {{name}} has been updated successfully.",
|
||||
"retention_error_message": "There was an issue in changing the retention period for {{name}}. Please try again or reach out to support@signoz.io",
|
||||
"retention_failed_message": "There was an issue in changing the retention period. Please try again or reach out to support@signoz.io",
|
||||
"retention_comparison_error": "Total retention period for {{name}} can’t be lower or equal to the period after which data is moved to s3.",
|
||||
"retention_null_value_error": "Retention Period for {{name}} is not set yet. Please set by choosing below",
|
||||
"retention_confirmation": "Are you sure you want to change the retention period?",
|
||||
"retention_confirmation_description": "This will change the amount of storage needed for saving metrics & traces."
|
||||
}
|
||||
}
|
||||
|
24
frontend/src/api/disks/getDisks.ts
Normal file
24
frontend/src/api/disks/getDisks.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { PayloadProps } from 'types/api/disks/getDisks';
|
||||
|
||||
const getDisks = async (): Promise<
|
||||
SuccessResponse<PayloadProps> | ErrorResponse
|
||||
> => {
|
||||
try {
|
||||
const response = await axios.get(`/disks`);
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: 'Success',
|
||||
payload: response.data,
|
||||
};
|
||||
} catch (error) {
|
||||
return ErrorResponseHandler(error as AxiosError);
|
||||
}
|
||||
};
|
||||
|
||||
export default getDisks;
|
@ -9,7 +9,11 @@ const setRetention = async (
|
||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.post<PayloadProps>(
|
||||
`/settings/ttl?duration=${props.duration}&type=${props.type}`,
|
||||
`/settings/ttl?duration=${props.totalDuration}&type=${props.type}${
|
||||
props.coldStorage
|
||||
? `&coldStorage=${props.coldStorage};toColdDuration=${props.toColdDuration}`
|
||||
: ''
|
||||
}`,
|
||||
);
|
||||
|
||||
return {
|
||||
|
@ -1,108 +1,118 @@
|
||||
import { DownOutlined } from '@ant-design/icons';
|
||||
import { Button, Menu } from 'antd';
|
||||
import React from 'react';
|
||||
import { Col, Row, Select } from 'antd';
|
||||
import { find } from 'lodash-es';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { SettingPeroid } from '.';
|
||||
import {
|
||||
Dropdown,
|
||||
Input,
|
||||
RetentionContainer,
|
||||
TextContainer,
|
||||
Typography,
|
||||
RetentionFieldInputContainer,
|
||||
RetentionFieldLabel,
|
||||
} from './styles';
|
||||
import {
|
||||
convertHoursValueToRelevantUnit,
|
||||
SettingPeriod,
|
||||
TimeUnits,
|
||||
} from './utils';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
function Retention({
|
||||
retentionValue,
|
||||
setRentionValue,
|
||||
selectedRetentionPeroid,
|
||||
setSelectedRetentionPeroid,
|
||||
setRetentionValue,
|
||||
text,
|
||||
}: RetentionProps): JSX.Element {
|
||||
const options: Option[] = [
|
||||
{
|
||||
key: 'hr',
|
||||
value: 'Hrs',
|
||||
},
|
||||
{
|
||||
key: 'day',
|
||||
value: 'Days',
|
||||
},
|
||||
{
|
||||
key: 'month',
|
||||
value: 'Months',
|
||||
},
|
||||
];
|
||||
|
||||
const onClickHandler = (
|
||||
e: { key: string },
|
||||
func: React.Dispatch<React.SetStateAction<SettingPeroid>>,
|
||||
): void => {
|
||||
const selected = e.key as SettingPeroid;
|
||||
func(selected);
|
||||
};
|
||||
|
||||
const menu = (
|
||||
<Menu onClick={(e): void => onClickHandler(e, setSelectedRetentionPeroid)}>
|
||||
{options.map((option) => (
|
||||
<Menu.Item key={option.key}>{option.value}</Menu.Item>
|
||||
))}
|
||||
</Menu>
|
||||
hide,
|
||||
}: RetentionProps): JSX.Element | null {
|
||||
const {
|
||||
value: initialValue,
|
||||
timeUnitValue: initialTimeUnitValue,
|
||||
} = convertHoursValueToRelevantUnit(Number(retentionValue));
|
||||
const [selectedTimeUnit, setSelectTimeUnit] = useState(initialTimeUnitValue);
|
||||
const [selectedValue, setSelectedValue] = useState<number | null>(
|
||||
initialValue,
|
||||
);
|
||||
const interacted = useRef(false);
|
||||
useEffect(() => {
|
||||
if (!interacted.current) setSelectedValue(initialValue);
|
||||
}, [initialValue]);
|
||||
|
||||
const currentSelectedOption = (option: SettingPeroid): string | undefined => {
|
||||
return options.find((e) => e.key === option)?.value;
|
||||
useEffect(() => {
|
||||
if (!interacted.current) setSelectTimeUnit(initialTimeUnitValue);
|
||||
}, [initialTimeUnitValue]);
|
||||
|
||||
const menuItems = TimeUnits.map((option) => (
|
||||
<Option key={option.value} value={option.value}>
|
||||
{option.key}
|
||||
</Option>
|
||||
));
|
||||
|
||||
const currentSelectedOption = (option: SettingPeriod): void => {
|
||||
const selectedValue = find(TimeUnits, (e) => e.value === option)?.value;
|
||||
if (selectedValue) setSelectTimeUnit(selectedValue);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const inverseMultiplier = find(
|
||||
TimeUnits,
|
||||
(timeUnit) => timeUnit.value === selectedTimeUnit,
|
||||
)?.multiplier;
|
||||
if (!selectedValue) setRetentionValue(null);
|
||||
if (selectedValue && inverseMultiplier) {
|
||||
setRetentionValue(selectedValue * (1 / inverseMultiplier));
|
||||
}
|
||||
}, [selectedTimeUnit, selectedValue, setRetentionValue]);
|
||||
|
||||
const onChangeHandler = (
|
||||
e: React.ChangeEvent<HTMLInputElement>,
|
||||
func: React.Dispatch<React.SetStateAction<string>>,
|
||||
func: React.Dispatch<React.SetStateAction<number | null>>,
|
||||
): void => {
|
||||
interacted.current = true;
|
||||
const { value } = e.target;
|
||||
const integerValue = parseInt(value, 10);
|
||||
|
||||
if (value.length > 0 && integerValue.toString() === value) {
|
||||
const parsedValue = Math.abs(integerValue).toString();
|
||||
const parsedValue = Math.abs(integerValue);
|
||||
func(parsedValue);
|
||||
}
|
||||
|
||||
if (value.length === 0) {
|
||||
func('');
|
||||
func(null);
|
||||
}
|
||||
};
|
||||
|
||||
if (hide) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<RetentionContainer>
|
||||
<TextContainer>
|
||||
<Typography>{text}</Typography>
|
||||
</TextContainer>
|
||||
|
||||
<Input
|
||||
value={retentionValue}
|
||||
onChange={(e): void => onChangeHandler(e, setRentionValue)}
|
||||
/>
|
||||
|
||||
<Dropdown overlay={menu}>
|
||||
<Button>
|
||||
{currentSelectedOption(selectedRetentionPeroid)} <DownOutlined />
|
||||
</Button>
|
||||
</Dropdown>
|
||||
<Row justify="space-between">
|
||||
<Col flex={1} style={{ display: 'flex' }}>
|
||||
<RetentionFieldLabel>{text}</RetentionFieldLabel>
|
||||
</Col>
|
||||
<Col flex="150px">
|
||||
<RetentionFieldInputContainer>
|
||||
<Input
|
||||
value={selectedValue && selectedValue >= 0 ? selectedValue : ''}
|
||||
onChange={(e): void => onChangeHandler(e, setSelectedValue)}
|
||||
style={{ width: 75 }}
|
||||
/>
|
||||
<Select
|
||||
value={selectedTimeUnit}
|
||||
onChange={currentSelectedOption}
|
||||
style={{ width: 100 }}
|
||||
>
|
||||
{menuItems}
|
||||
</Select>
|
||||
</RetentionFieldInputContainer>
|
||||
</Col>
|
||||
</Row>
|
||||
</RetentionContainer>
|
||||
);
|
||||
}
|
||||
|
||||
interface Option {
|
||||
key: SettingPeroid;
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface RetentionProps {
|
||||
retentionValue: string;
|
||||
retentionValue: number | null;
|
||||
text: string;
|
||||
setRentionValue: React.Dispatch<React.SetStateAction<string>>;
|
||||
selectedRetentionPeroid: SettingPeroid;
|
||||
setSelectedRetentionPeroid: React.Dispatch<
|
||||
React.SetStateAction<SettingPeroid>
|
||||
>;
|
||||
setRetentionValue: React.Dispatch<React.SetStateAction<number | null>>;
|
||||
hide: boolean;
|
||||
}
|
||||
|
||||
export default Retention;
|
||||
|
@ -1,43 +1,69 @@
|
||||
import { Button, Modal, notification, Typography } from 'antd';
|
||||
import getRetentionperoidApi from 'api/settings/getRetention';
|
||||
import { Button, Col, Modal, notification, Row, Typography } from 'antd';
|
||||
import getDisks from 'api/disks/getDisks';
|
||||
import getRetentionPeriodApi from 'api/settings/getRetention';
|
||||
import setRetentionApi from 'api/settings/setRetention';
|
||||
import Spinner from 'components/Spinner';
|
||||
import TextToolTip from 'components/TextToolTip';
|
||||
import useFetch from 'hooks/useFetch';
|
||||
import convertIntoHr from 'lib/convertIntoHr';
|
||||
import getSettingsPeroid from 'lib/getSettingsPeroid';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { find } from 'lodash-es';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { IDiskType } from 'types/api/disks/getDisks';
|
||||
import { PayloadProps } from 'types/api/settings/getRetention';
|
||||
|
||||
import Retention from './Retention';
|
||||
import {
|
||||
ButtonContainer,
|
||||
Container,
|
||||
ErrorText,
|
||||
ErrorTextContainer,
|
||||
ToolTipContainer,
|
||||
} from './styles';
|
||||
|
||||
function GeneralSettings(): JSX.Element {
|
||||
const [
|
||||
selectedMetricsPeroid,
|
||||
setSelectedMetricsPeroid,
|
||||
] = useState<SettingPeroid>('month');
|
||||
const { t } = useTranslation();
|
||||
const [notifications, Element] = notification.useNotification();
|
||||
|
||||
const [retentionPeroidMetrics, setRetentionPeroidMetrics] = useState<string>(
|
||||
'',
|
||||
);
|
||||
const [modal, setModal] = useState<boolean>(false);
|
||||
const [postApiLoading, setPostApiLoading] = useState<boolean>(false);
|
||||
|
||||
const [selectedTracePeroid, setSelectedTracePeroid] = useState<SettingPeroid>(
|
||||
'hr',
|
||||
);
|
||||
const [availableDisks, setAvailableDisks] = useState<IDiskType[] | null>(null);
|
||||
|
||||
const [retentionPeroidTrace, setRetentionPeroidTrace] = useState<string>('');
|
||||
const [isDefaultMetrics, setIsDefaultMetrics] = useState<boolean>(false);
|
||||
const [isDefaultTrace, setIsDefaultTrace] = useState<boolean>(false);
|
||||
useEffect(() => {
|
||||
getDisks().then((response) => setAvailableDisks(response.payload));
|
||||
}, []);
|
||||
|
||||
const { payload: currentTTLValues, loading, error, errorMessage } = useFetch<
|
||||
PayloadProps,
|
||||
undefined
|
||||
>(getRetentionPeriodApi, undefined);
|
||||
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,
|
||||
);
|
||||
}
|
||||
}, [currentTTLValues]);
|
||||
|
||||
const onModalToggleHandler = (): void => {
|
||||
setModal((modal) => !modal);
|
||||
@ -47,142 +73,182 @@ function GeneralSettings(): JSX.Element {
|
||||
onModalToggleHandler();
|
||||
}, []);
|
||||
|
||||
const { payload, loading, error, errorMessage } = useFetch<
|
||||
PayloadProps,
|
||||
undefined
|
||||
>(getRetentionperoidApi, undefined);
|
||||
const s3Enabled = useMemo(
|
||||
() => !!find(availableDisks, (disks: IDiskType) => disks?.type === 's3'),
|
||||
[availableDisks],
|
||||
);
|
||||
|
||||
const checkMetricTraceDefault = (trace: number, metric: number): void => {
|
||||
if (metric === -1) {
|
||||
setIsDefaultMetrics(true);
|
||||
} else {
|
||||
setIsDefaultMetrics(false);
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
if (trace === -1) {
|
||||
setIsDefaultTrace(true);
|
||||
} else {
|
||||
setIsDefaultTrace(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading && payload !== undefined) {
|
||||
const {
|
||||
metrics_ttl_duration_hrs: metricTllDuration,
|
||||
traces_ttl_duration_hrs: traceTllDuration,
|
||||
} = payload;
|
||||
|
||||
checkMetricTraceDefault(traceTllDuration, metricTllDuration);
|
||||
|
||||
const traceValue = getSettingsPeroid(traceTllDuration);
|
||||
const metricsValue = getSettingsPeroid(metricTllDuration);
|
||||
|
||||
setRetentionPeroidTrace(traceValue.value.toString());
|
||||
setSelectedTracePeroid(traceValue.peroid);
|
||||
|
||||
setRetentionPeroidMetrics(metricsValue.value.toString());
|
||||
setSelectedMetricsPeroid(metricsValue.peroid);
|
||||
}
|
||||
}, [setSelectedMetricsPeroid, loading, payload]);
|
||||
return null;
|
||||
});
|
||||
|
||||
const onOkHandler = async (): Promise<void> => {
|
||||
try {
|
||||
setPostApiLoading(true);
|
||||
const retentionTraceValue =
|
||||
retentionPeroidTrace === '0' && (payload?.traces_ttl_duration_hrs || 0) < 0
|
||||
? payload?.traces_ttl_duration_hrs || 0
|
||||
: parseInt(retentionPeroidTrace, 10);
|
||||
|
||||
const retentionMetricsValue =
|
||||
retentionPeroidMetrics === '0' &&
|
||||
(payload?.metrics_ttl_duration_hrs || 0) < 0
|
||||
? payload?.metrics_ttl_duration_hrs || 0
|
||||
: parseInt(retentionPeroidMetrics, 10);
|
||||
|
||||
const [tracesResponse, metricsResponse] = await Promise.all([
|
||||
const [metricsTTLApiResponse, tracesTTLApiResponse] = await Promise.all([
|
||||
setRetentionApi({
|
||||
duration: `${convertIntoHr(retentionTraceValue, selectedTracePeroid)}h`,
|
||||
type: 'traces',
|
||||
type: 'metrics',
|
||||
totalDuration: `${metricsTotalRetentionPeriod || -1}h`,
|
||||
coldStorage: s3Enabled ? 's3' : null,
|
||||
toColdDuration: `${metricsS3RetentionPeriod || -1}h`,
|
||||
}),
|
||||
setRetentionApi({
|
||||
duration: `${convertIntoHr(
|
||||
retentionMetricsValue,
|
||||
selectedMetricsPeroid,
|
||||
)}h`,
|
||||
type: 'metrics',
|
||||
type: 'traces',
|
||||
totalDuration: `${tracesTotalRetentionPeriod || -1}h`,
|
||||
coldStorage: s3Enabled ? 's3' : null,
|
||||
toColdDuration: `${tracesS3RetentionPeriod || -1}h`,
|
||||
}),
|
||||
]);
|
||||
[
|
||||
{
|
||||
apiResponse: metricsTTLApiResponse,
|
||||
name: 'metrics',
|
||||
},
|
||||
{
|
||||
apiResponse: tracesTTLApiResponse,
|
||||
name: 'traces',
|
||||
},
|
||||
].forEach(({ apiResponse, name }) => {
|
||||
if (apiResponse.statusCode === 200) {
|
||||
notifications.success({
|
||||
message: 'Success!',
|
||||
placement: 'topRight',
|
||||
|
||||
if (
|
||||
tracesResponse.statusCode === 200 &&
|
||||
metricsResponse.statusCode === 200
|
||||
) {
|
||||
notifications.success({
|
||||
message: 'Success!',
|
||||
placement: 'topRight',
|
||||
description: 'Congrats. The retention periods were updated correctly.',
|
||||
});
|
||||
|
||||
checkMetricTraceDefault(retentionTraceValue, retentionMetricsValue);
|
||||
|
||||
onModalToggleHandler();
|
||||
} else {
|
||||
notifications.error({
|
||||
message: 'Error',
|
||||
description:
|
||||
'There was an issue in changing the retention period. Please try again or reach out to support@signoz.io',
|
||||
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:
|
||||
'There was an issue in changing the retention period. Please try again or reach out to support@signoz.io',
|
||||
description: t('settings.retention_failed_message'),
|
||||
placement: 'topRight',
|
||||
});
|
||||
}
|
||||
setModal(false);
|
||||
};
|
||||
|
||||
const [isDisabled, errorText] = useMemo(() => {
|
||||
// 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');
|
||||
}
|
||||
}
|
||||
return [isDisabled, errorText];
|
||||
}, [
|
||||
metricsS3RetentionPeriod,
|
||||
metricsTotalRetentionPeriod,
|
||||
s3Enabled,
|
||||
t,
|
||||
tracesS3RetentionPeriod,
|
||||
tracesTotalRetentionPeriod,
|
||||
]);
|
||||
|
||||
if (error) {
|
||||
return <Typography>{errorMessage}</Typography>;
|
||||
}
|
||||
|
||||
if (loading || payload === undefined) {
|
||||
if (loading || currentTTLValues === undefined) {
|
||||
return <Spinner tip="Loading.." height="70vh" />;
|
||||
}
|
||||
|
||||
const getErrorText = (): string => {
|
||||
const getValue = (value: string): string =>
|
||||
`Retention Peroid for ${value} is not set yet. Please set by choosing below`;
|
||||
|
||||
if (!isDefaultMetrics && !isDefaultTrace) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (isDefaultMetrics && !isDefaultTrace) {
|
||||
return `${getValue('Metrics')}`;
|
||||
}
|
||||
|
||||
if (!isDefaultMetrics && isDefaultTrace) {
|
||||
return `${getValue('Trace')}`;
|
||||
}
|
||||
|
||||
return `${getValue('Trace , Metrics')}`;
|
||||
};
|
||||
|
||||
const isDisabledHandler = (): boolean => {
|
||||
return !!(retentionPeroidTrace === '' || retentionPeroidMetrics === '');
|
||||
};
|
||||
|
||||
const errorText = getErrorText();
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Col xs={24} md={22} xl={20} xxl={18} style={{ margin: 'auto' }}>
|
||||
{Element}
|
||||
|
||||
{errorText ? (
|
||||
<ErrorTextContainer>
|
||||
<ErrorText>{errorText}</ErrorText>
|
||||
@ -204,25 +270,10 @@ function GeneralSettings(): JSX.Element {
|
||||
/>
|
||||
</ToolTipContainer>
|
||||
)}
|
||||
|
||||
<Retention
|
||||
text="Retention Period for Metrics"
|
||||
selectedRetentionPeroid={selectedMetricsPeroid}
|
||||
setRentionValue={setRetentionPeroidMetrics}
|
||||
retentionValue={retentionPeroidMetrics}
|
||||
setSelectedRetentionPeroid={setSelectedMetricsPeroid}
|
||||
/>
|
||||
|
||||
<Retention
|
||||
text="Retention Period for Traces"
|
||||
selectedRetentionPeroid={selectedTracePeroid}
|
||||
setRentionValue={setRetentionPeroidTrace}
|
||||
retentionValue={retentionPeroidTrace}
|
||||
setSelectedRetentionPeroid={setSelectedTracePeroid}
|
||||
/>
|
||||
<Row justify="space-around">{renderConfig}</Row>
|
||||
|
||||
<Modal
|
||||
title="Are you sure you want to change the retention period?"
|
||||
title={t('settings.retention_confirmation')}
|
||||
focusTriggerAfterClose
|
||||
forceRender
|
||||
destroyOnClose
|
||||
@ -233,24 +284,18 @@ function GeneralSettings(): JSX.Element {
|
||||
visible={modal}
|
||||
confirmLoading={postApiLoading}
|
||||
>
|
||||
<Typography>
|
||||
This will change the amount of storage needed for saving metrics & traces.
|
||||
</Typography>
|
||||
<Typography>{t('settings.retention_confirmation_description')}</Typography>
|
||||
</Modal>
|
||||
|
||||
<ButtonContainer>
|
||||
<Button
|
||||
onClick={onClickSaveHandler}
|
||||
disabled={isDisabledHandler()}
|
||||
type="primary"
|
||||
>
|
||||
<Button onClick={onClickSaveHandler} disabled={isDisabled} type="primary">
|
||||
Save
|
||||
</Button>
|
||||
</ButtonContainer>
|
||||
</Container>
|
||||
</Col>
|
||||
);
|
||||
}
|
||||
|
||||
export type SettingPeroid = 'hr' | 'day' | 'month';
|
||||
export type SettingPeriod = 'hr' | 'day' | 'month';
|
||||
|
||||
export default GeneralSettings;
|
||||
|
@ -1,16 +1,13 @@
|
||||
import {
|
||||
Col,
|
||||
Dropdown as DropDownComponent,
|
||||
Input as InputComponent,
|
||||
Typography as TypographyComponent,
|
||||
} from 'antd';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const RetentionContainer = styled.div`
|
||||
width: 50%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
export const RetentionContainer = styled(Col)`
|
||||
margin: 0.75rem 0;
|
||||
`;
|
||||
|
||||
export const Input = styled(InputComponent)`
|
||||
@ -37,13 +34,6 @@ export const ButtonContainer = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
export const Container = styled.div`
|
||||
&&& {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
`;
|
||||
|
||||
export const Dropdown = styled(DropDownComponent)`
|
||||
&&& {
|
||||
display: flex;
|
||||
@ -90,3 +80,12 @@ export const ErrorText = styled(TypographyComponent)`
|
||||
font-style: italic;
|
||||
}
|
||||
`;
|
||||
|
||||
export const RetentionFieldLabel = styled(TypographyComponent)`
|
||||
vertical-align: middle;
|
||||
white-space: pre-wrap;
|
||||
`;
|
||||
|
||||
export const RetentionFieldInputContainer = styled.div`
|
||||
display: inline-flex;
|
||||
`;
|
||||
|
42
frontend/src/container/GeneralSettings/utils.ts
Normal file
42
frontend/src/container/GeneralSettings/utils.ts
Normal file
@ -0,0 +1,42 @@
|
||||
export type SettingPeriod = 'hr' | 'day' | 'month';
|
||||
|
||||
export interface ITimeUnit {
|
||||
value: SettingPeriod;
|
||||
key: string;
|
||||
multiplier: number;
|
||||
}
|
||||
export const TimeUnits: ITimeUnit[] = [
|
||||
{
|
||||
value: 'hr',
|
||||
key: 'Hours',
|
||||
multiplier: 1,
|
||||
},
|
||||
{
|
||||
value: 'day',
|
||||
key: 'Days',
|
||||
multiplier: 1 / 24,
|
||||
},
|
||||
{
|
||||
value: 'month',
|
||||
key: 'Months',
|
||||
multiplier: 1 / (24 * 30),
|
||||
},
|
||||
];
|
||||
|
||||
export const convertHoursValueToRelevantUnit = (
|
||||
value: number,
|
||||
): { value: number; timeUnitValue: SettingPeriod } => {
|
||||
if (value)
|
||||
for (let idx = TimeUnits.length - 1; idx >= 0; idx -= 1) {
|
||||
const timeUnit = TimeUnits[idx];
|
||||
const convertedValue = timeUnit.multiplier * value;
|
||||
|
||||
if (
|
||||
convertedValue >= 1 &&
|
||||
convertedValue === parseInt(`${convertedValue}`, 10)
|
||||
) {
|
||||
return { value: convertedValue, timeUnitValue: timeUnit.value };
|
||||
}
|
||||
}
|
||||
return { value, timeUnitValue: TimeUnits[0].value };
|
||||
};
|
@ -1,6 +1,6 @@
|
||||
import { SettingPeroid } from 'container/GeneralSettings';
|
||||
import { SettingPeriod } from 'container/GeneralSettings';
|
||||
|
||||
const converIntoHr = (value: number, peroid: SettingPeroid): number => {
|
||||
const converIntoHr = (value: number, peroid: SettingPeriod): number => {
|
||||
if (peroid === 'day') {
|
||||
return value * 24;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { SettingPeroid } from 'container/GeneralSettings';
|
||||
import { SettingPeriod } from 'container/GeneralSettings';
|
||||
|
||||
const getSettingsPeroid = (hr: number): PayloadProps => {
|
||||
if (hr <= 0) {
|
||||
@ -30,7 +30,7 @@ const getSettingsPeroid = (hr: number): PayloadProps => {
|
||||
|
||||
interface PayloadProps {
|
||||
value: number;
|
||||
peroid: SettingPeroid;
|
||||
peroid: SettingPeriod;
|
||||
}
|
||||
|
||||
export default getSettingsPeroid;
|
||||
|
@ -5,29 +5,32 @@ import CreateAlertChannels from 'container/CreateAlertChannels';
|
||||
import GeneralSettings from 'container/GeneralSettings';
|
||||
import history from 'lib/history';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
function SettingsPage(): JSX.Element {
|
||||
const pathName = history.location.pathname;
|
||||
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<RouteTab
|
||||
{...{
|
||||
routes: [
|
||||
{
|
||||
Component: GeneralSettings,
|
||||
name: 'General Settings',
|
||||
name: t('routes.general'),
|
||||
route: ROUTES.SETTINGS,
|
||||
},
|
||||
{
|
||||
Component: (): JSX.Element => {
|
||||
return <CreateAlertChannels preType="slack" />;
|
||||
},
|
||||
name: 'Alert Channels',
|
||||
name: t('routes.alert_channels'),
|
||||
route: ROUTES.ALL_CHANNELS,
|
||||
},
|
||||
],
|
||||
activeKey:
|
||||
pathName === ROUTES.SETTINGS ? 'General Settings' : 'Alert Channels',
|
||||
pathName === ROUTES.SETTINGS
|
||||
? t('routes.general')
|
||||
: t('routes.alert_channels'),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
@ -4,27 +4,30 @@ import AlertChannels from 'container/AllAlertChannels';
|
||||
import GeneralSettings from 'container/GeneralSettings';
|
||||
import history from 'lib/history';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
function AllAlertChannels(): JSX.Element {
|
||||
const pathName = history.location.pathname;
|
||||
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<RouteTab
|
||||
{...{
|
||||
routes: [
|
||||
{
|
||||
Component: GeneralSettings,
|
||||
name: 'General Settings',
|
||||
name: t('routes.general'),
|
||||
route: ROUTES.SETTINGS,
|
||||
},
|
||||
{
|
||||
Component: AlertChannels,
|
||||
name: 'Alert Channels',
|
||||
name: t('routes.alert_channels'),
|
||||
route: ROUTES.ALL_CHANNELS,
|
||||
},
|
||||
],
|
||||
activeKey:
|
||||
pathName === ROUTES.SETTINGS ? 'General Settings' : 'Alert Channels',
|
||||
pathName === ROUTES.SETTINGS
|
||||
? t('routes.general')
|
||||
: t('routes.alert_channels'),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
@ -14,7 +14,7 @@ function SettingsPage(): JSX.Element {
|
||||
routes: [
|
||||
{
|
||||
Component: GeneralSettings,
|
||||
name: 'General Settings',
|
||||
name: 'General',
|
||||
route: ROUTES.SETTINGS,
|
||||
},
|
||||
{
|
||||
@ -23,8 +23,7 @@ function SettingsPage(): JSX.Element {
|
||||
route: ROUTES.ALL_CHANNELS,
|
||||
},
|
||||
],
|
||||
activeKey:
|
||||
pathName === ROUTES.ALL_CHANNELS ? 'Alert Channels' : 'General Settings',
|
||||
activeKey: pathName === ROUTES.ALL_CHANNELS ? 'Alert Channels' : 'General',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
5
frontend/src/types/api/disks/getDisks.ts
Normal file
5
frontend/src/types/api/disks/getDisks.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export type PayloadProps = IDiskType[];
|
||||
export interface IDiskType {
|
||||
name: string;
|
||||
type: string;
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
export interface PayloadProps {
|
||||
metrics_ttl_duration_hrs: number;
|
||||
metrics_move_ttl_duration_hrs?: number;
|
||||
traces_ttl_duration_hrs: number;
|
||||
traces_move_ttl_duration_hrs?: number;
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
export interface Props {
|
||||
type: 'metrics' | 'traces';
|
||||
duration: string;
|
||||
totalDuration: string;
|
||||
coldStorage?: 's3' | null;
|
||||
toColdDuration?: string;
|
||||
}
|
||||
|
||||
export interface PayloadProps {
|
||||
|
Loading…
x
Reference in New Issue
Block a user