mirror of
https://github.com/SigNoz/signoz.git
synced 2025-12-18 07:56:56 +00:00
chore: add count based limits for metrics (#6738)
This commit is contained in:
parent
bab8c8274c
commit
92299e1b08
@ -58,7 +58,11 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { useMutation } from 'react-query';
|
import { useMutation } from 'react-query';
|
||||||
import { useCopyToClipboard } from 'react-use';
|
import { useCopyToClipboard } from 'react-use';
|
||||||
import { ErrorResponse } from 'types/api';
|
import { ErrorResponse } from 'types/api';
|
||||||
import { LimitProps } from 'types/api/ingestionKeys/limits/types';
|
import {
|
||||||
|
AddLimitProps,
|
||||||
|
LimitProps,
|
||||||
|
UpdateLimitProps,
|
||||||
|
} from 'types/api/ingestionKeys/limits/types';
|
||||||
import {
|
import {
|
||||||
IngestionKeyProps,
|
IngestionKeyProps,
|
||||||
PaginationProps,
|
PaginationProps,
|
||||||
@ -69,6 +73,18 @@ const { Option } = Select;
|
|||||||
|
|
||||||
const BYTES = 1073741824;
|
const BYTES = 1073741824;
|
||||||
|
|
||||||
|
const COUNT_MULTIPLIER = {
|
||||||
|
thousand: 1000,
|
||||||
|
million: 1000000,
|
||||||
|
billion: 1000000000,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SIGNALS_CONFIG = [
|
||||||
|
{ name: 'logs', usesSize: true, usesCount: false },
|
||||||
|
{ name: 'traces', usesSize: true, usesCount: false },
|
||||||
|
{ name: 'metrics', usesSize: false, usesCount: true },
|
||||||
|
];
|
||||||
|
|
||||||
// Using any type here because antd's DatePicker expects its own internal Dayjs type
|
// Using any type here because antd's DatePicker expects its own internal Dayjs type
|
||||||
// which conflicts with our project's Dayjs type that has additional plugins (tz, utc etc).
|
// which conflicts with our project's Dayjs type that has additional plugins (tz, utc etc).
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
|
||||||
@ -76,8 +92,6 @@ export const disabledDate = (current: any): boolean =>
|
|||||||
// Disable all dates before today
|
// Disable all dates before today
|
||||||
current && current < dayjs().endOf('day');
|
current && current < dayjs().endOf('day');
|
||||||
|
|
||||||
const SIGNALS = ['logs', 'traces', 'metrics'];
|
|
||||||
|
|
||||||
export const showErrorNotification = (
|
export const showErrorNotification = (
|
||||||
notifications: NotificationInstance,
|
notifications: NotificationInstance,
|
||||||
err: Error,
|
err: Error,
|
||||||
@ -101,6 +115,31 @@ export const API_KEY_EXPIRY_OPTIONS: ExpiryOption[] = [
|
|||||||
{ value: '0', label: 'No Expiry' },
|
{ value: '0', label: 'No Expiry' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const countToUnit = (count: number): { value: number; unit: string } => {
|
||||||
|
if (
|
||||||
|
count >= COUNT_MULTIPLIER.billion ||
|
||||||
|
count / COUNT_MULTIPLIER.million >= 1000
|
||||||
|
) {
|
||||||
|
return { value: count / COUNT_MULTIPLIER.billion, unit: 'billion' };
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
count >= COUNT_MULTIPLIER.million ||
|
||||||
|
count / COUNT_MULTIPLIER.thousand >= 1000
|
||||||
|
) {
|
||||||
|
return { value: count / COUNT_MULTIPLIER.million, unit: 'million' };
|
||||||
|
}
|
||||||
|
if (count >= COUNT_MULTIPLIER.thousand) {
|
||||||
|
return { value: count / COUNT_MULTIPLIER.thousand, unit: 'thousand' };
|
||||||
|
}
|
||||||
|
// Default to million for small numbers
|
||||||
|
return { value: count / COUNT_MULTIPLIER.million, unit: 'million' };
|
||||||
|
};
|
||||||
|
|
||||||
|
const countFromUnit = (value: number, unit: string): number =>
|
||||||
|
value *
|
||||||
|
(COUNT_MULTIPLIER[unit as keyof typeof COUNT_MULTIPLIER] ||
|
||||||
|
COUNT_MULTIPLIER.million);
|
||||||
|
|
||||||
function MultiIngestionSettings(): JSX.Element {
|
function MultiIngestionSettings(): JSX.Element {
|
||||||
const { user } = useAppContext();
|
const { user } = useAppContext();
|
||||||
const { notifications } = useNotifications();
|
const { notifications } = useNotifications();
|
||||||
@ -181,7 +220,6 @@ function MultiIngestionSettings(): JSX.Element {
|
|||||||
|
|
||||||
const showEditModal = (apiKey: IngestionKeyProps): void => {
|
const showEditModal = (apiKey: IngestionKeyProps): void => {
|
||||||
setActiveAPIKey(apiKey);
|
setActiveAPIKey(apiKey);
|
||||||
|
|
||||||
handleFormReset();
|
handleFormReset();
|
||||||
setUpdatedTags(apiKey.tags || []);
|
setUpdatedTags(apiKey.tags || []);
|
||||||
|
|
||||||
@ -424,44 +462,90 @@ function MultiIngestionSettings(): JSX.Element {
|
|||||||
addEditLimitForm.resetFields();
|
addEditLimitForm.resetFields();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* eslint-disable sonarjs/cognitive-complexity */
|
||||||
const handleAddLimit = (
|
const handleAddLimit = (
|
||||||
APIKey: IngestionKeyProps,
|
APIKey: IngestionKeyProps,
|
||||||
signalName: string,
|
signalName: string,
|
||||||
): void => {
|
): void => {
|
||||||
const { dailyLimit, secondsLimit } = addEditLimitForm.getFieldsValue();
|
const {
|
||||||
|
dailyLimit,
|
||||||
|
secondsLimit,
|
||||||
|
dailyCount,
|
||||||
|
dailyCountUnit,
|
||||||
|
secondsCount,
|
||||||
|
secondsCountUnit,
|
||||||
|
} = addEditLimitForm.getFieldsValue();
|
||||||
|
|
||||||
const payload = {
|
const payload: AddLimitProps = {
|
||||||
keyID: APIKey.id,
|
keyID: APIKey.id,
|
||||||
signal: signalName,
|
signal: signalName,
|
||||||
config: {},
|
config: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!isUndefined(dailyLimit)) {
|
const signalCfg = SIGNALS_CONFIG.find((cfg) => cfg.name === signalName);
|
||||||
payload.config = {
|
if (!signalCfg) return;
|
||||||
day: {
|
|
||||||
|
// Only set size if usesSize is true
|
||||||
|
if (signalCfg.usesSize) {
|
||||||
|
if (!isUndefined(dailyLimit)) {
|
||||||
|
payload.config.day = {
|
||||||
|
...payload.config.day,
|
||||||
size: gbToBytes(dailyLimit),
|
size: gbToBytes(dailyLimit),
|
||||||
},
|
};
|
||||||
};
|
}
|
||||||
}
|
if (!isUndefined(secondsLimit)) {
|
||||||
|
payload.config.second = {
|
||||||
if (!isUndefined(secondsLimit)) {
|
...payload.config.second,
|
||||||
payload.config = {
|
|
||||||
...payload.config,
|
|
||||||
second: {
|
|
||||||
size: gbToBytes(secondsLimit),
|
size: gbToBytes(secondsLimit),
|
||||||
},
|
};
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isUndefined(dailyLimit) && isUndefined(secondsLimit)) {
|
// Only set count if usesCount is true
|
||||||
// No need to save as no limit is provided, close the edit view and reset active signal and api key
|
if (signalCfg.usesCount) {
|
||||||
|
if (!isUndefined(dailyCount)) {
|
||||||
|
payload.config.day = {
|
||||||
|
...payload.config.day,
|
||||||
|
count: countFromUnit(dailyCount, dailyCountUnit || 'million'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!isUndefined(secondsCount)) {
|
||||||
|
payload.config.second = {
|
||||||
|
...payload.config.second,
|
||||||
|
count: countFromUnit(secondsCount, secondsCountUnit || 'million'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If neither size nor count was given, skip
|
||||||
|
const noSizeProvided =
|
||||||
|
isUndefined(dailyLimit) && isUndefined(secondsLimit) && signalCfg.usesSize;
|
||||||
|
const noCountProvided =
|
||||||
|
isUndefined(dailyCount) && isUndefined(secondsCount) && signalCfg.usesCount;
|
||||||
|
|
||||||
|
if (
|
||||||
|
signalCfg.usesSize &&
|
||||||
|
signalCfg.usesCount &&
|
||||||
|
noSizeProvided &&
|
||||||
|
noCountProvided
|
||||||
|
) {
|
||||||
|
// Both size and count are effectively empty
|
||||||
setActiveSignal(null);
|
setActiveSignal(null);
|
||||||
setActiveAPIKey(null);
|
setActiveAPIKey(null);
|
||||||
setIsEditAddLimitOpen(false);
|
setIsEditAddLimitOpen(false);
|
||||||
setUpdatedTags([]);
|
setUpdatedTags([]);
|
||||||
hideAddViewModal();
|
hideAddViewModal();
|
||||||
setHasCreateLimitForIngestionKeyError(false);
|
setHasCreateLimitForIngestionKeyError(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!signalCfg.usesSize && !signalCfg.usesCount) {
|
||||||
|
// Edge case: If there's no count or size usage at all
|
||||||
|
setActiveSignal(null);
|
||||||
|
setActiveAPIKey(null);
|
||||||
|
setIsEditAddLimitOpen(false);
|
||||||
|
setUpdatedTags([]);
|
||||||
|
hideAddViewModal();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -472,44 +556,73 @@ function MultiIngestionSettings(): JSX.Element {
|
|||||||
APIKey: IngestionKeyProps,
|
APIKey: IngestionKeyProps,
|
||||||
signal: LimitProps,
|
signal: LimitProps,
|
||||||
): void => {
|
): void => {
|
||||||
const { dailyLimit, secondsLimit } = addEditLimitForm.getFieldsValue();
|
const {
|
||||||
const payload = {
|
dailyLimit,
|
||||||
|
secondsLimit,
|
||||||
|
dailyCount,
|
||||||
|
dailyCountUnit,
|
||||||
|
secondsCount,
|
||||||
|
secondsCountUnit,
|
||||||
|
} = addEditLimitForm.getFieldsValue();
|
||||||
|
|
||||||
|
const payload: UpdateLimitProps = {
|
||||||
limitID: signal.id,
|
limitID: signal.id,
|
||||||
signal: signal.signal,
|
signal: signal.signal,
|
||||||
config: {},
|
config: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isUndefined(dailyLimit) && isUndefined(secondsLimit)) {
|
const signalCfg = SIGNALS_CONFIG.find((cfg) => cfg.name === signal.signal);
|
||||||
showDeleteLimitModal(APIKey, signal);
|
if (!signalCfg) return;
|
||||||
|
|
||||||
|
const noSizeProvided =
|
||||||
|
isUndefined(dailyLimit) && isUndefined(secondsLimit) && signalCfg.usesSize;
|
||||||
|
const noCountProvided =
|
||||||
|
isUndefined(dailyCount) && isUndefined(secondsCount) && signalCfg.usesCount;
|
||||||
|
|
||||||
|
// If the user cleared out all fields, remove the limit
|
||||||
|
if (noSizeProvided && noCountProvided) {
|
||||||
|
showDeleteLimitModal(APIKey, signal);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isUndefined(dailyLimit)) {
|
if (signalCfg.usesSize) {
|
||||||
payload.config = {
|
if (!isUndefined(dailyLimit)) {
|
||||||
day: {
|
payload.config.day = {
|
||||||
|
...payload.config.day,
|
||||||
size: gbToBytes(dailyLimit),
|
size: gbToBytes(dailyLimit),
|
||||||
},
|
};
|
||||||
};
|
}
|
||||||
|
if (!isUndefined(secondsLimit)) {
|
||||||
|
payload.config.second = {
|
||||||
|
...payload.config.second,
|
||||||
|
size: gbToBytes(secondsLimit),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isUndefined(secondsLimit)) {
|
if (signalCfg.usesCount) {
|
||||||
payload.config = {
|
if (!isUndefined(dailyCount)) {
|
||||||
...payload.config,
|
payload.config.day = {
|
||||||
second: {
|
...payload.config.day,
|
||||||
size: gbToBytes(secondsLimit),
|
count: countFromUnit(dailyCount, dailyCountUnit || 'million'),
|
||||||
},
|
};
|
||||||
};
|
}
|
||||||
|
if (!isUndefined(secondsCount)) {
|
||||||
|
payload.config.second = {
|
||||||
|
...payload.config.second,
|
||||||
|
count: countFromUnit(secondsCount, secondsCountUnit || 'million'),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateLimitForIngestionKey(payload);
|
updateLimitForIngestionKey(payload);
|
||||||
};
|
};
|
||||||
|
/* eslint-enable sonarjs/cognitive-complexity */
|
||||||
|
|
||||||
const bytesToGb = (size: number | undefined): number => {
|
const bytesToGb = (size: number | undefined): number => {
|
||||||
if (!size) {
|
if (!size) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return size / BYTES;
|
return size / BYTES;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -517,6 +630,12 @@ function MultiIngestionSettings(): JSX.Element {
|
|||||||
APIKey: IngestionKeyProps,
|
APIKey: IngestionKeyProps,
|
||||||
signal: LimitProps,
|
signal: LimitProps,
|
||||||
): void => {
|
): void => {
|
||||||
|
const dayCount = signal?.config?.day?.count;
|
||||||
|
const secondCount = signal?.config?.second?.count;
|
||||||
|
|
||||||
|
const dayCountConverted = countToUnit(dayCount || 0);
|
||||||
|
const secondCountConverted = countToUnit(secondCount || 0);
|
||||||
|
|
||||||
setActiveAPIKey(APIKey);
|
setActiveAPIKey(APIKey);
|
||||||
setActiveSignal({
|
setActiveSignal({
|
||||||
...signal,
|
...signal,
|
||||||
@ -524,11 +643,14 @@ function MultiIngestionSettings(): JSX.Element {
|
|||||||
...signal.config,
|
...signal.config,
|
||||||
day: {
|
day: {
|
||||||
...signal.config?.day,
|
...signal.config?.day,
|
||||||
enabled: !isNil(signal?.config?.day?.size),
|
enabled:
|
||||||
|
!isNil(signal?.config?.day?.size) || !isNil(signal?.config?.day?.count),
|
||||||
},
|
},
|
||||||
second: {
|
second: {
|
||||||
...signal.config?.second,
|
...signal.config?.second,
|
||||||
enabled: !isNil(signal?.config?.second?.size),
|
enabled:
|
||||||
|
!isNil(signal?.config?.second?.size) ||
|
||||||
|
!isNil(signal?.config?.second?.count),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -536,15 +658,22 @@ function MultiIngestionSettings(): JSX.Element {
|
|||||||
addEditLimitForm.setFieldsValue({
|
addEditLimitForm.setFieldsValue({
|
||||||
dailyLimit: bytesToGb(signal?.config?.day?.size || 0),
|
dailyLimit: bytesToGb(signal?.config?.day?.size || 0),
|
||||||
secondsLimit: bytesToGb(signal?.config?.second?.size || 0),
|
secondsLimit: bytesToGb(signal?.config?.second?.size || 0),
|
||||||
enableDailyLimit: !isNil(signal?.config?.day?.size),
|
enableDailyLimit:
|
||||||
enableSecondLimit: !isNil(signal?.config?.second?.size),
|
!isNil(signal?.config?.day?.size) || !isNil(signal?.config?.day?.count),
|
||||||
|
enableSecondLimit:
|
||||||
|
!isNil(signal?.config?.second?.size) ||
|
||||||
|
!isNil(signal?.config?.second?.count),
|
||||||
|
dailyCount: dayCountConverted.value,
|
||||||
|
dailyCountUnit: dayCountConverted.unit,
|
||||||
|
secondsCount: secondCountConverted.value,
|
||||||
|
secondsCountUnit: secondCountConverted.unit,
|
||||||
});
|
});
|
||||||
|
|
||||||
setIsEditAddLimitOpen(true);
|
setIsEditAddLimitOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDeleteLimitHandler = (): void => {
|
const onDeleteLimitHandler = (): void => {
|
||||||
if (activeSignal && activeSignal?.id) {
|
if (activeSignal && activeSignal.id) {
|
||||||
deleteLimitForKey(activeSignal.id);
|
deleteLimitForKey(activeSignal.id);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -572,13 +701,13 @@ function MultiIngestionSettings(): JSX.Element {
|
|||||||
formatTimezoneAdjustedTimestamp,
|
formatTimezoneAdjustedTimestamp,
|
||||||
);
|
);
|
||||||
|
|
||||||
const limits: { [key: string]: LimitProps } = {};
|
// Convert array of limits to a dictionary for quick access
|
||||||
|
const limitsDict: Record<string, LimitProps> = {};
|
||||||
APIKey.limits?.forEach((limit: LimitProps) => {
|
APIKey.limits?.forEach((limitItem: LimitProps) => {
|
||||||
limits[limit.signal] = limit;
|
limitsDict[limitItem.signal] = limitItem;
|
||||||
});
|
});
|
||||||
|
|
||||||
const hasLimits = (signal: string): boolean => !!limits[signal];
|
const hasLimits = (signalName: string): boolean => !!limitsDict[signalName];
|
||||||
|
|
||||||
const items: CollapseProps['items'] = [
|
const items: CollapseProps['items'] = [
|
||||||
{
|
{
|
||||||
@ -614,11 +743,9 @@ function MultiIngestionSettings(): JSX.Element {
|
|||||||
onClick={(e): void => {
|
onClick={(e): void => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
showEditModal(APIKey);
|
showEditModal(APIKey);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
className="periscope-btn ghost"
|
className="periscope-btn ghost"
|
||||||
icon={<Trash2 color={Color.BG_CHERRY_500} size={14} />}
|
icon={<Trash2 color={Color.BG_CHERRY_500} size={14} />}
|
||||||
@ -670,18 +797,23 @@ function MultiIngestionSettings(): JSX.Element {
|
|||||||
|
|
||||||
<div className="limits-data">
|
<div className="limits-data">
|
||||||
<div className="signals">
|
<div className="signals">
|
||||||
{SIGNALS.map((signal) => {
|
{SIGNALS_CONFIG.map((signalCfg) => {
|
||||||
const hasValidDayLimit = !isNil(limits[signal]?.config?.day?.size);
|
const signalName = signalCfg.name;
|
||||||
const hasValidSecondLimit = !isNil(
|
const limit = limitsDict[signalName];
|
||||||
limits[signal]?.config?.second?.size,
|
|
||||||
);
|
const hasValidDayLimit =
|
||||||
|
limit?.config?.day?.size !== undefined ||
|
||||||
|
limit?.config?.day?.count !== undefined;
|
||||||
|
const hasValidSecondLimit =
|
||||||
|
limit?.config?.second?.size !== undefined ||
|
||||||
|
limit?.config?.second?.count !== undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="signal" key={signal}>
|
<div className="signal" key={signalName}>
|
||||||
<div className="header">
|
<div className="header">
|
||||||
<div className="signal-name">{signal}</div>
|
<div className="signal-name">{signalName}</div>
|
||||||
<div className="actions">
|
<div className="actions">
|
||||||
{hasLimits(signal) ? (
|
{hasLimits(signalName) ? (
|
||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
className="periscope-btn ghost"
|
className="periscope-btn ghost"
|
||||||
@ -690,10 +822,9 @@ function MultiIngestionSettings(): JSX.Element {
|
|||||||
onClick={(e): void => {
|
onClick={(e): void => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
enableEditLimitMode(APIKey, limits[signal]);
|
enableEditLimitMode(APIKey, limit);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
className="periscope-btn ghost"
|
className="periscope-btn ghost"
|
||||||
icon={<Trash2 color={Color.BG_CHERRY_500} size={14} />}
|
icon={<Trash2 color={Color.BG_CHERRY_500} size={14} />}
|
||||||
@ -701,7 +832,7 @@ function MultiIngestionSettings(): JSX.Element {
|
|||||||
onClick={(e): void => {
|
onClick={(e): void => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
showDeleteLimitModal(APIKey, limits[signal]);
|
showDeleteLimitModal(APIKey, limit);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
@ -712,14 +843,12 @@ function MultiIngestionSettings(): JSX.Element {
|
|||||||
shape="round"
|
shape="round"
|
||||||
icon={<PlusIcon size={14} />}
|
icon={<PlusIcon size={14} />}
|
||||||
disabled={!!(activeAPIKey?.id === APIKey.id && activeSignal)}
|
disabled={!!(activeAPIKey?.id === APIKey.id && activeSignal)}
|
||||||
// eslint-disable-next-line sonarjs/no-identical-functions
|
|
||||||
onClick={(e): void => {
|
onClick={(e): void => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
enableEditLimitMode(APIKey, {
|
enableEditLimitMode(APIKey, {
|
||||||
id: signal,
|
id: signalName,
|
||||||
signal,
|
signal: signalName,
|
||||||
config: {},
|
config: {},
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
@ -732,7 +861,7 @@ function MultiIngestionSettings(): JSX.Element {
|
|||||||
|
|
||||||
<div className="signal-limit-values">
|
<div className="signal-limit-values">
|
||||||
{activeAPIKey?.id === APIKey.id &&
|
{activeAPIKey?.id === APIKey.id &&
|
||||||
activeSignal?.signal === signal &&
|
activeSignal?.signal === signalName &&
|
||||||
isEditAddLimitOpen ? (
|
isEditAddLimitOpen ? (
|
||||||
<Form
|
<Form
|
||||||
name="edit-ingestion-key-limit-form"
|
name="edit-ingestion-key-limit-form"
|
||||||
@ -740,8 +869,8 @@ function MultiIngestionSettings(): JSX.Element {
|
|||||||
form={addEditLimitForm}
|
form={addEditLimitForm}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
initialValues={{
|
initialValues={{
|
||||||
dailyLimit: bytesToGb(limits[signal]?.config?.day?.size),
|
dailyLimit: bytesToGb(limit?.config?.day?.size || 0),
|
||||||
secondsLimit: bytesToGb(limits[signal]?.config?.second?.size),
|
secondsLimit: bytesToGb(limit?.config?.second?.size || 0),
|
||||||
}}
|
}}
|
||||||
className="edit-ingestion-key-limit-form"
|
className="edit-ingestion-key-limit-form"
|
||||||
>
|
>
|
||||||
@ -756,16 +885,20 @@ function MultiIngestionSettings(): JSX.Element {
|
|||||||
size="small"
|
size="small"
|
||||||
checked={activeSignal?.config?.day?.enabled}
|
checked={activeSignal?.config?.day?.enabled}
|
||||||
onChange={(value): void => {
|
onChange={(value): void => {
|
||||||
setActiveSignal({
|
setActiveSignal((prev) =>
|
||||||
...activeSignal,
|
prev
|
||||||
config: {
|
? {
|
||||||
...activeSignal.config,
|
...prev,
|
||||||
day: {
|
config: {
|
||||||
...activeSignal.config?.day,
|
...prev.config,
|
||||||
enabled: value,
|
day: {
|
||||||
},
|
...prev.config?.day,
|
||||||
},
|
enabled: value,
|
||||||
});
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@ -775,50 +908,87 @@ function MultiIngestionSettings(): JSX.Element {
|
|||||||
Add a limit for data ingested daily
|
Add a limit for data ingested daily
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="size">
|
{signalCfg.usesSize && (
|
||||||
{activeSignal?.config?.day?.enabled ? (
|
<div className="size">
|
||||||
<Form.Item name="dailyLimit" key="dailyLimit">
|
{activeSignal?.config?.day?.enabled ? (
|
||||||
<InputNumber
|
<Form.Item name="dailyLimit" key="dailyLimit">
|
||||||
disabled={!activeSignal?.config?.day?.enabled}
|
<InputNumber
|
||||||
key="dailyLimit"
|
disabled={!activeSignal?.config?.day?.enabled}
|
||||||
addonAfter={
|
addonAfter={
|
||||||
<Select defaultValue="GiB" disabled>
|
<Select defaultValue="GiB" disabled>
|
||||||
<Option value="TiB"> TiB</Option>
|
<Option value="TiB">TiB</Option>
|
||||||
<Option value="GiB"> GiB</Option>
|
<Option value="GiB">GiB</Option>
|
||||||
<Option value="MiB"> MiB </Option>
|
<Option value="MiB">MiB</Option>
|
||||||
<Option value="KiB"> KiB </Option>
|
<Option value="KiB">KiB</Option>
|
||||||
</Select>
|
</Select>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
) : (
|
) : (
|
||||||
<div className="no-limit">
|
<div className="no-limit">
|
||||||
<Infinity size={16} /> NO LIMIT
|
<Infinity size={16} /> NO LIMIT
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
{signalCfg.usesCount && (
|
||||||
|
<div className="count">
|
||||||
|
{activeSignal?.config?.day?.enabled ? (
|
||||||
|
<Form.Item name="dailyCount" key="dailyCount">
|
||||||
|
<InputNumber
|
||||||
|
placeholder="Enter max # of samples/day"
|
||||||
|
addonAfter={
|
||||||
|
<Form.Item
|
||||||
|
name="dailyCountUnit"
|
||||||
|
noStyle
|
||||||
|
initialValue="million"
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
style={{
|
||||||
|
width: 90,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Option value="thousand">Thousand</Option>
|
||||||
|
<Option value="million">Million</Option>
|
||||||
|
<Option value="billion">Billion</Option>
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
) : (
|
||||||
|
<div className="no-limit">
|
||||||
|
<Infinity size={16} /> NO LIMIT
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="second-limit">
|
<div className="second-limit">
|
||||||
<div className="heading">
|
<div className="heading">
|
||||||
<div className="title">
|
<div className="title">
|
||||||
Per Second limit{' '}
|
Per Second limit
|
||||||
<div className="limit-enable-disable-toggle">
|
<div className="limit-enable-disable-toggle">
|
||||||
<Form.Item name="enableSecondLimit">
|
<Form.Item name="enableSecondLimit">
|
||||||
<Switch
|
<Switch
|
||||||
size="small"
|
size="small"
|
||||||
checked={activeSignal?.config?.second?.enabled}
|
checked={activeSignal?.config?.second?.enabled}
|
||||||
onChange={(value): void => {
|
onChange={(value): void => {
|
||||||
setActiveSignal({
|
setActiveSignal((prev) =>
|
||||||
...activeSignal,
|
prev
|
||||||
config: {
|
? {
|
||||||
...activeSignal.config,
|
...prev,
|
||||||
second: {
|
config: {
|
||||||
...activeSignal.config?.second,
|
...prev.config,
|
||||||
enabled: value,
|
second: {
|
||||||
},
|
...prev.config?.second,
|
||||||
},
|
enabled: value,
|
||||||
});
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@ -828,37 +998,68 @@ function MultiIngestionSettings(): JSX.Element {
|
|||||||
Add a limit for data ingested every second
|
Add a limit for data ingested every second
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{signalCfg.usesSize && (
|
||||||
<div className="size">
|
<div className="size">
|
||||||
{activeSignal?.config?.second?.enabled ? (
|
{activeSignal?.config?.second?.enabled ? (
|
||||||
<Form.Item name="secondsLimit" key="secondsLimit">
|
<Form.Item name="secondsLimit" key="secondsLimit">
|
||||||
<InputNumber
|
<InputNumber
|
||||||
key="secondsLimit"
|
disabled={!activeSignal?.config?.second?.enabled}
|
||||||
disabled={!activeSignal?.config?.second?.enabled}
|
addonAfter={
|
||||||
addonAfter={
|
<Select defaultValue="GiB" disabled>
|
||||||
<Select defaultValue="GiB" disabled>
|
<Option value="TiB">TiB</Option>
|
||||||
<Option value="TiB"> TiB</Option>
|
<Option value="GiB">GiB</Option>
|
||||||
<Option value="GiB"> GiB</Option>
|
<Option value="MiB">MiB</Option>
|
||||||
<Option value="MiB"> MiB </Option>
|
<Option value="KiB">KiB</Option>
|
||||||
<Option value="KiB"> KiB </Option>
|
</Select>
|
||||||
</Select>
|
}
|
||||||
}
|
/>
|
||||||
/>
|
</Form.Item>
|
||||||
</Form.Item>
|
) : (
|
||||||
) : (
|
<div className="no-limit">
|
||||||
<div className="no-limit">
|
<Infinity size={16} /> NO LIMIT
|
||||||
<Infinity size={16} /> NO LIMIT
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
</div>
|
||||||
</div>
|
)}
|
||||||
|
{signalCfg.usesCount && (
|
||||||
|
<div className="count">
|
||||||
|
{activeSignal?.config?.second?.enabled ? (
|
||||||
|
<Form.Item name="secondsCount" key="secondsCount">
|
||||||
|
<InputNumber
|
||||||
|
placeholder="Enter max # of samples/s"
|
||||||
|
addonAfter={
|
||||||
|
<Form.Item
|
||||||
|
name="secondsCountUnit"
|
||||||
|
noStyle
|
||||||
|
initialValue="million"
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
style={{
|
||||||
|
width: 90,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Option value="thousand">Thousand</Option>
|
||||||
|
<Option value="million">Million</Option>
|
||||||
|
<Option value="billion">Billion</Option>
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
) : (
|
||||||
|
<div className="no-limit">
|
||||||
|
<Infinity size={16} /> NO LIMIT
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{activeAPIKey?.id === APIKey.id &&
|
{activeAPIKey?.id === APIKey.id &&
|
||||||
activeSignal.signal === signal &&
|
activeSignal.signal === signalName &&
|
||||||
!isLoadingLimitForKey &&
|
!isLoadingLimitForKey &&
|
||||||
hasCreateLimitForIngestionKeyError &&
|
hasCreateLimitForIngestionKeyError &&
|
||||||
createLimitForIngestionKeyError &&
|
|
||||||
createLimitForIngestionKeyError?.error && (
|
createLimitForIngestionKeyError?.error && (
|
||||||
<div className="error">
|
<div className="error">
|
||||||
{createLimitForIngestionKeyError?.error}
|
{createLimitForIngestionKeyError?.error}
|
||||||
@ -866,17 +1067,17 @@ function MultiIngestionSettings(): JSX.Element {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{activeAPIKey?.id === APIKey.id &&
|
{activeAPIKey?.id === APIKey.id &&
|
||||||
activeSignal.signal === signal &&
|
activeSignal.signal === signalName &&
|
||||||
!isLoadingLimitForKey &&
|
!isLoadingLimitForKey &&
|
||||||
hasUpdateLimitForIngestionKeyError &&
|
hasUpdateLimitForIngestionKeyError &&
|
||||||
updateLimitForIngestionKeyError && (
|
updateLimitForIngestionKeyError?.error && (
|
||||||
<div className="error">
|
<div className="error">
|
||||||
{updateLimitForIngestionKeyError?.error}
|
{updateLimitForIngestionKeyError?.error}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{activeAPIKey?.id === APIKey.id &&
|
{activeAPIKey?.id === APIKey.id &&
|
||||||
activeSignal.signal === signal &&
|
activeSignal.signal === signalName &&
|
||||||
isEditAddLimitOpen && (
|
isEditAddLimitOpen && (
|
||||||
<div className="signal-limit-save-discard">
|
<div className="signal-limit-save-discard">
|
||||||
<Button
|
<Button
|
||||||
@ -890,10 +1091,10 @@ function MultiIngestionSettings(): JSX.Element {
|
|||||||
isLoadingLimitForKey || isLoadingUpdatedLimitForKey
|
isLoadingLimitForKey || isLoadingUpdatedLimitForKey
|
||||||
}
|
}
|
||||||
onClick={(): void => {
|
onClick={(): void => {
|
||||||
if (!hasLimits(signal)) {
|
if (!hasLimits(signalName)) {
|
||||||
handleAddLimit(APIKey, signal);
|
handleAddLimit(APIKey, signalName);
|
||||||
} else {
|
} else {
|
||||||
handleUpdateLimit(APIKey, limits[signal]);
|
handleUpdateLimit(APIKey, limitsDict[signalName]);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -915,55 +1116,99 @@ function MultiIngestionSettings(): JSX.Element {
|
|||||||
</Form>
|
</Form>
|
||||||
) : (
|
) : (
|
||||||
<div className="signal-limit-view-mode">
|
<div className="signal-limit-view-mode">
|
||||||
|
{/* DAILY limit usage/limit */}
|
||||||
<div className="signal-limit-value">
|
<div className="signal-limit-value">
|
||||||
<div className="limit-type">
|
<div className="limit-type">
|
||||||
Daily <Minus size={16} />{' '}
|
Daily <Minus size={16} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="limit-value">
|
<div className="limit-value">
|
||||||
{hasValidDayLimit ? (
|
{/* Size (if usesSize) */}
|
||||||
<>
|
{signalCfg.usesSize &&
|
||||||
{getYAxisFormattedValue(
|
(hasValidDayLimit &&
|
||||||
(limits[signal]?.metric?.day?.size || 0).toString(),
|
limit?.config?.day?.size !== undefined ? (
|
||||||
'bytes',
|
<>
|
||||||
)}{' '}
|
{getYAxisFormattedValue(
|
||||||
/{' '}
|
(limit?.metric?.day?.size || 0).toString(),
|
||||||
{getYAxisFormattedValue(
|
'bytes',
|
||||||
(limits[signal]?.config?.day?.size || 0).toString(),
|
)}{' '}
|
||||||
'bytes',
|
/{' '}
|
||||||
)}
|
{getYAxisFormattedValue(
|
||||||
</>
|
(limit?.config?.day?.size || 0).toString(),
|
||||||
) : (
|
'bytes',
|
||||||
<>
|
)}
|
||||||
<Infinity size={16} /> NO LIMIT
|
</>
|
||||||
</>
|
) : (
|
||||||
)}
|
<>
|
||||||
|
<Infinity size={16} /> NO LIMIT
|
||||||
|
</>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/* Count (if usesCount) */}
|
||||||
|
{signalCfg.usesCount &&
|
||||||
|
(limit?.config?.day?.count !== undefined ? (
|
||||||
|
<div style={{ marginTop: 4 }}>
|
||||||
|
{countToUnit(
|
||||||
|
limit?.metric?.day?.count || 0,
|
||||||
|
).value.toFixed(2)}{' '}
|
||||||
|
{countToUnit(limit?.metric?.day?.count || 0).unit} /{' '}
|
||||||
|
{countToUnit(
|
||||||
|
limit?.config?.day?.count || 0,
|
||||||
|
).value.toFixed(2)}{' '}
|
||||||
|
{countToUnit(limit?.config?.day?.count || 0).unit}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Infinity size={16} /> NO LIMIT
|
||||||
|
</>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* SECOND limit usage/limit */}
|
||||||
<div className="signal-limit-value">
|
<div className="signal-limit-value">
|
||||||
<div className="limit-type">
|
<div className="limit-type">
|
||||||
Seconds <Minus size={16} />
|
Seconds <Minus size={16} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="limit-value">
|
<div className="limit-value">
|
||||||
{hasValidSecondLimit ? (
|
{/* Size (if usesSize) */}
|
||||||
<>
|
{signalCfg.usesSize &&
|
||||||
{getYAxisFormattedValue(
|
(hasValidSecondLimit &&
|
||||||
(limits[signal]?.metric?.second?.size || 0).toString(),
|
limit?.config?.second?.size !== undefined ? (
|
||||||
'bytes',
|
<>
|
||||||
)}{' '}
|
{getYAxisFormattedValue(
|
||||||
/{' '}
|
(limit?.metric?.second?.size || 0).toString(),
|
||||||
{getYAxisFormattedValue(
|
'bytes',
|
||||||
(limits[signal]?.config?.second?.size || 0).toString(),
|
)}{' '}
|
||||||
'bytes',
|
/{' '}
|
||||||
)}
|
{getYAxisFormattedValue(
|
||||||
</>
|
(limit?.config?.second?.size || 0).toString(),
|
||||||
) : (
|
'bytes',
|
||||||
<>
|
)}
|
||||||
<Infinity size={16} /> NO LIMIT
|
</>
|
||||||
</>
|
) : (
|
||||||
)}
|
<>
|
||||||
|
<Infinity size={16} /> NO LIMIT
|
||||||
|
</>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{/* Count (if usesCount) */}
|
||||||
|
{signalCfg.usesCount &&
|
||||||
|
(limit?.config?.second?.count !== undefined ? (
|
||||||
|
<div style={{ marginTop: 4 }}>
|
||||||
|
{countToUnit(
|
||||||
|
limit?.metric?.second?.count || 0,
|
||||||
|
).value.toFixed(2)}{' '}
|
||||||
|
{countToUnit(limit?.metric?.second?.count || 0).unit} /{' '}
|
||||||
|
{countToUnit(
|
||||||
|
limit?.config?.second?.count || 0,
|
||||||
|
).value.toFixed(2)}{' '}
|
||||||
|
{countToUnit(limit?.config?.second?.count || 0).unit}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Infinity size={16} /> NO LIMIT
|
||||||
|
</>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -1033,7 +1278,6 @@ function MultiIngestionSettings(): JSX.Element {
|
|||||||
className="learn-more"
|
className="learn-more"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
{' '}
|
|
||||||
Learn more <ArrowUpRight size={14} />
|
Learn more <ArrowUpRight size={14} />
|
||||||
</a>
|
</a>
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
|
|||||||
@ -1,3 +1,14 @@
|
|||||||
|
export interface LimitConfig {
|
||||||
|
size?: number;
|
||||||
|
count?: number; // mainly used for metrics
|
||||||
|
enabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LimitSettings {
|
||||||
|
day?: LimitConfig;
|
||||||
|
second?: LimitConfig;
|
||||||
|
}
|
||||||
|
|
||||||
export interface LimitProps {
|
export interface LimitProps {
|
||||||
id: string;
|
id: string;
|
||||||
signal: string;
|
signal: string;
|
||||||
@ -5,56 +16,20 @@ export interface LimitProps {
|
|||||||
key_id?: string;
|
key_id?: string;
|
||||||
created_at?: string;
|
created_at?: string;
|
||||||
updated_at?: string;
|
updated_at?: string;
|
||||||
config?: {
|
config?: LimitSettings;
|
||||||
day?: {
|
metric?: LimitSettings;
|
||||||
size?: number;
|
|
||||||
enabled?: boolean;
|
|
||||||
};
|
|
||||||
second?: {
|
|
||||||
size?: number;
|
|
||||||
enabled?: boolean;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
metric?: {
|
|
||||||
day?: {
|
|
||||||
size?: number;
|
|
||||||
enabled?: boolean;
|
|
||||||
};
|
|
||||||
second?: {
|
|
||||||
size?: number;
|
|
||||||
enabled?: boolean;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AddLimitProps {
|
export interface AddLimitProps {
|
||||||
keyID: string;
|
keyID: string;
|
||||||
signal: string;
|
signal: string;
|
||||||
config: {
|
config: LimitSettings;
|
||||||
day?: {
|
|
||||||
size?: number;
|
|
||||||
enabled?: boolean;
|
|
||||||
};
|
|
||||||
second?: {
|
|
||||||
size?: number;
|
|
||||||
enabled?: boolean;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateLimitProps {
|
export interface UpdateLimitProps {
|
||||||
limitID: string;
|
limitID: string;
|
||||||
signal: string;
|
signal: string;
|
||||||
config: {
|
config: LimitSettings;
|
||||||
day?: {
|
|
||||||
size?: number;
|
|
||||||
enabled?: boolean;
|
|
||||||
};
|
|
||||||
second?: {
|
|
||||||
size?: number;
|
|
||||||
enabled?: boolean;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LimitSuccessProps {
|
export interface LimitSuccessProps {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user