mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-10-12 18:01:30 +08: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 { useCopyToClipboard } from 'react-use';
|
||||
import { ErrorResponse } from 'types/api';
|
||||
import { LimitProps } from 'types/api/ingestionKeys/limits/types';
|
||||
import {
|
||||
AddLimitProps,
|
||||
LimitProps,
|
||||
UpdateLimitProps,
|
||||
} from 'types/api/ingestionKeys/limits/types';
|
||||
import {
|
||||
IngestionKeyProps,
|
||||
PaginationProps,
|
||||
@ -69,6 +73,18 @@ const { Option } = Select;
|
||||
|
||||
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
|
||||
// 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
|
||||
@ -76,8 +92,6 @@ export const disabledDate = (current: any): boolean =>
|
||||
// Disable all dates before today
|
||||
current && current < dayjs().endOf('day');
|
||||
|
||||
const SIGNALS = ['logs', 'traces', 'metrics'];
|
||||
|
||||
export const showErrorNotification = (
|
||||
notifications: NotificationInstance,
|
||||
err: Error,
|
||||
@ -101,6 +115,31 @@ export const API_KEY_EXPIRY_OPTIONS: ExpiryOption[] = [
|
||||
{ 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 {
|
||||
const { user } = useAppContext();
|
||||
const { notifications } = useNotifications();
|
||||
@ -181,7 +220,6 @@ function MultiIngestionSettings(): JSX.Element {
|
||||
|
||||
const showEditModal = (apiKey: IngestionKeyProps): void => {
|
||||
setActiveAPIKey(apiKey);
|
||||
|
||||
handleFormReset();
|
||||
setUpdatedTags(apiKey.tags || []);
|
||||
|
||||
@ -424,44 +462,90 @@ function MultiIngestionSettings(): JSX.Element {
|
||||
addEditLimitForm.resetFields();
|
||||
};
|
||||
|
||||
/* eslint-disable sonarjs/cognitive-complexity */
|
||||
const handleAddLimit = (
|
||||
APIKey: IngestionKeyProps,
|
||||
signalName: string,
|
||||
): void => {
|
||||
const { dailyLimit, secondsLimit } = addEditLimitForm.getFieldsValue();
|
||||
const {
|
||||
dailyLimit,
|
||||
secondsLimit,
|
||||
dailyCount,
|
||||
dailyCountUnit,
|
||||
secondsCount,
|
||||
secondsCountUnit,
|
||||
} = addEditLimitForm.getFieldsValue();
|
||||
|
||||
const payload = {
|
||||
const payload: AddLimitProps = {
|
||||
keyID: APIKey.id,
|
||||
signal: signalName,
|
||||
config: {},
|
||||
};
|
||||
|
||||
if (!isUndefined(dailyLimit)) {
|
||||
payload.config = {
|
||||
day: {
|
||||
const signalCfg = SIGNALS_CONFIG.find((cfg) => cfg.name === signalName);
|
||||
if (!signalCfg) return;
|
||||
|
||||
// Only set size if usesSize is true
|
||||
if (signalCfg.usesSize) {
|
||||
if (!isUndefined(dailyLimit)) {
|
||||
payload.config.day = {
|
||||
...payload.config.day,
|
||||
size: gbToBytes(dailyLimit),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (!isUndefined(secondsLimit)) {
|
||||
payload.config = {
|
||||
...payload.config,
|
||||
second: {
|
||||
};
|
||||
}
|
||||
if (!isUndefined(secondsLimit)) {
|
||||
payload.config.second = {
|
||||
...payload.config.second,
|
||||
size: gbToBytes(secondsLimit),
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (isUndefined(dailyLimit) && isUndefined(secondsLimit)) {
|
||||
// No need to save as no limit is provided, close the edit view and reset active signal and api key
|
||||
// Only set count if usesCount is true
|
||||
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);
|
||||
setActiveAPIKey(null);
|
||||
setIsEditAddLimitOpen(false);
|
||||
setUpdatedTags([]);
|
||||
hideAddViewModal();
|
||||
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;
|
||||
}
|
||||
|
||||
@ -472,44 +556,73 @@ function MultiIngestionSettings(): JSX.Element {
|
||||
APIKey: IngestionKeyProps,
|
||||
signal: LimitProps,
|
||||
): void => {
|
||||
const { dailyLimit, secondsLimit } = addEditLimitForm.getFieldsValue();
|
||||
const payload = {
|
||||
const {
|
||||
dailyLimit,
|
||||
secondsLimit,
|
||||
dailyCount,
|
||||
dailyCountUnit,
|
||||
secondsCount,
|
||||
secondsCountUnit,
|
||||
} = addEditLimitForm.getFieldsValue();
|
||||
|
||||
const payload: UpdateLimitProps = {
|
||||
limitID: signal.id,
|
||||
signal: signal.signal,
|
||||
config: {},
|
||||
};
|
||||
|
||||
if (isUndefined(dailyLimit) && isUndefined(secondsLimit)) {
|
||||
showDeleteLimitModal(APIKey, signal);
|
||||
const signalCfg = SIGNALS_CONFIG.find((cfg) => cfg.name === signal.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;
|
||||
}
|
||||
|
||||
if (!isUndefined(dailyLimit)) {
|
||||
payload.config = {
|
||||
day: {
|
||||
if (signalCfg.usesSize) {
|
||||
if (!isUndefined(dailyLimit)) {
|
||||
payload.config.day = {
|
||||
...payload.config.day,
|
||||
size: gbToBytes(dailyLimit),
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
if (!isUndefined(secondsLimit)) {
|
||||
payload.config.second = {
|
||||
...payload.config.second,
|
||||
size: gbToBytes(secondsLimit),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (!isUndefined(secondsLimit)) {
|
||||
payload.config = {
|
||||
...payload.config,
|
||||
second: {
|
||||
size: gbToBytes(secondsLimit),
|
||||
},
|
||||
};
|
||||
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'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
updateLimitForIngestionKey(payload);
|
||||
};
|
||||
/* eslint-enable sonarjs/cognitive-complexity */
|
||||
|
||||
const bytesToGb = (size: number | undefined): number => {
|
||||
if (!size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return size / BYTES;
|
||||
};
|
||||
|
||||
@ -517,6 +630,12 @@ function MultiIngestionSettings(): JSX.Element {
|
||||
APIKey: IngestionKeyProps,
|
||||
signal: LimitProps,
|
||||
): 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);
|
||||
setActiveSignal({
|
||||
...signal,
|
||||
@ -524,11 +643,14 @@ function MultiIngestionSettings(): JSX.Element {
|
||||
...signal.config,
|
||||
day: {
|
||||
...signal.config?.day,
|
||||
enabled: !isNil(signal?.config?.day?.size),
|
||||
enabled:
|
||||
!isNil(signal?.config?.day?.size) || !isNil(signal?.config?.day?.count),
|
||||
},
|
||||
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({
|
||||
dailyLimit: bytesToGb(signal?.config?.day?.size || 0),
|
||||
secondsLimit: bytesToGb(signal?.config?.second?.size || 0),
|
||||
enableDailyLimit: !isNil(signal?.config?.day?.size),
|
||||
enableSecondLimit: !isNil(signal?.config?.second?.size),
|
||||
enableDailyLimit:
|
||||
!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);
|
||||
};
|
||||
|
||||
const onDeleteLimitHandler = (): void => {
|
||||
if (activeSignal && activeSignal?.id) {
|
||||
if (activeSignal && activeSignal.id) {
|
||||
deleteLimitForKey(activeSignal.id);
|
||||
}
|
||||
};
|
||||
@ -572,13 +701,13 @@ function MultiIngestionSettings(): JSX.Element {
|
||||
formatTimezoneAdjustedTimestamp,
|
||||
);
|
||||
|
||||
const limits: { [key: string]: LimitProps } = {};
|
||||
|
||||
APIKey.limits?.forEach((limit: LimitProps) => {
|
||||
limits[limit.signal] = limit;
|
||||
// Convert array of limits to a dictionary for quick access
|
||||
const limitsDict: Record<string, LimitProps> = {};
|
||||
APIKey.limits?.forEach((limitItem: LimitProps) => {
|
||||
limitsDict[limitItem.signal] = limitItem;
|
||||
});
|
||||
|
||||
const hasLimits = (signal: string): boolean => !!limits[signal];
|
||||
const hasLimits = (signalName: string): boolean => !!limitsDict[signalName];
|
||||
|
||||
const items: CollapseProps['items'] = [
|
||||
{
|
||||
@ -614,11 +743,9 @@ function MultiIngestionSettings(): JSX.Element {
|
||||
onClick={(e): void => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
showEditModal(APIKey);
|
||||
}}
|
||||
/>
|
||||
|
||||
<Button
|
||||
className="periscope-btn ghost"
|
||||
icon={<Trash2 color={Color.BG_CHERRY_500} size={14} />}
|
||||
@ -670,18 +797,23 @@ function MultiIngestionSettings(): JSX.Element {
|
||||
|
||||
<div className="limits-data">
|
||||
<div className="signals">
|
||||
{SIGNALS.map((signal) => {
|
||||
const hasValidDayLimit = !isNil(limits[signal]?.config?.day?.size);
|
||||
const hasValidSecondLimit = !isNil(
|
||||
limits[signal]?.config?.second?.size,
|
||||
);
|
||||
{SIGNALS_CONFIG.map((signalCfg) => {
|
||||
const signalName = signalCfg.name;
|
||||
const limit = limitsDict[signalName];
|
||||
|
||||
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 (
|
||||
<div className="signal" key={signal}>
|
||||
<div className="signal" key={signalName}>
|
||||
<div className="header">
|
||||
<div className="signal-name">{signal}</div>
|
||||
<div className="signal-name">{signalName}</div>
|
||||
<div className="actions">
|
||||
{hasLimits(signal) ? (
|
||||
{hasLimits(signalName) ? (
|
||||
<>
|
||||
<Button
|
||||
className="periscope-btn ghost"
|
||||
@ -690,10 +822,9 @@ function MultiIngestionSettings(): JSX.Element {
|
||||
onClick={(e): void => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
enableEditLimitMode(APIKey, limits[signal]);
|
||||
enableEditLimitMode(APIKey, limit);
|
||||
}}
|
||||
/>
|
||||
|
||||
<Button
|
||||
className="periscope-btn ghost"
|
||||
icon={<Trash2 color={Color.BG_CHERRY_500} size={14} />}
|
||||
@ -701,7 +832,7 @@ function MultiIngestionSettings(): JSX.Element {
|
||||
onClick={(e): void => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
showDeleteLimitModal(APIKey, limits[signal]);
|
||||
showDeleteLimitModal(APIKey, limit);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
@ -712,14 +843,12 @@ function MultiIngestionSettings(): JSX.Element {
|
||||
shape="round"
|
||||
icon={<PlusIcon size={14} />}
|
||||
disabled={!!(activeAPIKey?.id === APIKey.id && activeSignal)}
|
||||
// eslint-disable-next-line sonarjs/no-identical-functions
|
||||
onClick={(e): void => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
enableEditLimitMode(APIKey, {
|
||||
id: signal,
|
||||
signal,
|
||||
id: signalName,
|
||||
signal: signalName,
|
||||
config: {},
|
||||
});
|
||||
}}
|
||||
@ -732,7 +861,7 @@ function MultiIngestionSettings(): JSX.Element {
|
||||
|
||||
<div className="signal-limit-values">
|
||||
{activeAPIKey?.id === APIKey.id &&
|
||||
activeSignal?.signal === signal &&
|
||||
activeSignal?.signal === signalName &&
|
||||
isEditAddLimitOpen ? (
|
||||
<Form
|
||||
name="edit-ingestion-key-limit-form"
|
||||
@ -740,8 +869,8 @@ function MultiIngestionSettings(): JSX.Element {
|
||||
form={addEditLimitForm}
|
||||
autoComplete="off"
|
||||
initialValues={{
|
||||
dailyLimit: bytesToGb(limits[signal]?.config?.day?.size),
|
||||
secondsLimit: bytesToGb(limits[signal]?.config?.second?.size),
|
||||
dailyLimit: bytesToGb(limit?.config?.day?.size || 0),
|
||||
secondsLimit: bytesToGb(limit?.config?.second?.size || 0),
|
||||
}}
|
||||
className="edit-ingestion-key-limit-form"
|
||||
>
|
||||
@ -756,16 +885,20 @@ function MultiIngestionSettings(): JSX.Element {
|
||||
size="small"
|
||||
checked={activeSignal?.config?.day?.enabled}
|
||||
onChange={(value): void => {
|
||||
setActiveSignal({
|
||||
...activeSignal,
|
||||
config: {
|
||||
...activeSignal.config,
|
||||
day: {
|
||||
...activeSignal.config?.day,
|
||||
enabled: value,
|
||||
},
|
||||
},
|
||||
});
|
||||
setActiveSignal((prev) =>
|
||||
prev
|
||||
? {
|
||||
...prev,
|
||||
config: {
|
||||
...prev.config,
|
||||
day: {
|
||||
...prev.config?.day,
|
||||
enabled: value,
|
||||
},
|
||||
},
|
||||
}
|
||||
: null,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
@ -775,50 +908,87 @@ function MultiIngestionSettings(): JSX.Element {
|
||||
Add a limit for data ingested daily
|
||||
</div>
|
||||
</div>
|
||||
<div className="size">
|
||||
{activeSignal?.config?.day?.enabled ? (
|
||||
<Form.Item name="dailyLimit" key="dailyLimit">
|
||||
<InputNumber
|
||||
disabled={!activeSignal?.config?.day?.enabled}
|
||||
key="dailyLimit"
|
||||
addonAfter={
|
||||
<Select defaultValue="GiB" disabled>
|
||||
<Option value="TiB"> TiB</Option>
|
||||
<Option value="GiB"> GiB</Option>
|
||||
<Option value="MiB"> MiB </Option>
|
||||
<Option value="KiB"> KiB </Option>
|
||||
</Select>
|
||||
}
|
||||
/>
|
||||
</Form.Item>
|
||||
) : (
|
||||
<div className="no-limit">
|
||||
<Infinity size={16} /> NO LIMIT
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{signalCfg.usesSize && (
|
||||
<div className="size">
|
||||
{activeSignal?.config?.day?.enabled ? (
|
||||
<Form.Item name="dailyLimit" key="dailyLimit">
|
||||
<InputNumber
|
||||
disabled={!activeSignal?.config?.day?.enabled}
|
||||
addonAfter={
|
||||
<Select defaultValue="GiB" disabled>
|
||||
<Option value="TiB">TiB</Option>
|
||||
<Option value="GiB">GiB</Option>
|
||||
<Option value="MiB">MiB</Option>
|
||||
<Option value="KiB">KiB</Option>
|
||||
</Select>
|
||||
}
|
||||
/>
|
||||
</Form.Item>
|
||||
) : (
|
||||
<div className="no-limit">
|
||||
<Infinity size={16} /> NO LIMIT
|
||||
</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 className="second-limit">
|
||||
<div className="heading">
|
||||
<div className="title">
|
||||
Per Second limit{' '}
|
||||
Per Second limit
|
||||
<div className="limit-enable-disable-toggle">
|
||||
<Form.Item name="enableSecondLimit">
|
||||
<Switch
|
||||
size="small"
|
||||
checked={activeSignal?.config?.second?.enabled}
|
||||
onChange={(value): void => {
|
||||
setActiveSignal({
|
||||
...activeSignal,
|
||||
config: {
|
||||
...activeSignal.config,
|
||||
second: {
|
||||
...activeSignal.config?.second,
|
||||
enabled: value,
|
||||
},
|
||||
},
|
||||
});
|
||||
setActiveSignal((prev) =>
|
||||
prev
|
||||
? {
|
||||
...prev,
|
||||
config: {
|
||||
...prev.config,
|
||||
second: {
|
||||
...prev.config?.second,
|
||||
enabled: value,
|
||||
},
|
||||
},
|
||||
}
|
||||
: null,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
@ -828,37 +998,68 @@ function MultiIngestionSettings(): JSX.Element {
|
||||
Add a limit for data ingested every second
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="size">
|
||||
{activeSignal?.config?.second?.enabled ? (
|
||||
<Form.Item name="secondsLimit" key="secondsLimit">
|
||||
<InputNumber
|
||||
key="secondsLimit"
|
||||
disabled={!activeSignal?.config?.second?.enabled}
|
||||
addonAfter={
|
||||
<Select defaultValue="GiB" disabled>
|
||||
<Option value="TiB"> TiB</Option>
|
||||
<Option value="GiB"> GiB</Option>
|
||||
<Option value="MiB"> MiB </Option>
|
||||
<Option value="KiB"> KiB </Option>
|
||||
</Select>
|
||||
}
|
||||
/>
|
||||
</Form.Item>
|
||||
) : (
|
||||
<div className="no-limit">
|
||||
<Infinity size={16} /> NO LIMIT
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{signalCfg.usesSize && (
|
||||
<div className="size">
|
||||
{activeSignal?.config?.second?.enabled ? (
|
||||
<Form.Item name="secondsLimit" key="secondsLimit">
|
||||
<InputNumber
|
||||
disabled={!activeSignal?.config?.second?.enabled}
|
||||
addonAfter={
|
||||
<Select defaultValue="GiB" disabled>
|
||||
<Option value="TiB">TiB</Option>
|
||||
<Option value="GiB">GiB</Option>
|
||||
<Option value="MiB">MiB</Option>
|
||||
<Option value="KiB">KiB</Option>
|
||||
</Select>
|
||||
}
|
||||
/>
|
||||
</Form.Item>
|
||||
) : (
|
||||
<div className="no-limit">
|
||||
<Infinity size={16} /> NO LIMIT
|
||||
</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>
|
||||
|
||||
{activeAPIKey?.id === APIKey.id &&
|
||||
activeSignal.signal === signal &&
|
||||
activeSignal.signal === signalName &&
|
||||
!isLoadingLimitForKey &&
|
||||
hasCreateLimitForIngestionKeyError &&
|
||||
createLimitForIngestionKeyError &&
|
||||
createLimitForIngestionKeyError?.error && (
|
||||
<div className="error">
|
||||
{createLimitForIngestionKeyError?.error}
|
||||
@ -866,17 +1067,17 @@ function MultiIngestionSettings(): JSX.Element {
|
||||
)}
|
||||
|
||||
{activeAPIKey?.id === APIKey.id &&
|
||||
activeSignal.signal === signal &&
|
||||
activeSignal.signal === signalName &&
|
||||
!isLoadingLimitForKey &&
|
||||
hasUpdateLimitForIngestionKeyError &&
|
||||
updateLimitForIngestionKeyError && (
|
||||
updateLimitForIngestionKeyError?.error && (
|
||||
<div className="error">
|
||||
{updateLimitForIngestionKeyError?.error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeAPIKey?.id === APIKey.id &&
|
||||
activeSignal.signal === signal &&
|
||||
activeSignal.signal === signalName &&
|
||||
isEditAddLimitOpen && (
|
||||
<div className="signal-limit-save-discard">
|
||||
<Button
|
||||
@ -890,10 +1091,10 @@ function MultiIngestionSettings(): JSX.Element {
|
||||
isLoadingLimitForKey || isLoadingUpdatedLimitForKey
|
||||
}
|
||||
onClick={(): void => {
|
||||
if (!hasLimits(signal)) {
|
||||
handleAddLimit(APIKey, signal);
|
||||
if (!hasLimits(signalName)) {
|
||||
handleAddLimit(APIKey, signalName);
|
||||
} else {
|
||||
handleUpdateLimit(APIKey, limits[signal]);
|
||||
handleUpdateLimit(APIKey, limitsDict[signalName]);
|
||||
}
|
||||
}}
|
||||
>
|
||||
@ -915,55 +1116,99 @@ function MultiIngestionSettings(): JSX.Element {
|
||||
</Form>
|
||||
) : (
|
||||
<div className="signal-limit-view-mode">
|
||||
{/* DAILY limit usage/limit */}
|
||||
<div className="signal-limit-value">
|
||||
<div className="limit-type">
|
||||
Daily <Minus size={16} />{' '}
|
||||
Daily <Minus size={16} />
|
||||
</div>
|
||||
|
||||
<div className="limit-value">
|
||||
{hasValidDayLimit ? (
|
||||
<>
|
||||
{getYAxisFormattedValue(
|
||||
(limits[signal]?.metric?.day?.size || 0).toString(),
|
||||
'bytes',
|
||||
)}{' '}
|
||||
/{' '}
|
||||
{getYAxisFormattedValue(
|
||||
(limits[signal]?.config?.day?.size || 0).toString(),
|
||||
'bytes',
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Infinity size={16} /> NO LIMIT
|
||||
</>
|
||||
)}
|
||||
{/* Size (if usesSize) */}
|
||||
{signalCfg.usesSize &&
|
||||
(hasValidDayLimit &&
|
||||
limit?.config?.day?.size !== undefined ? (
|
||||
<>
|
||||
{getYAxisFormattedValue(
|
||||
(limit?.metric?.day?.size || 0).toString(),
|
||||
'bytes',
|
||||
)}{' '}
|
||||
/{' '}
|
||||
{getYAxisFormattedValue(
|
||||
(limit?.config?.day?.size || 0).toString(),
|
||||
'bytes',
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<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>
|
||||
|
||||
{/* SECOND limit usage/limit */}
|
||||
<div className="signal-limit-value">
|
||||
<div className="limit-type">
|
||||
Seconds <Minus size={16} />
|
||||
</div>
|
||||
|
||||
<div className="limit-value">
|
||||
{hasValidSecondLimit ? (
|
||||
<>
|
||||
{getYAxisFormattedValue(
|
||||
(limits[signal]?.metric?.second?.size || 0).toString(),
|
||||
'bytes',
|
||||
)}{' '}
|
||||
/{' '}
|
||||
{getYAxisFormattedValue(
|
||||
(limits[signal]?.config?.second?.size || 0).toString(),
|
||||
'bytes',
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Infinity size={16} /> NO LIMIT
|
||||
</>
|
||||
)}
|
||||
{/* Size (if usesSize) */}
|
||||
{signalCfg.usesSize &&
|
||||
(hasValidSecondLimit &&
|
||||
limit?.config?.second?.size !== undefined ? (
|
||||
<>
|
||||
{getYAxisFormattedValue(
|
||||
(limit?.metric?.second?.size || 0).toString(),
|
||||
'bytes',
|
||||
)}{' '}
|
||||
/{' '}
|
||||
{getYAxisFormattedValue(
|
||||
(limit?.config?.second?.size || 0).toString(),
|
||||
'bytes',
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<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>
|
||||
@ -1033,7 +1278,6 @@ function MultiIngestionSettings(): JSX.Element {
|
||||
className="learn-more"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{' '}
|
||||
Learn more <ArrowUpRight size={14} />
|
||||
</a>
|
||||
</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 {
|
||||
id: string;
|
||||
signal: string;
|
||||
@ -5,56 +16,20 @@ export interface LimitProps {
|
||||
key_id?: string;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
config?: {
|
||||
day?: {
|
||||
size?: number;
|
||||
enabled?: boolean;
|
||||
};
|
||||
second?: {
|
||||
size?: number;
|
||||
enabled?: boolean;
|
||||
};
|
||||
};
|
||||
metric?: {
|
||||
day?: {
|
||||
size?: number;
|
||||
enabled?: boolean;
|
||||
};
|
||||
second?: {
|
||||
size?: number;
|
||||
enabled?: boolean;
|
||||
};
|
||||
};
|
||||
config?: LimitSettings;
|
||||
metric?: LimitSettings;
|
||||
}
|
||||
|
||||
export interface AddLimitProps {
|
||||
keyID: string;
|
||||
signal: string;
|
||||
config: {
|
||||
day?: {
|
||||
size?: number;
|
||||
enabled?: boolean;
|
||||
};
|
||||
second?: {
|
||||
size?: number;
|
||||
enabled?: boolean;
|
||||
};
|
||||
};
|
||||
config: LimitSettings;
|
||||
}
|
||||
|
||||
export interface UpdateLimitProps {
|
||||
limitID: string;
|
||||
signal: string;
|
||||
config: {
|
||||
day?: {
|
||||
size?: number;
|
||||
enabled?: boolean;
|
||||
};
|
||||
second?: {
|
||||
size?: number;
|
||||
enabled?: boolean;
|
||||
};
|
||||
};
|
||||
config: LimitSettings;
|
||||
}
|
||||
|
||||
export interface LimitSuccessProps {
|
||||
|
Loading…
x
Reference in New Issue
Block a user