Compare commits

...

10 Commits

Author SHA1 Message Date
Gaurav Tewari
9949a72799 chore: sync with master 2026-05-22 19:57:09 +05:30
Manika Malhotra
4da5673e12 chore: migrate antd Progress to signoz ui component (#11398)
* chore: migrate antd ProgressBar to signoz ui component

* fix: homepage progress bar leaking section, resolve comments

* fix: remove stripe animation from progress bars in api monitoring section

* revert: accidental unrelated files
2026-05-22 14:09:08 +00:00
Ashwin Bhatkal
c3db819d8e chore: update code owners for dashboard v2 and e2e (#11412)
* chore: update code owners for dashboard and e2e

* chore: update code owners order
2026-05-22 13:19:04 +00:00
Piyush Singariya
c83578f211 chore: stats collection for logspipeline (#11409)
* feat: logspipeline statscollector

* fix: collect total and enabled

* chore: update metric name
2026-05-22 13:16:51 +00:00
Vinicius Lourenço
04a4d3fe32 fix(date-time-selection-v2): out of sync query params (#11399)
* fix(date-time-selection-v2): out of sync query params

* chore(get-current-search-params): explain why we have that file

* fix(pr): address comments
2026-05-22 11:58:53 +00:00
swapnil-signoz
27dc996fd8 chore(integrations): make dot-metrics dashboards canonical, remove IsDotMetricsEnabled flag (#11406)
IsDotMetricsEnabled always returns true so the _dot.json variants were
always served. Replace each non-dot dashboard JSON with the dot content,
delete the _dot.json files, and remove the dead flag-check logic from
HydrateFileUris.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 10:14:48 +00:00
Karan Balani
83b25f3e9a fix(authdomain): sso enabled toggle value from nested config (#11402)
* fix(authdomain): read ssoEnabled from nested config path in enforce SSO column

* test(authdomain): assert enforce SSO toggle reflects nested config.ssoEnabled

* test(authdomain): drop unnecessary Switch mock

---------

Co-authored-by: Karan Balani <29383381+balanikaran@users.noreply.github.com>
2026-05-22 09:24:09 +00:00
Gaurav Tewari
02d60e78be chore: update markdown 2026-05-22 10:59:44 +05:30
Gaurav Tewari
dd72eaac73 chore: remove markdown 2026-05-22 10:44:54 +05:30
Gaurav Tewari
e13014baba chore: refactor input 2026-05-22 02:17:01 +05:30
106 changed files with 1937 additions and 19908 deletions

7
.github/CODEOWNERS vendored
View File

@@ -118,6 +118,9 @@ go.mod @therealpandey
/tests/integration/ @therealpandey
# e2e tests
/tests/e2e/ @AshwinBhatkal
# Flagger Owners
/pkg/flagger/ @therealpandey
@@ -162,3 +165,7 @@ go.mod @therealpandey
/frontend/src/lib/dashboard/ @SigNoz/pulse-frontend
/frontend/src/lib/dashboardVariables/ @SigNoz/pulse-frontend
/frontend/src/components/NewSelect/ @SigNoz/pulse-frontend
## Dashboard V2
/frontend/src/pages/DashboardPageV2/ @SigNoz/pulse-frontend
/frontend/src/pages/DashboardsListPageV2/ @SigNoz/pulse-frontend

View File

@@ -18,6 +18,7 @@ const BANNED_COMPONENTS = {
'Use @signozhq/ui/typography Typography instead of antd Typography.',
Switch: 'Use @signozhq/ui/switch Switch instead of antd Switch.',
Badge: 'Use @signozhq/ui/badge instead of antd Badge.',
Progress: 'Use @signozhq/ui/progress instead of antd Progress.',
};
export default {

View File

@@ -51,13 +51,6 @@
background: var(--l1-background);
}
.progress-container {
.ant-progress-bg {
height: 8px !important;
border-radius: 4px;
}
}
.ant-table-tbody > tr:hover > td {
background: color-mix(in srgb, var(--l1-foreground) 4%, transparent);
}

View File

@@ -9,13 +9,13 @@ import {
Flex,
Input,
InputRef,
Progress,
Space,
Spin,
TableColumnsType,
TableColumnType,
Tooltip,
} from 'antd';
import { Progress } from '@signozhq/ui/progress';
import { Typography } from '@signozhq/ui/typography';
import type { FilterDropdownProps } from 'antd/lib/table/interface';
import logEvent from 'api/common/logEvent';
@@ -59,7 +59,7 @@ function ProgressRender(item: string | number): JSX.Element {
<Progress
percent={percent}
strokeLinecap="butt"
size="small"
showInfo
strokeColor={((): string => {
const cpuPercent = percent;
if (cpuPercent >= 90) {

View File

@@ -6,7 +6,7 @@ import {
useState,
} from 'react';
import { Color } from '@signozhq/design-tokens';
import { Input } from 'antd';
import { Input } from '@signozhq/ui/input';
import logEvent from 'api/common/logEvent';
import cx from 'classnames';
import { TimezonePickerShortcuts } from 'constants/shortcuts/TimezonePickerShortcuts';

View File

@@ -1,5 +1,6 @@
import { useTranslation } from 'react-i18next';
import { Card, Form, Input } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Card, Form } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import { PANEL_TYPES } from 'constants/queryBuilder';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';

View File

@@ -1,5 +1,6 @@
import { useState } from 'react';
import { Button, Input } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Button } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import cx from 'classnames';
import { X } from '@signozhq/icons';

View File

@@ -1,5 +1,6 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import { Button, Input, InputNumber, Popover, Tooltip } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Button, InputNumber, Popover, Tooltip } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import type { DefaultOptionType } from 'antd/es/select';
import cx from 'classnames';

View File

@@ -1,6 +1,7 @@
/* eslint-disable sonarjs/no-identical-functions */
import { Fragment, useMemo, useState } from 'react';
import { Button, Checkbox, Input, Skeleton } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Button, Checkbox, Skeleton } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import cx from 'classnames';
import { removeKeysFromExpression } from 'components/QueryBuilderV2/utils';

View File

@@ -1,5 +1,6 @@
import { useMemo } from 'react';
import { Button, Input } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Button } from 'antd';
import { Check, TableColumnsSplit, X } from '@signozhq/icons';
import { Filter as FilterType } from 'types/api/quickFilters/getCustomFilters';

View File

@@ -45,6 +45,10 @@
.contributors-row {
height: 80px;
}
.top-contributors-progress {
--progress-background: transparent;
}
&__content {
.ant-table {
&-cell {

View File

@@ -1,6 +1,7 @@
import { HTMLAttributes } from 'react';
import { Color } from '@signozhq/design-tokens';
import { Progress, Table, TableColumnsType as ColumnsType } from 'antd';
import { Table, TableColumnsType as ColumnsType } from 'antd';
import { Progress } from '@signozhq/ui/progress';
import logEvent from 'api/common/logEvent';
import { ConditionalAlertPopover } from 'container/AlertHistory/AlertPopover/AlertPopover';
import AlertLabels from 'pages/AlertDetails/AlertHeader/AlertLabels/AlertLabels';
@@ -51,8 +52,8 @@ function TopContributorsRows({
<Progress
percent={(count / totalCurrentTriggers) * 100}
showInfo={false}
trailColor="rgba(255, 255, 255, 0)"
strokeColor={Color.BG_ROBIN_500}
className="top-contributors-progress"
/>
</ConditionalAlertPopover>
),

View File

@@ -141,12 +141,9 @@
.progress-container {
width: 158px;
.ant-progress {
margin: 0;
.ant-progress-text {
font-weight: 600;
}
span {
font-weight: 600;
}
}

View File

@@ -1,7 +1,8 @@
import { useMemo } from 'react';
import { useQueries } from 'react-query';
import { Color } from '@signozhq/design-tokens';
import { Progress, Skeleton, Tooltip } from 'antd';
import { Skeleton, Tooltip } from 'antd';
import { Progress } from '@signozhq/ui/progress';
import { Typography } from '@signozhq/ui/typography';
import { ENTITY_VERSION_V5 } from 'constants/app';
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
@@ -136,12 +137,11 @@ function DomainMetrics({
<Tooltip title={formattedDomainMetricsData.errorRate}>
{formattedDomainMetricsData.errorRate !== '-' ? (
<Progress
status="active"
percent={Number(
Number(formattedDomainMetricsData.errorRate).toFixed(2),
)}
strokeLinecap="butt"
size="small"
showInfo
strokeColor={((): string => {
const errorRatePercent = Number(
Number(formattedDomainMetricsData.errorRate).toFixed(2),

View File

@@ -1,7 +1,8 @@
import { useMemo } from 'react';
import { UseQueryResult } from 'react-query';
import { Color } from '@signozhq/design-tokens';
import { Progress, Skeleton, Tooltip } from 'antd';
import { Skeleton, Tooltip } from 'antd';
import { Progress } from '@signozhq/ui/progress';
import { Typography } from '@signozhq/ui/typography';
import {
getDisplayValue,
@@ -80,10 +81,9 @@ function EndPointMetrics({
<Tooltip title={metricsData?.errorRate}>
{metricsData?.errorRate !== '-' ? (
<Progress
status="active"
percent={Number(Number(metricsData?.errorRate ?? 0).toFixed(2))}
strokeLinecap="butt"
size="small"
showInfo
strokeColor={((): string => {
const errorRatePercent = Number(
Number(metricsData?.errorRate ?? 0).toFixed(2),

View File

@@ -1,6 +1,7 @@
import { ReactNode } from 'react';
import { Color } from '@signozhq/design-tokens';
import { Progress, TableColumnType as ColumnType, Tag, Tooltip } from 'antd';
import { TableColumnType as ColumnType, Tag, Tooltip } from 'antd';
import { Progress } from '@signozhq/ui/progress';
import { convertFiltersToExpressionWithExistingQuery } from 'components/QueryBuilderV2/utils';
import {
FiltersType,
@@ -257,10 +258,9 @@ export const columnsConfig: ColumnType<APIDomainsRowData>[] = [
errorRate === 'n/a' || errorRate === '-' ? 0 : errorRate;
return (
<Progress
status="active"
percent={Number((errorRateValue as number).toFixed(2))}
strokeLinecap="butt"
size="small"
showInfo
strokeColor={((): string => {
const errorRatePercent = Number((errorRateValue as number).toFixed(2));
if (errorRatePercent >= 90) {
@@ -1022,14 +1022,13 @@ export const getEndPointsColumnsConfig = (
className: `column`,
render: (errorRate: number | string): React.ReactNode => (
<Progress
status="active"
percent={Number(
(
(errorRate === 'n/a' || errorRate === '-' ? 0 : errorRate) as number
).toFixed(1),
)}
strokeLinecap="butt"
size="small"
showInfo
strokeColor={((): string => {
const errorRatePercent = Number((errorRate as number).toFixed(1));
if (errorRatePercent >= 90) {
@@ -2514,10 +2513,9 @@ export const dependentServicesColumns: ColumnType<DependentServicesData>[] = [
render: (errorPercentage: number | string): React.ReactNode =>
errorPercentage !== '-' ? (
<Progress
status="active"
percent={Number((errorPercentage as number).toFixed(2))}
strokeLinecap="butt"
size="small"
showInfo
strokeColor={((): string => {
const errorPercentagePercent = Number(
(errorPercentage as number).toFixed(2),
@@ -3022,14 +3020,13 @@ export const getAllEndpointsWidgetData = (
),
F1: (errorRate: any): ReactNode => (
<Progress
status="active"
percent={Number(
(
(errorRate === 'n/a' || errorRate === '-' ? 0 : errorRate) as number
).toFixed(2),
)}
strokeLinecap="butt"
size="small"
showInfo
strokeColor={((): string => {
const errorRatePercent = Number(
(

View File

@@ -1,5 +1,6 @@
import { useMemo, useState } from 'react';
import { Button, Input, Select, Tooltip } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Button, Select, Tooltip } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import { CircleX, Trash } from '@signozhq/icons';
import { useAppContext } from 'providers/App/App';

View File

@@ -1,4 +1,5 @@
import { Collapse, Input } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Collapse } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import { useCreateAlertState } from '../context';

View File

@@ -1,5 +1,6 @@
import { useMemo } from 'react';
import { Input, Select } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Select } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import { ADVANCED_OPTIONS_TIME_UNIT_OPTIONS } from '../../context/constants';

View File

@@ -1,6 +1,5 @@
import React, { useEffect, useState } from 'react';
import { Input } from 'antd';
import { Input } from '@signozhq/ui/input';
import './TimeInput.scss';
export interface TimeInputProps {

View File

@@ -1,4 +1,5 @@
import { Input, Select } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Select } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import { useCreateAlertState } from '../context';

View File

@@ -16,7 +16,8 @@ import {
Plus,
X,
} from '@signozhq/icons';
import { Button, Card, Input, Modal, Popover, Tag, Tooltip } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Button, Card, Modal, Popover, Tag, Tooltip } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import logEvent from 'api/common/logEvent';
import ConfigureIcon from 'assets/Integrations/ConfigureIcon';

View File

@@ -1,5 +1,6 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { Button, Input } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Button } from 'antd';
import { PrecisionOption, PrecisionOptionsEnum } from 'components/Graph/types';
import { ResizeTable } from 'components/ResizeTable';
import { useNotifications } from 'hooks/useNotifications';

View File

@@ -19,11 +19,11 @@ import {
Info,
} from '@signozhq/icons';
import { Color } from '@signozhq/design-tokens';
import { Input } from '@signozhq/ui/input';
import {
Button,
ColorPicker,
Divider,
Input,
Modal,
RefSelectProps,
Select,

View File

@@ -1,7 +1,7 @@
import { Dispatch, SetStateAction } from 'react';
import { useTranslation } from 'react-i18next';
import { Form, Input } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Form } from 'antd';
import { EmailChannel } from '../../CreateAlertChannels/config';
function EmailForm({ setSelectedConfig }: EmailFormProps): JSX.Element {

View File

@@ -1,6 +1,7 @@
import { Dispatch, SetStateAction } from 'react';
import { useTranslation } from 'react-i18next';
import { Form, Input } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Form } from 'antd';
import { MarkdownRenderer } from 'components/MarkdownRenderer/MarkdownRenderer';
import { WebhookChannel } from '../../CreateAlertChannels/config';

View File

@@ -1,7 +1,8 @@
import { Dispatch, ReactElement, SetStateAction } from 'react';
import { useTranslation } from 'react-i18next';
import { Form, FormInstance, Input, Select } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Switch } from '@signozhq/ui/switch';
import { Form, FormInstance, Select } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import type { Store } from 'antd/lib/form/interface';
import ROUTES from 'constants/routes';

View File

@@ -6,7 +6,8 @@ import { useIsFetching } from 'react-query';
import { useDispatch } from 'react-redux';
import { useLocation } from 'react-router-dom';
import { Color } from '@signozhq/design-tokens';
import { Button, Form, Input, Modal } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Button, Form, Modal } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import logEvent from 'api/common/logEvent';
import cx from 'classnames';

View File

@@ -39,7 +39,5 @@
width: 100% !important;
.ant-progress-steps-outer {
width: 100% !important;
}
--progress-width: 100%;
}

View File

@@ -1,4 +1,4 @@
import { Progress } from 'antd';
import { Progress } from '@signozhq/ui/progress';
import { ChecklistItem } from '../HomeChecklist/HomeChecklist';
@@ -15,9 +15,7 @@ function StepsProgress({
const totalChecklistItems = checklistItems.length;
const progress = Math.round(
(completedChecklistItems.length / totalChecklistItems) * 100,
);
const progress = (completedChecklistItems.length / totalChecklistItems) * 100;
return (
<div className="steps-progress-container">

View File

@@ -1,6 +1,7 @@
import React from 'react';
import { Color } from '@signozhq/design-tokens';
import { Progress, Tag } from 'antd';
import { Tag } from 'antd';
import { Progress } from '@signozhq/ui/progress';
import { Typography } from '@signozhq/ui/typography';
import {
getHostLists,
@@ -79,8 +80,8 @@ export const hostDetailsMetadataConfig: K8sDetailsMetadataConfig<HostData>[] = [
render: (value): React.ReactNode => (
<Progress
percent={Number(Number(value).toFixed(1))}
size="small"
strokeColor={getProgressColor(Number(value))}
showInfo
/>
),
},
@@ -90,8 +91,8 @@ export const hostDetailsMetadataConfig: K8sDetailsMetadataConfig<HostData>[] = [
render: (value): React.ReactNode => (
<Progress
percent={Number(Number(value).toFixed(1))}
size="small"
strokeColor={getMemoryProgressColor(Number(value))}
showInfo
/>
),
},

View File

@@ -60,11 +60,6 @@
& > div {
width: 100%;
}
:global(.ant-progress-bg) {
height: 8px !important;
border-radius: 4px;
}
}
.progressBar {

View File

@@ -103,12 +103,8 @@
.progress-container {
width: 158px;
.ant-progress {
margin: 0;
.ant-progress-text {
font-weight: 600;
}
span {
font-weight: 600;
}
}
@@ -292,10 +288,6 @@
}
.progress-container {
.ant-progress-bg {
height: 8px !important;
border-radius: 4px;
}
}
.ant-table-tbody > tr:hover > td {

View File

@@ -1,4 +1,4 @@
import { Progress } from 'antd';
import { Progress } from '@signozhq/ui/progress';
import TanStackTable from 'components/TanStackTableView';
import {
getMemoryProgressColor,
@@ -53,7 +53,6 @@ export function EntityProgressBar({
<Progress
percent={percentage}
strokeLinecap="butt"
size="small"
status="normal"
strokeColor={getStrokeColor(type, value)}
className={styles.progressBar}

View File

@@ -5,12 +5,12 @@ import { useCopyToClipboard } from 'react-use';
import { Color } from '@signozhq/design-tokens';
import { Badge } from '@signozhq/ui/badge';
import { Button } from '@signozhq/ui/button';
import { Input } from '@signozhq/ui/input';
import {
Col,
Collapse,
DatePicker,
Form,
Input,
InputNumber,
Modal,
Row,

View File

@@ -1,4 +1,5 @@
import { Form, Input } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Form } from 'antd';
import { CloudintegrationtypesCredentialsDTO } from 'api/generated/services/sigNoz.schemas';
function RenderConnectionFields({

View File

@@ -1,6 +1,7 @@
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Button, Form, Input } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Button, Form } from 'antd';
import apply from 'api/v3/licenses/post';
import { useNotifications } from 'hooks/useNotifications';
import APIError from 'types/api/error';

View File

@@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { ChangeEvent, useState } from 'react';
import { Button, Input, Modal } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Button, Modal } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import ApacheIcon from 'assets/CustomIcons/ApacheIcon';
import DockerIcon from 'assets/CustomIcons/DockerIcon';

View File

@@ -12,11 +12,11 @@ import { useTranslation } from 'react-i18next';
import { generatePath } from 'react-router-dom';
import { useCopyToClipboard } from 'react-use';
import { Color } from '@signozhq/design-tokens';
import { Input } from '@signozhq/ui/input';
import {
Button,
Dropdown,
Flex,
Input,
MenuProps,
Modal,
Popover,

View File

@@ -1,7 +1,8 @@
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { LoaderCircle, Check } from '@signozhq/icons';
import { Button, Input, Space } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Button, Space } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import logEvent from 'api/common/logEvent';
import { useNotifications } from 'hooks/useNotifications';

View File

@@ -2,8 +2,9 @@ import { ReactNode, useState } from 'react';
import MEditor, { EditorProps, Monaco } from '@monaco-editor/react';
import { Color } from '@signozhq/design-tokens';
import { Button } from '@signozhq/ui/button';
import { Input } from '@signozhq/ui/input';
import { Switch } from '@signozhq/ui/switch';
import { Collapse, Divider, Input, Tag } from 'antd';
import { Collapse, Divider, Tag } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import { AddToQueryHOCProps } from 'components/Logs/AddToQueryHOC';
import { ChangeViewFunctionType } from 'container/ExplorerOptions/types';

View File

@@ -2,7 +2,8 @@ import { ChangeEvent, useCallback, useState } from 'react';
// eslint-disable-next-line no-restricted-imports
import { useSelector } from 'react-redux';
import { CirclePlus, X } from '@signozhq/icons';
import { Col, Input } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Col } from 'antd';
import CategoryHeading from 'components/Logs/CategoryHeading';
import { fieldSearchFilter } from 'lib/logs/fieldSearch';
import { AppState } from 'store/reducers';

View File

@@ -2,7 +2,8 @@ import { useCallback, useMemo, useState } from 'react';
// eslint-disable-next-line no-restricted-imports
import { useSelector } from 'react-redux';
import { SquareX, X } from '@signozhq/icons';
import { Button, Input, Select } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Button, Select } from 'antd';
import CategoryHeading from 'components/Logs/CategoryHeading';
import {
ConditionalOperators,

View File

@@ -1,6 +1,7 @@
import { Input } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import { Select } from 'antd';
// TODO(@signozhq/ui-input): migrate this <Input> once @signozhq/ui Input
// supports the `onWheel` handler (used to blur on scroll for number inputs).
import { Input, Select } from 'antd';
import classNames from 'classnames';
import { TIME_AGGREGATION_OPTIONS } from './constants';

View File

@@ -1,7 +1,8 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useQueryClient } from 'react-query';
import type { TableColumnsType as ColumnsType } from 'antd';
import { Button, Collapse, Input, Select, Spin } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Button, Collapse, Select, Spin } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import logEvent from 'api/common/logEvent';
import {

View File

@@ -142,13 +142,6 @@
}
}
.progress-container {
.ant-progress-bg {
height: 8px !important;
border-radius: 4px;
}
}
.ant-table-tbody > tr:hover > td {
background: color-mix(in srgb, var(--l1-foreground) 4%, transparent);
}

View File

@@ -7,7 +7,8 @@ import {
DropResult,
} from 'react-beautiful-dnd';
import { Color } from '@signozhq/design-tokens';
import { Button, Divider, Dropdown, Input, MenuProps, Tooltip } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Button, Divider, Dropdown, MenuProps, Tooltip } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import { FieldDataType } from 'api/v5/v5';
import { SOMETHING_WENT_WRONG } from 'constants/api';

View File

@@ -1,5 +1,7 @@
import { useEffect, useMemo, useState } from 'react';
import { Button, Col, Form, Input as AntInput, Input, Row } from 'antd';
// TODO(@signozhq/ui-input): migrate <Input> once @signozhq/ui Input
// supports the `spellCheck` prop on the URL input below.
import { Button, Col, Form, Input, Input as AntInput, Row } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import { CONTEXT_LINK_FIELDS } from 'container/NewWidget/RightContainer/ContextLinks/constants';
import {

View File

@@ -1,7 +1,8 @@
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Blocks, Check, LoaderCircle } from '@signozhq/icons';
import { Button, Card, Form, Input, Select, Space } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Button, Card, Form, Select, Space } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import logEvent from 'api/common/logEvent';
import cx from 'classnames';

View File

@@ -1,7 +1,8 @@
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Check, Server, LoaderCircle } from '@signozhq/icons';
import { Button, Card, Form, Input, Space } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Button, Card, Form, Space } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import logEvent from 'api/common/logEvent';
import cx from 'classnames';

View File

@@ -58,6 +58,26 @@ describe('AuthDomain', () => {
});
});
it('reflects ssoEnabled state from nested config in each row toggle', async () => {
server.use(
rest.get(AUTH_DOMAINS_LIST_ENDPOINT, (_, res, ctx) =>
res(ctx.status(200), ctx.json(mockDomainsListResponse)),
),
);
render(<AuthDomain />);
// mockDomainsListResponse rows:
// [0] signoz.io → config.ssoEnabled: true
// [1] example.com → config.ssoEnabled: false
// [2] corp.io → config.ssoEnabled: true
const switches = await screen.findAllByRole('switch');
expect(switches).toHaveLength(3);
expect(switches[0]).toBeChecked();
expect(switches[1]).not.toBeChecked();
expect(switches[2]).toBeChecked();
});
it('renders empty state when no domains exist', async () => {
server.use(
rest.get(AUTH_DOMAINS_LIST_ENDPOINT, (_, res, ctx) =>

View File

@@ -121,14 +121,14 @@ function AuthDomain(): JSX.Element {
},
{
title: 'Enforce SSO',
dataIndex: 'ssoEnabled',
dataIndex: ['config', 'ssoEnabled'],
key: 'ssoEnabled',
width: 80,
render: (
value: boolean,
record: AuthtypesGettableAuthDomainDTO,
): JSX.Element => (
<SSOEnforcementToggle isDefaultChecked={value} record={record} />
<SSOEnforcementToggle isDefaultChecked={!!value} record={record} />
),
},
{

View File

@@ -1,6 +1,7 @@
import { useTranslation } from 'react-i18next';
import { Plus, Trash2 } from '@signozhq/icons';
import { Button, Form, FormInstance, Input, Select, Space } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Button, Form, FormInstance, Select, Space } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import { requireErrorMessage } from 'utils/form/requireErrorMessage';

View File

@@ -87,12 +87,7 @@
.service-progress-indicator {
width: fit-content;
margin-inline-end: 0px !important;
margin-bottom: 0px !important;
.ant-progress-inner {
width: 30px;
}
--progress-width: 30px;
}
.percent-value {

View File

@@ -1,6 +1,7 @@
import { useEffect, useMemo, useState } from 'react';
import { useParams } from 'react-router-dom';
import { Progress, Skeleton, Tooltip } from 'antd';
import { Skeleton, Tooltip } from 'antd';
import { Progress } from '@signozhq/ui/progress';
import { Typography } from '@signozhq/ui/typography';
import { AxiosError } from 'axios';
import Spinner from 'components/Spinner';

View File

@@ -1,6 +1,6 @@
import { useTranslation } from 'react-i18next';
import { Form, Input } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Form } from 'antd';
import { ProcessorFormField } from '../../AddNewProcessor/config';
import { formValidationRules } from '../../config';

View File

@@ -1,4 +1,6 @@
import { ChangeEventHandler, useState } from 'react';
// TODO(@signozhq/ui-input): migrate to @signozhq/ui Input once the antd
// `InputProps` spread (`size`, etc.) is no longer needed on this wrapper.
import { Input, InputProps } from 'antd';
function CSVInput({ value, onChange, ...otherProps }: InputProps): JSX.Element {

View File

@@ -1,7 +1,8 @@
import { useEffect, useState } from 'react';
import { Info } from '@signozhq/icons';
import { Input } from '@signozhq/ui/input';
import { Switch } from '@signozhq/ui/switch';
import { Flex, Form, Input, Space, Tooltip } from 'antd';
import { Flex, Form, Space, Tooltip } from 'antd';
import { ProcessorData } from 'types/api/pipeline/def';
import { PREDEFINED_MAPPING } from '../config';

View File

@@ -1,6 +1,7 @@
import { useTranslation } from 'react-i18next';
import { Form, Input, Select, Space } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Switch } from '@signozhq/ui/switch';
import { Form, Select, Space } from 'antd';
import { ModalFooterTitle } from 'container/PipelinePage/styles';
import { ProcessorData } from 'types/api/pipeline/def';

View File

@@ -2,7 +2,8 @@ import React, { ChangeEvent, useEffect, useState } from 'react';
import { useHistory } from 'react-router-dom';
import { Plus, Search } from '@signozhq/icons';
import { Color } from '@signozhq/design-tokens';
import { Button, Flex, Form, Input, Tooltip } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Button, Flex, Form, Tooltip } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import {
useDeleteDowntimeScheduleByID,

View File

@@ -1,6 +1,7 @@
import { useCallback, useMemo, useState } from 'react';
import { useQuery } from 'react-query';
import { Input, Skeleton } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Skeleton } from 'antd';
import { getKeySuggestions } from 'api/querySuggestions/getKeySuggestions';
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
import { QUERY_BUILDER_KEY_TYPES } from 'constants/antlrQueryConstants';

View File

@@ -1,7 +1,8 @@
import { ChangeEvent, useMemo } from 'react';
import { Plus, Search } from '@signozhq/icons';
import { Color } from '@signozhq/design-tokens';
import { Button, Flex, Input, Tooltip } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Button, Flex, Tooltip } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import { useAppContext } from 'providers/App/App';
import { USER_ROLES } from 'types/roles';

View File

@@ -1,5 +1,5 @@
import { useCallback, useMemo, useState } from 'react';
import { Input } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Typography } from '@signozhq/ui/typography';
import cx from 'classnames';
import CopyClipboardHOC from 'components/Logs/CopyClipboardHOC';

View File

@@ -1,5 +1,6 @@
import { useState } from 'react';
import { Collapse, Input, Modal } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Collapse, Modal } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import { getYAxisFormattedValue } from 'components/Graph/yAxisConfig';
import { Diamond } from '@signozhq/icons';

View File

@@ -9,10 +9,10 @@ import {
useState,
} from 'react';
import { useMutation, useQuery } from 'react-query';
import { Input } from '@signozhq/ui/input';
import {
Button,
Checkbox,
Input,
Modal,
Select,
Skeleton,

View File

@@ -0,0 +1,258 @@
import {
act,
fireEvent,
render,
screen,
waitFor,
} from '@testing-library/react';
import DateTimeSelection from '../index';
import {
__resetSearchParamsGetter,
__setSearchParamsGetterForTest,
} from '../utils/getUnstableCurrentSearchParams';
import { queryClient, TestWrapper } from './testUtils';
const mockSafeNavigate = jest.fn();
jest.mock('hooks/useSafeNavigate', () => ({
useSafeNavigate: (): { safeNavigate: jest.Mock } => ({
safeNavigate: mockSafeNavigate,
}),
}));
jest.mock('container/NewExplorerCTA', () => ({
__esModule: true,
default: (): null => null,
}));
jest.mock('components/CustomTimePicker/CustomTimePicker', () => ({
__esModule: true,
default: ({
onSelect,
}: {
onSelect: (value: string) => void;
}): JSX.Element => (
<div data-testid="custom-time-picker">
<button
type="button"
data-testid="select-15m"
onClick={(): void => onSelect('15m')}
>
15m
</button>
<button
type="button"
data-testid="select-1h"
onClick={(): void => onSelect('1h')}
>
1h
</button>
<button
type="button"
data-testid="select-6h"
onClick={(): void => onSelect('6h')}
>
6h
</button>
<button
type="button"
data-testid="select-custom"
onClick={(): void => onSelect('custom')}
>
Custom
</button>
</div>
),
}));
describe('DateTimeSelectionV2 - Edge Cases', () => {
let currentSearchParams: URLSearchParams;
beforeEach(() => {
jest.clearAllMocks();
mockSafeNavigate.mockClear();
queryClient.clear();
});
afterEach(() => {
__resetSearchParamsGetter();
});
describe('Fresh Params at Navigation Time (Core Fix)', () => {
it('should read params at navigation time, not render time', async () => {
currentSearchParams = new URLSearchParams('relativeTime=30m');
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper
initialSearchParams="relativeTime=30m"
onUrlUpdate={(event): void => {
currentSearchParams = event.searchParams;
}}
>
<DateTimeSelection showAutoRefresh />
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
currentSearchParams = new URLSearchParams(
'relativeTime=30m&externalParam=addedLater',
);
mockSafeNavigate.mockClear();
act(() => {
fireEvent.click(screen.getByTestId('select-1h'));
});
await waitFor(() => {
expect(mockSafeNavigate).toHaveBeenCalled();
});
const navigatedUrl = mockSafeNavigate.mock.calls[
mockSafeNavigate.mock.calls.length - 1
][0] as string;
expect(navigatedUrl).toContain('relativeTime=1h');
expect(navigatedUrl).toContain('externalParam=addedLater');
});
it('should preserve multiple externally added params', async () => {
currentSearchParams = new URLSearchParams('relativeTime=30m');
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper initialSearchParams="relativeTime=30m">
<DateTimeSelection showAutoRefresh />
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
currentSearchParams = new URLSearchParams(
'relativeTime=30m&yAxisUnit=bytes&groupBy=host&view=table',
);
mockSafeNavigate.mockClear();
act(() => {
fireEvent.click(screen.getByTestId('select-6h'));
});
await waitFor(() => {
expect(mockSafeNavigate).toHaveBeenCalled();
});
const navigatedUrl = mockSafeNavigate.mock.calls[
mockSafeNavigate.mock.calls.length - 1
][0] as string;
expect(navigatedUrl).toContain('relativeTime=6h');
expect(navigatedUrl).toContain('yAxisUnit=bytes');
expect(navigatedUrl).toContain('groupBy=host');
expect(navigatedUrl).toContain('view=table');
});
});
describe('Empty and Special Values', () => {
it('should handle empty URL params gracefully', async () => {
currentSearchParams = new URLSearchParams('');
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper initialSearchParams="">
<DateTimeSelection showAutoRefresh />
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
mockSafeNavigate.mockClear();
act(() => {
fireEvent.click(screen.getByTestId('select-15m'));
});
await waitFor(() => {
expect(mockSafeNavigate).toHaveBeenCalled();
});
const navigatedUrl = mockSafeNavigate.mock.calls[
mockSafeNavigate.mock.calls.length - 1
][0] as string;
expect(navigatedUrl).toContain('relativeTime=15m');
});
it('should handle special characters in preserved params', async () => {
currentSearchParams = new URLSearchParams(
'relativeTime=30m&filter=name%3D%22test%22',
);
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper initialSearchParams="relativeTime=30m&filter=name%3D%22test%22">
<DateTimeSelection showAutoRefresh />
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
mockSafeNavigate.mockClear();
act(() => {
fireEvent.click(screen.getByTestId('select-1h'));
});
await waitFor(() => {
expect(mockSafeNavigate).toHaveBeenCalled();
});
const navigatedUrl = mockSafeNavigate.mock.calls[
mockSafeNavigate.mock.calls.length - 1
][0] as string;
expect(navigatedUrl).toContain('relativeTime=1h');
expect(navigatedUrl).toContain('filter=');
});
it('should not navigate when selecting custom (opens picker instead)', async () => {
currentSearchParams = new URLSearchParams('relativeTime=30m');
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper initialSearchParams="relativeTime=30m">
<DateTimeSelection showAutoRefresh />
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
mockSafeNavigate.mockClear();
act(() => {
fireEvent.click(screen.getByTestId('select-custom'));
});
await new Promise((resolve) => setTimeout(resolve, 100));
const customNavigationCalls = mockSafeNavigate.mock.calls.filter((call) => {
const url = call[0] as string;
return url.includes('startTime=') || url.includes('endTime=');
});
expect(customNavigationCalls).toHaveLength(0);
});
});
});

View File

@@ -0,0 +1,180 @@
import {
act,
fireEvent,
render,
screen,
waitFor,
} from '@testing-library/react';
import DateTimeSelection from '../index';
import {
__resetSearchParamsGetter,
__setSearchParamsGetterForTest,
} from '../utils/getUnstableCurrentSearchParams';
import { queryClient, TestWrapper } from './testUtils';
const mockSafeNavigate = jest.fn();
jest.mock('hooks/useSafeNavigate', () => ({
useSafeNavigate: (): { safeNavigate: jest.Mock } => ({
safeNavigate: mockSafeNavigate,
}),
}));
jest.mock('container/NewExplorerCTA', () => ({
__esModule: true,
default: (): null => null,
}));
let mockOnCustomDateHandler: ((range: [unknown, unknown]) => void) | null =
null;
let mockOnValidCustomDateChange: ((data: { timeStr: string }) => void) | null =
null;
jest.mock('components/CustomTimePicker/CustomTimePicker', () => ({
__esModule: true,
default: ({
onSelect,
onCustomDateHandler,
onValidCustomDateChange,
}: {
onSelect: (value: string) => void;
onCustomDateHandler?: (range: [unknown, unknown]) => void;
onValidCustomDateChange?: (data: { timeStr: string }) => void;
}): JSX.Element => {
mockOnCustomDateHandler = onCustomDateHandler || null;
mockOnValidCustomDateChange = onValidCustomDateChange || null;
return (
<div data-testid="custom-time-picker">
<button
type="button"
data-testid="select-1h"
onClick={(): void => onSelect('1h')}
>
1h
</button>
</div>
);
},
}));
describe('DateTimeSelectionV2 - Modal Mode', () => {
let currentSearchParams: URLSearchParams;
beforeEach(() => {
jest.clearAllMocks();
mockSafeNavigate.mockClear();
queryClient.clear();
mockOnCustomDateHandler = null;
mockOnValidCustomDateChange = null;
});
afterEach(() => {
__resetSearchParamsGetter();
});
it('should call onTimeChange instead of navigating for relative time', async () => {
currentSearchParams = new URLSearchParams('relativeTime=30m');
__setSearchParamsGetterForTest(() => currentSearchParams);
const mockOnTimeChange = jest.fn();
render(
<TestWrapper initialSearchParams="relativeTime=30m">
<DateTimeSelection
showAutoRefresh
isModalTimeSelection
onTimeChange={mockOnTimeChange}
/>
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
mockSafeNavigate.mockClear();
act(() => {
fireEvent.click(screen.getByTestId('select-1h'));
});
await waitFor(() => {
expect(mockOnTimeChange).toHaveBeenCalledWith('1h');
});
expect(mockSafeNavigate).not.toHaveBeenCalled();
});
it('should call onTimeChange with custom and timestamps for custom date', async () => {
currentSearchParams = new URLSearchParams('relativeTime=30m');
__setSearchParamsGetterForTest(() => currentSearchParams);
const mockOnTimeChange = jest.fn();
render(
<TestWrapper initialSearchParams="relativeTime=30m">
<DateTimeSelection
showAutoRefresh
isModalTimeSelection
onTimeChange={mockOnTimeChange}
/>
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
const startMoment = { toDate: (): Date => new Date(1700000000000) };
const endMoment = { toDate: (): Date => new Date(1700003600000) };
mockSafeNavigate.mockClear();
act(() => {
mockOnCustomDateHandler?.([startMoment, endMoment]);
});
await waitFor(() => {
expect(mockOnTimeChange).toHaveBeenCalledWith(
'custom',
[1700000000000, 1700003600000],
);
});
expect(mockSafeNavigate).not.toHaveBeenCalled();
});
it('should call onTimeChange for valid custom date string in modal', async () => {
currentSearchParams = new URLSearchParams('relativeTime=30m');
__setSearchParamsGetterForTest(() => currentSearchParams);
const mockOnTimeChange = jest.fn();
render(
<TestWrapper initialSearchParams="relativeTime=30m">
<DateTimeSelection
showAutoRefresh
isModalTimeSelection
onTimeChange={mockOnTimeChange}
/>
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
mockSafeNavigate.mockClear();
act(() => {
mockOnValidCustomDateChange?.({ timeStr: '4h' });
});
await waitFor(() => {
expect(mockOnTimeChange).toHaveBeenCalledWith('4h');
});
expect(mockSafeNavigate).not.toHaveBeenCalled();
});
});

View File

@@ -0,0 +1,207 @@
// eslint-disable-next-line no-restricted-imports
import { Provider } from 'react-redux';
import { MemoryRouter, Route } from 'react-router-dom';
import { NuqsTestingAdapter } from 'nuqs/adapters/testing';
import { QueryClient, QueryClientProvider } from 'react-query';
import {
act,
fireEvent,
render,
screen,
waitFor,
} from '@testing-library/react';
import { parseAsString, useQueryState } from 'nuqs';
import { AppContext } from 'providers/App/App';
import TimezoneProvider from 'providers/Timezone';
import { QueryBuilderProvider } from 'providers/QueryBuilder';
import configureStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import store from 'store';
import { getAppContextMock } from 'tests/test-utils';
import { CompatRouter } from 'react-router-dom-v5-compat';
import DateTimeSelection from '../index';
import {
__resetSearchParamsGetter,
__setSearchParamsGetterForTest,
} from '../utils/getUnstableCurrentSearchParams';
const queryClient = new QueryClient({
defaultOptions: {
queries: { refetchOnWindowFocus: false, retry: false },
mutations: { retry: false },
},
});
const mockStore = configureStore([thunk]);
const mockSafeNavigate = jest.fn();
jest.mock('hooks/useSafeNavigate', () => ({
useSafeNavigate: (): { safeNavigate: jest.Mock } => ({
safeNavigate: mockSafeNavigate,
}),
}));
jest.mock('components/CustomTimePicker/CustomTimePicker', () => ({
__esModule: true,
default: ({
onSelect,
}: {
onSelect: (value: string) => void;
}): JSX.Element => (
<div data-testid="custom-time-picker">
<button
type="button"
data-testid="select-1h"
onClick={(): void => onSelect('1h')}
>
1h
</button>
</div>
),
}));
jest.mock('container/NewExplorerCTA', () => ({
__esModule: true,
default: (): null => null,
}));
function NuqsParamSetter({ paramValue }: { paramValue: string }): JSX.Element {
const [, setYAxisUnit] = useQueryState(
'yAxisUnit',
parseAsString.withDefault(''),
);
return (
<button
type="button"
data-testid="set-nuqs-param"
onClick={(): void => {
setYAxisUnit(paramValue);
}}
>
Set yAxisUnit
</button>
);
}
interface WrapperProps {
children: React.ReactNode;
initialSearchParams?: string;
initialPath?: string;
onUrlUpdate?: (event: { searchParams: URLSearchParams }) => void;
}
function TestWrapper({
children,
initialSearchParams = '',
initialPath = '/services',
onUrlUpdate,
}: WrapperProps): JSX.Element {
const initialEntry = initialSearchParams
? `${initialPath}?${initialSearchParams}`
: initialPath;
const mockedStore = mockStore({
...store.getState(),
app: {
...store.getState().app,
role: 'ADMIN',
user: {
userId: 'test-user-id',
email: 'test@signoz.io',
name: 'TestUser',
profilePictureURL: '',
accessJwt: '',
refreshJwt: '',
},
isLoggedIn: true,
},
});
return (
<MemoryRouter initialEntries={[initialEntry]}>
<CompatRouter>
<NuqsTestingAdapter
searchParams={initialSearchParams}
onUrlUpdate={onUrlUpdate}
>
<QueryClientProvider client={queryClient}>
<Provider store={mockedStore}>
<AppContext.Provider value={getAppContextMock('ADMIN')}>
<TimezoneProvider>
<QueryBuilderProvider>
<Route path="*">{children}</Route>
</QueryBuilderProvider>
</TimezoneProvider>
</AppContext.Provider>
</Provider>
</QueryClientProvider>
</NuqsTestingAdapter>
</CompatRouter>
</MemoryRouter>
);
}
describe('REGRESSION: DateTimeSelectionV2 preserves nuqs query params on time change', () => {
let currentSearchParams: URLSearchParams;
beforeEach(() => {
jest.clearAllMocks();
mockSafeNavigate.mockClear();
queryClient.clear();
// Initialize with test's initial search params
currentSearchParams = new URLSearchParams('relativeTime=30m');
__setSearchParamsGetterForTest(() => currentSearchParams);
});
afterEach(() => {
__resetSearchParamsGetter();
});
it('should preserve yAxisUnit param set via nuqs when changing time selection', async () => {
render(
<TestWrapper
initialSearchParams="relativeTime=30m"
onUrlUpdate={(event): void => {
// Sync nuqs URL updates to our mock getter
// This simulates how window.location.search would be updated in real browser
currentSearchParams = event.searchParams;
}}
>
<NuqsParamSetter paramValue="bytes" />
<DateTimeSelection showAutoRefresh />
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
act(() => {
fireEvent.click(screen.getByTestId('set-nuqs-param'));
});
await waitFor(() => {
expect(currentSearchParams.get('yAxisUnit')).toBe('bytes');
});
mockSafeNavigate.mockClear();
act(() => {
fireEvent.click(screen.getByTestId('select-1h'));
});
await waitFor(() => {
expect(mockSafeNavigate).toHaveBeenCalled();
});
const navigatedUrl = mockSafeNavigate.mock.calls[
mockSafeNavigate.mock.calls.length - 1
][0] as string;
expect(navigatedUrl).toContain('relativeTime=1h');
expect(navigatedUrl).toContain('yAxisUnit=bytes');
});
});

View File

@@ -0,0 +1,133 @@
import { render, screen, waitFor } from '@testing-library/react';
import ROUTES from 'constants/routes';
import DateTimeSelection from '../index';
import {
__resetSearchParamsGetter,
__setSearchParamsGetterForTest,
} from '../utils/getUnstableCurrentSearchParams';
import { queryClient, TestWrapper } from './testUtils';
const mockSafeNavigate = jest.fn();
jest.mock('hooks/useSafeNavigate', () => ({
useSafeNavigate: (): { safeNavigate: jest.Mock } => ({
safeNavigate: mockSafeNavigate,
}),
}));
jest.mock('container/NewExplorerCTA', () => ({
__esModule: true,
default: (): null => null,
}));
jest.mock('components/CustomTimePicker/CustomTimePicker', () => ({
__esModule: true,
default: (): JSX.Element => <div data-testid="custom-time-picker" />,
}));
describe('DateTimeSelectionV2 - Route-Specific Behavior', () => {
let currentSearchParams: URLSearchParams;
beforeEach(() => {
jest.clearAllMocks();
mockSafeNavigate.mockClear();
queryClient.clear();
});
afterEach(() => {
__resetSearchParamsGetter();
});
describe('Alert Pages', () => {
it('should set default time for alert overview when no time params', async () => {
currentSearchParams = new URLSearchParams('otherParam=value');
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper
initialSearchParams="otherParam=value"
initialPath={ROUTES.ALERT_OVERVIEW}
>
<DateTimeSelection showAutoRefresh defaultRelativeTime="6h" />
</TestWrapper>,
);
await waitFor(() => {
expect(mockSafeNavigate).toHaveBeenCalled();
});
const navigatedUrl = mockSafeNavigate.mock.calls[0][0] as string;
expect(navigatedUrl).toContain('relativeTime=6h');
expect(navigatedUrl).toContain('otherParam=value');
});
it('should set default time for alert history when no time params', async () => {
currentSearchParams = new URLSearchParams('filter=active');
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper
initialSearchParams="filter=active"
initialPath={ROUTES.ALERT_HISTORY}
>
<DateTimeSelection showAutoRefresh defaultRelativeTime="6h" />
</TestWrapper>,
);
await waitFor(() => {
expect(mockSafeNavigate).toHaveBeenCalled();
});
const navigatedUrl = mockSafeNavigate.mock.calls[0][0] as string;
expect(navigatedUrl).toContain('relativeTime=6h');
});
it('should NOT override existing time params on alert pages', async () => {
currentSearchParams = new URLSearchParams('relativeTime=1h&filter=active');
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper
initialSearchParams="relativeTime=1h&filter=active"
initialPath={ROUTES.ALERT_OVERVIEW}
>
<DateTimeSelection showAutoRefresh defaultRelativeTime="6h" />
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
const calls = mockSafeNavigate.mock.calls;
if (calls.length > 0) {
const lastUrl = calls[calls.length - 1][0] as string;
expect(lastUrl).toContain('relativeTime=1h');
}
});
});
describe('disableUrlSync Behavior', () => {
it('should not sync URL on mount when disableUrlSync is true', async () => {
currentSearchParams = new URLSearchParams('');
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper initialSearchParams="" initialPath="/services">
<DateTimeSelection showAutoRefresh disableUrlSync />
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
const syncCalls = mockSafeNavigate.mock.calls.filter((call) => {
const url = call[0] as string;
return url.includes('relativeTime=') && !url.includes('services?');
});
expect(syncCalls).toHaveLength(0);
});
});
});

View File

@@ -0,0 +1,353 @@
import {
act,
fireEvent,
render,
screen,
waitFor,
} from '@testing-library/react';
import DateTimeSelection from '../index';
import {
__resetSearchParamsGetter,
__setSearchParamsGetterForTest,
} from '../utils/getUnstableCurrentSearchParams';
import { queryClient, TestWrapper, createMockMoment } from './testUtils';
const mockSafeNavigate = jest.fn();
jest.mock('hooks/useSafeNavigate', () => ({
useSafeNavigate: (): { safeNavigate: jest.Mock } => ({
safeNavigate: mockSafeNavigate,
}),
}));
jest.mock('container/NewExplorerCTA', () => ({
__esModule: true,
default: (): null => null,
}));
let mockOnCustomDateHandler: ((range: [unknown, unknown]) => void) | null =
null;
let mockOnValidCustomDateChange: ((data: { timeStr: string }) => void) | null =
null;
jest.mock('components/CustomTimePicker/CustomTimePicker', () => ({
__esModule: true,
default: ({
onSelect,
onCustomDateHandler,
onValidCustomDateChange,
}: {
onSelect: (value: string) => void;
onCustomDateHandler?: (range: [unknown, unknown]) => void;
onValidCustomDateChange?: (data: { timeStr: string }) => void;
}): JSX.Element => {
mockOnCustomDateHandler = onCustomDateHandler || null;
mockOnValidCustomDateChange = onValidCustomDateChange || null;
return (
<div data-testid="custom-time-picker">
<button
type="button"
data-testid="select-15m"
onClick={(): void => onSelect('15m')}
>
15m
</button>
<button
type="button"
data-testid="select-1h"
onClick={(): void => onSelect('1h')}
>
1h
</button>
<button
type="button"
data-testid="select-6h"
onClick={(): void => onSelect('6h')}
>
6h
</button>
</div>
);
},
}));
describe('DateTimeSelectionV2 - Time Selection', () => {
let currentSearchParams: URLSearchParams;
beforeEach(() => {
jest.clearAllMocks();
mockSafeNavigate.mockClear();
queryClient.clear();
mockOnCustomDateHandler = null;
mockOnValidCustomDateChange = null;
});
afterEach(() => {
__resetSearchParamsGetter();
});
describe('Relative Time', () => {
it('should update relativeTime and remove startTime/endTime', async () => {
currentSearchParams = new URLSearchParams(
'startTime=1000&endTime=2000&otherParam=keep',
);
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper initialSearchParams="startTime=1000&endTime=2000&otherParam=keep">
<DateTimeSelection showAutoRefresh />
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
mockSafeNavigate.mockClear();
act(() => {
fireEvent.click(screen.getByTestId('select-15m'));
});
await waitFor(() => {
expect(mockSafeNavigate).toHaveBeenCalled();
});
const navigatedUrl = mockSafeNavigate.mock.calls[
mockSafeNavigate.mock.calls.length - 1
][0] as string;
expect(navigatedUrl).toContain('relativeTime=15m');
expect(navigatedUrl).not.toContain('startTime=');
expect(navigatedUrl).not.toContain('endTime=');
expect(navigatedUrl).toContain('otherParam=keep');
});
it('should remove activeLogId param on time change', async () => {
currentSearchParams = new URLSearchParams(
'relativeTime=30m&activeLogId=log123',
);
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper initialSearchParams="relativeTime=30m&activeLogId=log123">
<DateTimeSelection showAutoRefresh />
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
mockSafeNavigate.mockClear();
act(() => {
fireEvent.click(screen.getByTestId('select-1h'));
});
await waitFor(() => {
expect(mockSafeNavigate).toHaveBeenCalled();
});
const navigatedUrl = mockSafeNavigate.mock.calls[
mockSafeNavigate.mock.calls.length - 1
][0] as string;
expect(navigatedUrl).toContain('relativeTime=1h');
expect(navigatedUrl).not.toContain('activeLogId');
});
it('should update compositeQuery with new ID when present', async () => {
const compositeQuery = encodeURIComponent(
JSON.stringify({ id: 'old-id', builder: { queryData: [] } }),
);
currentSearchParams = new URLSearchParams(
`relativeTime=30m&compositeQuery=${compositeQuery}`,
);
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper
initialSearchParams={`relativeTime=30m&compositeQuery=${compositeQuery}`}
>
<DateTimeSelection showAutoRefresh />
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
mockSafeNavigate.mockClear();
act(() => {
fireEvent.click(screen.getByTestId('select-6h'));
});
await waitFor(() => {
expect(mockSafeNavigate).toHaveBeenCalled();
});
const navigatedUrl = mockSafeNavigate.mock.calls[
mockSafeNavigate.mock.calls.length - 1
][0] as string;
expect(navigatedUrl).toContain('compositeQuery=');
expect(navigatedUrl).not.toContain('old-id');
});
it('should preserve all non-time URL params', async () => {
currentSearchParams = new URLSearchParams(
'relativeTime=30m&param1=a&param2=b&param3=c',
);
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper initialSearchParams="relativeTime=30m&param1=a&param2=b&param3=c">
<DateTimeSelection showAutoRefresh />
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
mockSafeNavigate.mockClear();
act(() => {
fireEvent.click(screen.getByTestId('select-1h'));
});
await waitFor(() => {
expect(mockSafeNavigate).toHaveBeenCalled();
});
const navigatedUrl = mockSafeNavigate.mock.calls[
mockSafeNavigate.mock.calls.length - 1
][0] as string;
expect(navigatedUrl).toContain('relativeTime=1h');
expect(navigatedUrl).toContain('param1=a');
expect(navigatedUrl).toContain('param2=b');
expect(navigatedUrl).toContain('param3=c');
});
});
describe('Custom Date Range', () => {
it('should set startTime/endTime and remove relativeTime', async () => {
currentSearchParams = new URLSearchParams('relativeTime=30m&keepThis=yes');
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper initialSearchParams="relativeTime=30m&keepThis=yes">
<DateTimeSelection showAutoRefresh />
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
const startMoment = createMockMoment(1700000000000);
const endMoment = createMockMoment(1700003600000);
mockSafeNavigate.mockClear();
act(() => {
mockOnCustomDateHandler?.([startMoment, endMoment]);
});
await waitFor(() => {
expect(mockSafeNavigate).toHaveBeenCalled();
});
const navigatedUrl = mockSafeNavigate.mock.calls[
mockSafeNavigate.mock.calls.length - 1
][0] as string;
expect(navigatedUrl).toContain('startTime=1700000000000');
expect(navigatedUrl).toContain('endTime=1700003600000');
expect(navigatedUrl).not.toContain('relativeTime=');
expect(navigatedUrl).toContain('keepThis=yes');
});
it('should update compositeQuery when present for custom date', async () => {
const compositeQuery = encodeURIComponent(
JSON.stringify({ id: 'old-id', builder: { queryData: [] } }),
);
currentSearchParams = new URLSearchParams(
`relativeTime=30m&compositeQuery=${compositeQuery}`,
);
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper
initialSearchParams={`relativeTime=30m&compositeQuery=${compositeQuery}`}
>
<DateTimeSelection showAutoRefresh />
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
const startMoment = createMockMoment(1700000000000);
const endMoment = createMockMoment(1700003600000);
mockSafeNavigate.mockClear();
act(() => {
mockOnCustomDateHandler?.([startMoment, endMoment]);
});
await waitFor(() => {
expect(mockSafeNavigate).toHaveBeenCalled();
});
const navigatedUrl = mockSafeNavigate.mock.calls[
mockSafeNavigate.mock.calls.length - 1
][0] as string;
expect(navigatedUrl).toContain('compositeQuery=');
expect(navigatedUrl).not.toContain('old-id');
});
});
describe('Valid Custom Date String', () => {
it('should handle shorthand date format and preserve params', async () => {
currentSearchParams = new URLSearchParams('relativeTime=30m&filter=active');
__setSearchParamsGetterForTest(() => currentSearchParams);
render(
<TestWrapper initialSearchParams="relativeTime=30m&filter=active">
<DateTimeSelection showAutoRefresh />
</TestWrapper>,
);
await waitFor(() => {
expect(screen.getByTestId('custom-time-picker')).toBeInTheDocument();
});
mockSafeNavigate.mockClear();
act(() => {
mockOnValidCustomDateChange?.({ timeStr: '2h' });
});
await waitFor(() => {
expect(mockSafeNavigate).toHaveBeenCalled();
});
const navigatedUrl = mockSafeNavigate.mock.calls[
mockSafeNavigate.mock.calls.length - 1
][0] as string;
expect(navigatedUrl).toContain('relativeTime=2h');
expect(navigatedUrl).toContain('filter=active');
expect(navigatedUrl).not.toContain('startTime=');
expect(navigatedUrl).not.toContain('endTime=');
});
});
});

View File

@@ -0,0 +1,95 @@
// eslint-disable-next-line no-restricted-imports
import { Provider } from 'react-redux';
import { MemoryRouter, Route } from 'react-router-dom';
import { NuqsTestingAdapter } from 'nuqs/adapters/testing';
import { QueryClient, QueryClientProvider } from 'react-query';
import { AppContext } from 'providers/App/App';
import TimezoneProvider from 'providers/Timezone';
import { QueryBuilderProvider } from 'providers/QueryBuilder';
import configureStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import store from 'store';
import { getAppContextMock } from 'tests/test-utils';
import { CompatRouter } from 'react-router-dom-v5-compat';
export const queryClient = new QueryClient({
defaultOptions: {
queries: { refetchOnWindowFocus: false, retry: false },
mutations: { retry: false },
},
});
export const mockStore = configureStore([thunk]);
interface WrapperProps {
children: React.ReactNode;
initialSearchParams?: string;
initialPath?: string;
onUrlUpdate?: (event: { searchParams: URLSearchParams }) => void;
}
export function TestWrapper({
children,
initialSearchParams = '',
initialPath = '/services',
onUrlUpdate,
}: WrapperProps): JSX.Element {
const initialEntry = initialSearchParams
? `${initialPath}?${initialSearchParams}`
: initialPath;
const mockedStore = mockStore({
...store.getState(),
app: {
...store.getState().app,
role: 'ADMIN',
user: {
userId: 'test-user-id',
email: 'test@signoz.io',
name: 'TestUser',
profilePictureURL: '',
accessJwt: '',
refreshJwt: '',
},
isLoggedIn: true,
},
});
return (
<MemoryRouter initialEntries={[initialEntry]}>
<CompatRouter>
<NuqsTestingAdapter
searchParams={initialSearchParams}
onUrlUpdate={onUrlUpdate}
>
<QueryClientProvider client={queryClient}>
<Provider store={mockedStore}>
<AppContext.Provider value={getAppContextMock('ADMIN')}>
<TimezoneProvider>
<QueryBuilderProvider>
<Route path="*">{children}</Route>
</QueryBuilderProvider>
</TimezoneProvider>
</AppContext.Provider>
</Provider>
</QueryClientProvider>
</NuqsTestingAdapter>
</CompatRouter>
</MemoryRouter>
);
}
export function createMockMoment(timestamp: number): {
toDate: () => Date;
toISOString: () => string;
format: () => string;
toString: () => string;
} {
const date = new Date(timestamp);
return {
toDate: (): Date => date,
toISOString: (): string => date.toISOString(),
format: (): string => date.toISOString(),
toString: (): string => date.toString(),
};
}

View File

@@ -2,7 +2,7 @@ import { useCallback, useEffect, useState } from 'react';
// eslint-disable-next-line no-restricted-imports
import { connect, useDispatch, useSelector } from 'react-redux';
import { RouteComponentProps, withRouter } from 'react-router-dom';
import { useNavigationType, useSearchParams } from 'react-router-dom-v5-compat';
import { useNavigationType } from 'react-router-dom-v5-compat';
import { RefreshCw, Undo } from '@signozhq/icons';
import { Button } from 'antd';
import getLocalStorageKey from 'api/browser/localstorage/get';
@@ -20,7 +20,6 @@ import {
} from 'store/globalTime';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useSafeNavigate } from 'hooks/useSafeNavigate';
import useUrlQuery from 'hooks/useUrlQuery';
import { isValidShortHandDateTimeFormat } from 'lib/getMinMax';
import getTimeString from 'lib/getTimeString';
import { cloneDeep, isObject } from 'lodash-es';
@@ -54,6 +53,7 @@ import {
Time,
TimeRange,
} from './types';
import { getUnstableCurrentSearchParams } from './utils/getUnstableCurrentSearchParams';
import './DateTimeSelectionV2.styles.scss';
@@ -90,10 +90,12 @@ function DateTimeSelection({
const [hasSelectedTimeError, setHasSelectedTimeError] = useState(false);
const [isOpen, setIsOpen] = useState<boolean>(false);
const urlQuery = useUrlQuery();
const searchStartTime = urlQuery.get('startTime');
const searchEndTime = urlQuery.get('endTime');
const relativeTimeFromUrl = urlQuery.get(QueryParams.relativeTime);
const currentSearchParams = getUnstableCurrentSearchParams();
const searchStartTime = currentSearchParams.get(QueryParams.startTime);
const searchEndTime = currentSearchParams.get(QueryParams.endTime);
const relativeTimeFromUrl = currentSearchParams.get(QueryParams.relativeTime);
const hasTimeParamsInUrl =
(searchStartTime && searchEndTime) || relativeTimeFromUrl;
// Prioritize props for initial modal time, fallback to URL params
let initialModalStartTime = 0;
@@ -115,8 +117,6 @@ function DateTimeSelection({
);
const [modalEndTime, setModalEndTime] = useState<number>(initialModalEndTime);
const [searchParams] = useSearchParams();
// Effect to update modal time state when props change
useEffect(() => {
if (modalInitialStartTime !== undefined) {
@@ -323,14 +323,15 @@ function DateTimeSelection({
return;
}
urlQuery.delete('startTime');
urlQuery.delete('endTime');
const urlQuery = getUnstableCurrentSearchParams();
urlQuery.delete(QueryParams.startTime);
urlQuery.delete(QueryParams.endTime);
urlQuery.set(QueryParams.relativeTime, value);
// Remove Hidden Filters from URL query parameters on time change
urlQuery.delete(QueryParams.activeLogId);
if (searchParams.has(QueryParams.compositeQuery)) {
if (urlQuery.has(QueryParams.compositeQuery)) {
const updatedCompositeQuery = getUpdatedCompositeQuery();
urlQuery.set(QueryParams.compositeQuery, updatedCompositeQuery);
}
@@ -349,8 +350,6 @@ function DateTimeSelection({
getUpdatedCompositeQuery,
updateLocalStorageForRoutes,
updateTimeInterval,
urlQuery,
searchParams,
],
);
@@ -414,6 +413,7 @@ function DateTimeSelection({
updateLocalStorageForRoutes(JSON.stringify({ startTime, endTime }));
const urlQuery = getUnstableCurrentSearchParams();
urlQuery.set(
QueryParams.startTime,
startTime?.toDate().getTime().toString(),
@@ -421,7 +421,7 @@ function DateTimeSelection({
urlQuery.set(QueryParams.endTime, endTime?.toDate().getTime().toString());
urlQuery.delete(QueryParams.relativeTime);
if (searchParams.has(QueryParams.compositeQuery)) {
if (urlQuery.has(QueryParams.compositeQuery)) {
const updatedCompositeQuery = getUpdatedCompositeQuery();
urlQuery.set(QueryParams.compositeQuery, updatedCompositeQuery);
}
@@ -441,8 +441,9 @@ function DateTimeSelection({
updateTimeInterval(dateTimeStr);
updateLocalStorageForRoutes(dateTimeStr);
urlQuery.delete('startTime');
urlQuery.delete('endTime');
const urlQuery = getUnstableCurrentSearchParams();
urlQuery.delete(QueryParams.startTime);
urlQuery.delete(QueryParams.endTime);
urlQuery.set(QueryParams.relativeTime, dateTimeStr);
@@ -595,13 +596,12 @@ function DateTimeSelection({
// set the default relative time for alert history and overview pages if relative time is not specified
if (
(!urlQuery.has(QueryParams.startTime) ||
!urlQuery.has(QueryParams.endTime)) &&
!urlQuery.has(QueryParams.relativeTime) &&
!hasTimeParamsInUrl &&
(currentRoute === ROUTES.ALERT_OVERVIEW ||
currentRoute === ROUTES.ALERT_HISTORY)
) {
updateTimeInterval(defaultRelativeTime);
const urlQuery = getUnstableCurrentSearchParams();
urlQuery.set(QueryParams.relativeTime, defaultRelativeTime);
const generatedUrl = `${location.pathname}?${urlQuery.toString()}`;
safeNavigate(generatedUrl);
@@ -625,9 +625,10 @@ function DateTimeSelection({
updateTimeInterval(updatedTime, [preStartTime, preEndTime]);
}
const urlQuery = getUnstableCurrentSearchParams();
if (updatedTime !== 'custom') {
urlQuery.delete('startTime');
urlQuery.delete('endTime');
urlQuery.delete(QueryParams.startTime);
urlQuery.delete(QueryParams.endTime);
urlQuery.set(QueryParams.relativeTime, updatedTime);
} else {
const startTime = preStartTime.toString();

View File

@@ -0,0 +1,34 @@
/**
* This was introduced to fix a sync bug between Nuqs and react-router-dom
*
* We are using the wrong adapter for nuqs because the correct one only supports v6/v7,
* and we are at version v5. This causes the nuqs/react-router-dom to be out of sync.
*
* We can revert this commit once we migrate react-router-dom to v6, or once we migrate
* to DateTimeSelectionV3
*/
/**
* This was created to help testing the regression introduced between nuqs/react-router-dom
*/
type SearchParamsGetter = () => URLSearchParams;
let getter: SearchParamsGetter = (): URLSearchParams =>
new URLSearchParams(window.location.search);
/**
* This function will return a fresh instance of URLSearchParams every time it's called.
*
* DO NOT USE IT FOR useEffect/useCallback dependencies, use Nuqs instead.
*/
export function getUnstableCurrentSearchParams(): URLSearchParams {
return getter();
}
// Testing helpers
export function __setSearchParamsGetterForTest(fn: SearchParamsGetter): void {
getter = fn;
}
export function __resetSearchParamsGetter(): void {
getter = (): URLSearchParams => new URLSearchParams(window.location.search);
}

View File

@@ -1,5 +1,7 @@
import { Input } from 'antd';
// TODO(@signozhq/ui-input): migrate this styled(Input) once @signozhq/ui
// Input supports `addonAfter` (the consumer renders `<InputComponent addonAfter="ms">`).
import { Typography } from '@signozhq/ui/typography';
import { Input } from 'antd';
import styled from 'styled-components';
export const DurationText = styled.div`

View File

@@ -8,7 +8,8 @@ import {
import { useQuery } from 'react-query';
// eslint-disable-next-line no-restricted-imports
import { useSelector } from 'react-redux';
import { AutoComplete, Input } from 'antd';
import { Input } from '@signozhq/ui/input';
import { AutoComplete } from 'antd';
import getTagFilters from 'api/trace/getTagFilter';
import { AppState } from 'store/reducers';
import { GlobalReducer } from 'types/reducer/globalTime';

View File

@@ -2,7 +2,8 @@ import { useMemo, useState } from 'react';
import { useQuery } from 'react-query';
// eslint-disable-next-line no-restricted-imports
import { useSelector } from 'react-redux';
import { AutoComplete, Input, Space } from 'antd';
import { Input } from '@signozhq/ui/input';
import { AutoComplete, Space } from 'antd';
import getTagFilters from 'api/trace/getTagFilter';
import { AppState } from 'store/reducers';
import { GlobalReducer } from 'types/reducer/globalTime';

View File

@@ -1,6 +1,7 @@
import { ChangeEvent, useEffect, useMemo, useState } from 'react';
import { ArrowLeft, Check, Loader, Plus, Search } from '@signozhq/icons';
import { Button, Input, Spin } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Button, Spin } from 'antd';
import cx from 'classnames';
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
import SignozModal from 'components/SignozModal/SignozModal';

View File

@@ -1,4 +1,4 @@
import { Input } from 'antd';
import { Input } from '@signozhq/ui/input';
import styled from 'styled-components';
export const InputComponent = styled(Input)`

View File

@@ -0,0 +1,8 @@
function DashboardPageV2(): JSX.Element {
return (
<div>
<h1>Dashboard Page V2</h1>
</div>
);
}
export default DashboardPageV2;

View File

@@ -0,0 +1,9 @@
function DashboardsListPageV2(): JSX.Element {
return (
<div>
<h1>Dashboards List Page V2</h1>
</div>
);
}
export default DashboardsListPageV2;

View File

@@ -1,5 +1,6 @@
import { Dispatch, SetStateAction, useEffect, useState } from 'react';
import { Input, Select } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Select } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import './DropRateView.styles.scss';

View File

@@ -3,7 +3,8 @@ import { useTranslation } from 'react-i18next';
import { useLocation } from 'react-router-dom';
import { Color } from '@signozhq/design-tokens';
import { Button } from '@signozhq/ui/button';
import { ColorPicker, Input, Modal, Table, TableProps } from 'antd';
import { Input } from '@signozhq/ui/input';
import { ColorPicker, Modal, Table, TableProps } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import logEvent from 'api/common/logEvent';
import {

View File

@@ -1,4 +1,5 @@
import { Checkbox, Input, Select, Skeleton } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Checkbox, Select, Skeleton } from 'antd';
import { Button } from '@signozhq/ui/button';
import { Typography } from '@signozhq/ui/typography';
import cx from 'classnames';

View File

@@ -1,6 +1,7 @@
import { ChangeEvent, useEffect, useMemo, useState } from 'react';
import { Button } from '@signozhq/ui/button';
import { Input, Spin } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Spin } from 'antd';
import cx from 'classnames';
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
import SignozModal from 'components/SignozModal/SignozModal';

View File

@@ -1,6 +1,6 @@
import { useState } from 'react';
import { useQueryClient } from 'react-query';
import { Input } from 'antd';
import { Input } from '@signozhq/ui/input';
import SignozModal from 'components/SignozModal/SignozModal';
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
import { useRenameFunnel } from 'hooks/TracesFunnels/useFunnels';

View File

@@ -1,6 +1,7 @@
import { ChangeEvent } from 'react';
import { Color } from '@signozhq/design-tokens';
import { Button, Input, Popover, Tooltip } from 'antd';
import { Input } from '@signozhq/ui/input';
import { Button, Popover, Tooltip } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import { ArrowDownWideNarrow, Check, Plus, Search } from '@signozhq/icons';
import { useAppContext } from 'providers/App/App';

View File

@@ -0,0 +1,50 @@
package impllogspipeline
import (
"context"
"github.com/SigNoz/signoz/pkg/modules/logspipeline"
"github.com/SigNoz/signoz/pkg/sqlstore"
"github.com/SigNoz/signoz/pkg/valuer"
)
const elementType = "log_pipelines"
type module struct {
sqlStore sqlstore.SQLStore
}
func NewModule(sqlStore sqlstore.SQLStore) logspipeline.Module {
return &module{sqlStore: sqlStore}
}
func (m *module) Collect(ctx context.Context, orgID valuer.UUID) (map[string]any, error) {
subq := m.sqlStore.BunDB().NewSelect().
TableExpr("agent_config_version").
ColumnExpr("MAX(version)").
Where("org_id = ?", orgID).
Where("element_type = ?", elementType)
var result struct {
Total int `bun:"total"`
Enabled int `bun:"enabled_count"`
}
err := m.sqlStore.BunDB().NewSelect().
TableExpr("agent_config_element AS e").
Join("JOIN agent_config_version AS v ON v.id = e.version_id").
Join("JOIN pipelines AS p ON p.id = e.element_id").
Where("v.org_id = ?", orgID).
Where("v.element_type = ?", elementType).
Where("v.version = (?)", subq).
ColumnExpr("COUNT(*) AS total").
ColumnExpr("SUM(CASE WHEN p.enabled THEN 1 ELSE 0 END) AS enabled_count").
Scan(ctx, &result)
if err != nil {
return nil, err
}
return map[string]any{
"logs_pipeline.total.count": result.Total,
"logs_pipeline.enabled.count": result.Enabled,
}, nil
}

View File

@@ -0,0 +1,7 @@
package logspipeline
import "github.com/SigNoz/signoz/pkg/statsreporter"
type Module interface {
statsreporter.StatsCollector
}

View File

@@ -7,8 +7,6 @@ import (
"strings"
"unicode"
"github.com/SigNoz/signoz/pkg/query-service/constants"
"encoding/base64"
"encoding/json"
"fmt"
@@ -178,27 +176,6 @@ func HydrateFileUris(spec interface{}, fs embed.FS, basedir string) (interface{}
if specMap, ok := spec.(map[string]interface{}); ok {
result := map[string]interface{}{}
for k, v := range specMap {
// Check if this is a dashboards slice and if dot metrics are enabled
if k == "dashboards" && constants.IsDotMetricsEnabled {
if dashboards, ok := v.([]interface{}); ok {
for i, dashboard := range dashboards {
if dashboardUri, ok := dashboard.(string); ok {
if strings.HasPrefix(dashboardUri, "file://") {
dashboards[i] = strings.Replace(dashboardUri, ".json", "_dot.json", 1)
}
} else if dashBoardMap, ok := dashboard.(map[string]interface{}); ok {
if dashboardUri, ok := dashBoardMap["definition"].(string); ok {
if strings.HasPrefix(dashboardUri, "file://") {
dashboardUri = strings.Replace(dashboardUri, ".json", "_dot.json", 1)
}
dashBoardMap["definition"] = dashboardUri
}
dashboards[i] = dashBoardMap
}
}
v = dashboards
}
}
hydrated, err := HydrateFileUris(v, fs, basedir)
if err != nil {
return nil, err

View File

@@ -367,9 +367,9 @@
"id": "31d3f13b-27d5-4291-9fb3-d5d5708a72f3",
"modificationUUID": "e4a9edf1-acd0-48b7-8a35-1b4bb668408d",
"multiSelect": false,
"name": "mysql_instance_endpoint",
"name": "mysql.instance.endpoint",
"order": 0,
"queryValue": "SELECT JSONExtractString(labels, 'mysql_instance_endpoint') as mysql_instance_endpoint\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'mysql_uptime'\nGROUP BY mysql_instance_endpoint",
"queryValue": "SELECT JSONExtractString(labels, 'mysql.instance.endpoint') as `mysql.instance.endpoint`\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'mysql.uptime'\nGROUP BY `mysql.instance.endpoint`",
"selectedValue": "",
"showALLOption": false,
"sort": "ASC",
@@ -393,7 +393,7 @@
{
"aggregations": [
{
"metricName": "mysql_buffer_pool_usage",
"metricName": "mysql.buffer_pool.pages",
"reduceTo": "avg",
"spaceAggregation": "avg",
"temporality": null,
@@ -404,7 +404,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
},
"functions": [],
"groupBy": [
@@ -529,7 +529,7 @@
{
"aggregations": [
{
"metricName": "mysql_buffer_pool_pages",
"metricName": "mysql.buffer_pool.pages",
"reduceTo": "avg",
"spaceAggregation": "sum",
"temporality": null,
@@ -540,7 +540,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
},
"functions": [],
"groupBy": [
@@ -658,7 +658,7 @@
{
"aggregations": [
{
"metricName": "mysql_buffer_pool_data_pages",
"metricName": "mysql.buffer_pool.data_pages",
"reduceTo": "avg",
"spaceAggregation": "sum",
"temporality": null,
@@ -669,7 +669,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
},
"functions": [],
"groupBy": [
@@ -788,7 +788,7 @@
{
"aggregations": [
{
"metricName": "mysql_buffer_pool_page_flushes",
"metricName": "mysql.buffer_pool.page_flushes",
"reduceTo": "avg",
"spaceAggregation": "sum",
"temporality": null,
@@ -799,7 +799,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
},
"functions": [],
"groupBy": [],
@@ -915,7 +915,7 @@
{
"aggregations": [
{
"metricName": "mysql_table_io_wait_count",
"metricName": "mysql.table.io.wait.count",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
@@ -926,7 +926,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
},
"functions": [],
"groupBy": [
@@ -1045,7 +1045,7 @@
{
"aggregations": [
{
"metricName": "mysql_table_io_wait_count",
"metricName": "mysql.table.io.wait.count",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
@@ -1056,7 +1056,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
},
"functions": [],
"groupBy": [
@@ -1175,7 +1175,7 @@
{
"aggregations": [
{
"metricName": "mysql_table_io_wait_count",
"metricName": "mysql.table.io.wait.count",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
@@ -1186,7 +1186,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
},
"functions": [],
"groupBy": [
@@ -1305,7 +1305,7 @@
{
"aggregations": [
{
"metricName": "mysql_handlers",
"metricName": "mysql.handlers",
"reduceTo": "avg",
"spaceAggregation": "sum",
"temporality": null,
@@ -1316,7 +1316,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
},
"functions": [],
"groupBy": [
@@ -1329,7 +1329,7 @@
}
],
"having": {
"expression": "sum(mysql_handlers) \u003e 0"
"expression": "sum(mysql.handlers) \u003e 0"
},
"legend": "{{kind}}",
"limit": null,
@@ -1434,7 +1434,7 @@
{
"aggregations": [
{
"metricName": "mysql_locks",
"metricName": "mysql.locks",
"reduceTo": "avg",
"spaceAggregation": "sum",
"temporality": null,
@@ -1445,7 +1445,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
},
"functions": [],
"groupBy": [
@@ -1563,7 +1563,7 @@
{
"aggregations": [
{
"metricName": "mysql_log_operations",
"metricName": "mysql.log_operations",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
@@ -1574,7 +1574,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
},
"functions": [],
"groupBy": [
@@ -1693,7 +1693,7 @@
{
"aggregations": [
{
"metricName": "mysql_connection_count",
"metricName": "mysql.connection.count",
"reduceTo": "avg",
"spaceAggregation": "sum",
"temporality": null,
@@ -1704,7 +1704,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
},
"functions": [],
"groupBy": [],
@@ -1720,7 +1720,7 @@
{
"aggregations": [
{
"metricName": "mysql_connection_errors",
"metricName": "mysql.connection.errors",
"reduceTo": "avg",
"spaceAggregation": "sum",
"temporality": null,
@@ -1731,7 +1731,7 @@
"disabled": false,
"expression": "B",
"filter": {
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
},
"functions": [],
"groupBy": [
@@ -1745,7 +1745,7 @@
}
],
"having": {
"expression": "sum(mysql_connection_errors) \u003e 0"
"expression": "sum(mysql.connection.errors) \u003e 0"
},
"legend": "Error count: {{error}}",
"limit": null,
@@ -1850,7 +1850,7 @@
{
"aggregations": [
{
"metricName": "mysql_opened_resources",
"metricName": "mysql.opened_resources",
"reduceTo": "avg",
"spaceAggregation": "sum",
"temporality": null,
@@ -1861,7 +1861,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
},
"functions": [],
"groupBy": [
@@ -1874,7 +1874,7 @@
}
],
"having": {
"expression": "sum(mysql_opened_resources) \u003e 0"
"expression": "sum(mysql.opened_resources) \u003e 0"
},
"legend": "{{kind}}",
"limit": null,
@@ -1979,7 +1979,7 @@
{
"aggregations": [
{
"metricName": "mysql_operations",
"metricName": "mysql.operations",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
@@ -1990,7 +1990,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
},
"functions": [],
"groupBy": [
@@ -2109,7 +2109,7 @@
{
"aggregations": [
{
"metricName": "mysql_row_locks",
"metricName": "mysql.row_locks",
"reduceTo": "avg",
"spaceAggregation": "sum",
"temporality": null,
@@ -2120,7 +2120,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
},
"functions": [],
"groupBy": [
@@ -2238,7 +2238,7 @@
{
"aggregations": [
{
"metricName": "mysql_threads",
"metricName": "mysql.threads",
"reduceTo": "avg",
"spaceAggregation": "sum",
"temporality": null,
@@ -2249,7 +2249,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
},
"functions": [],
"groupBy": [
@@ -2367,7 +2367,7 @@
{
"aggregations": [
{
"metricName": "mysql_row_operations",
"metricName": "mysql.row_operations",
"reduceTo": "avg",
"spaceAggregation": "sum",
"temporality": null,
@@ -2378,7 +2378,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
},
"functions": [],
"groupBy": [
@@ -2497,7 +2497,7 @@
{
"aggregations": [
{
"metricName": "mysql_prepared_statements",
"metricName": "mysql.prepared_statements",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
@@ -2508,7 +2508,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "mysql_instance_endpoint = $mysql_instance_endpoint"
"expression": "mysql.instance.endpoint = $mysql.instance.endpoint"
},
"functions": [],
"groupBy": [

View File

@@ -287,7 +287,7 @@
"multiSelect": true,
"name": "table_name",
"order": 0,
"queryValue": "SELECT JSONExtractString(labels, 'postgresql_table_name') AS table_name\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'postgresql_operations' AND JSONExtractString(labels, 'postgresql_database_name') IN {{.db_name}}\nGROUP BY table_name",
"queryValue": "SELECT JSONExtractString(labels, 'postgresql.table.name') AS table_name\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'postgresql.operations' AND JSONExtractString(labels, 'postgresql.database.name') IN {{.db_name}}\nGROUP BY table_name",
"selectedValue": [
"public.pgbench_accounts",
"public.pgbench_branches",
@@ -309,7 +309,7 @@
"multiSelect": true,
"name": "db_name",
"order": 0,
"queryValue": "SELECT JSONExtractString(labels, 'postgresql_database_name') AS db_name\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'postgresql_operations'\nGROUP BY db_name",
"queryValue": "SELECT JSONExtractString(labels, 'postgresql.database.name') AS db_name\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'postgresql.operations'\nGROUP BY db_name",
"selectedValue": [
"pgtestdb"
],
@@ -335,7 +335,7 @@
{
"aggregations": [
{
"metricName": "postgresql_operations",
"metricName": "postgresql.operations",
"reduceTo": "sum",
"spaceAggregation": "avg",
"temporality": null,
@@ -346,22 +346,22 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "(operation = 'ins' AND postgresql_database_name IN $db_name)"
"expression": "(operation = 'ins' AND postgresql.database.name IN $db_name)"
},
"groupBy": [
{
"dataType": "string",
"id": "postgresql_database_name--string--tag--false",
"id": "postgresql.database.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "postgresql_database_name",
"key": "postgresql.database.name",
"type": "tag"
}
],
"having": {
"expression": ""
},
"legend": "{{postgresql_database_name}}",
"legend": "{{postgresql.database.name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
@@ -410,7 +410,7 @@
{
"aggregations": [
{
"metricName": "postgresql_operations",
"metricName": "postgresql.operations",
"reduceTo": "sum",
"spaceAggregation": "avg",
"temporality": null,
@@ -421,22 +421,22 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "(operation = 'upd' AND postgresql_database_name IN $db_name)"
"expression": "(operation = 'upd' AND postgresql.database.name IN $db_name)"
},
"groupBy": [
{
"dataType": "string",
"id": "postgresql_database_name--string--tag--false",
"id": "postgresql.database.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "postgresql_database_name",
"key": "postgresql.database.name",
"type": "tag"
}
],
"having": {
"expression": ""
},
"legend": "{{postgresql_database_name}}",
"legend": "{{postgresql.database.name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
@@ -485,7 +485,7 @@
{
"aggregations": [
{
"metricName": "postgresql_operations",
"metricName": "postgresql.operations",
"reduceTo": "sum",
"spaceAggregation": "avg",
"temporality": null,
@@ -496,22 +496,22 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "(operation = 'del' AND postgresql_database_name IN $db_name)"
"expression": "(operation = 'del' AND postgresql.database.name IN $db_name)"
},
"groupBy": [
{
"dataType": "string",
"id": "postgresql_database_name--string--tag--false",
"id": "postgresql.database.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "postgresql_database_name",
"key": "postgresql.database.name",
"type": "tag"
}
],
"having": {
"expression": ""
},
"legend": "{{postgresql_database_name}}",
"legend": "{{postgresql.database.name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
@@ -560,7 +560,7 @@
{
"aggregations": [
{
"metricName": "postgresql_operations",
"metricName": "postgresql.operations",
"reduceTo": "sum",
"spaceAggregation": "avg",
"temporality": null,
@@ -571,22 +571,22 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "(operation = 'hot_upd' AND postgresql_database_name IN $db_name)"
"expression": "(operation = 'hot_upd' AND postgresql.database.name IN $db_name)"
},
"groupBy": [
{
"dataType": "string",
"id": "postgresql_database_name--string--tag--false",
"id": "postgresql.database.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "postgresql_database_name",
"key": "postgresql.database.name",
"type": "tag"
}
],
"having": {
"expression": ""
},
"legend": "{{postgresql_database_name}}",
"legend": "{{postgresql.database.name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
@@ -635,7 +635,7 @@
{
"aggregations": [
{
"metricName": "postgresql_operations",
"metricName": "postgresql.operations",
"reduceTo": "avg",
"spaceAggregation": "avg",
"temporality": null,
@@ -646,15 +646,15 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "(operation = 'ins' AND postgresql_database_name IN $db_name)"
"expression": "(operation = 'ins' AND postgresql.database.name IN $db_name)"
},
"groupBy": [
{
"dataType": "string",
"id": "postgresql_database_name--string--tag--false",
"id": "postgresql.database.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "postgresql_database_name",
"key": "postgresql.database.name",
"type": "tag"
}
],
@@ -670,7 +670,7 @@
{
"aggregations": [
{
"metricName": "postgresql_operations",
"metricName": "postgresql.operations",
"reduceTo": "avg",
"spaceAggregation": "avg",
"temporality": null,
@@ -681,15 +681,15 @@
"disabled": false,
"expression": "B",
"filter": {
"expression": "(operation = 'upd' AND postgresql_database_name IN $db_name)"
"expression": "(operation = 'upd' AND postgresql.database.name IN $db_name)"
},
"groupBy": [
{
"dataType": "string",
"id": "postgresql_database_name--string--tag--false",
"id": "postgresql.database.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "postgresql_database_name",
"key": "postgresql.database.name",
"type": "tag"
}
],
@@ -705,7 +705,7 @@
{
"aggregations": [
{
"metricName": "postgresql_operations",
"metricName": "postgresql.operations",
"reduceTo": "avg",
"spaceAggregation": "avg",
"temporality": null,
@@ -716,15 +716,15 @@
"disabled": false,
"expression": "C",
"filter": {
"expression": "(operation = 'del' AND postgresql_database_name IN $db_name)"
"expression": "(operation = 'del' AND postgresql.database.name IN $db_name)"
},
"groupBy": [
{
"dataType": "string",
"id": "postgresql_database_name--string--tag--false",
"id": "postgresql.database.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "postgresql_database_name",
"key": "postgresql.database.name",
"type": "tag"
}
],
@@ -780,7 +780,7 @@
{
"aggregations": [
{
"metricName": "postgresql_database_locks",
"metricName": "postgresql.database.locks",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
@@ -852,7 +852,7 @@
{
"aggregations": [
{
"metricName": "postgresql_deadlocks",
"metricName": "postgresql.deadlocks",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
@@ -863,22 +863,22 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "postgresql_database_name IN $db_name"
"expression": "postgresql.database.name IN $db_name"
},
"groupBy": [
{
"dataType": "string",
"id": "postgresql_database_name--string--tag--false",
"id": "postgresql.database.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "postgresql_database_name",
"key": "postgresql.database.name",
"type": "tag"
}
],
"having": {
"expression": ""
},
"legend": "{{postgresql_database_name}}",
"legend": "{{postgresql.database.name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
@@ -927,7 +927,7 @@
{
"aggregations": [
{
"metricName": "postgresql_backends",
"metricName": "postgresql.backends",
"reduceTo": "sum",
"spaceAggregation": "avg",
"temporality": null,
@@ -938,22 +938,22 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "postgresql_database_name IN $db_name"
"expression": "postgresql.database.name IN $db_name"
},
"groupBy": [
{
"dataType": "string",
"id": "postgresql_database_name--string--tag--false",
"id": "postgresql.database.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "postgresql_database_name",
"key": "postgresql.database.name",
"type": "tag"
}
],
"having": {
"expression": ""
},
"legend": "{{postgresql_database_name}}",
"legend": "{{postgresql.database.name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
@@ -1002,7 +1002,7 @@
{
"aggregations": [
{
"metricName": "postgresql_rows",
"metricName": "postgresql.rows",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
@@ -1013,7 +1013,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "(state = 'dead' AND postgresql_database_name IN $db_name AND postgresql_table_name IN $table_name)"
"expression": "(state = 'dead' AND postgresql.database.name IN $db_name AND postgresql.table.name IN $table_name)"
},
"groupBy": [],
"having": {
@@ -1068,7 +1068,7 @@
{
"aggregations": [
{
"metricName": "postgresql_index_scans",
"metricName": "postgresql.index.scans",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
@@ -1079,22 +1079,22 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "(postgresql_database_name IN $db_name AND postgresql_table_name IN $table_name)"
"expression": "(postgresql.database.name IN $db_name AND postgresql.table.name IN $table_name)"
},
"groupBy": [
{
"dataType": "string",
"id": "postgresql_index_name--string--tag--false",
"id": "postgresql.index.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "postgresql_index_name",
"key": "postgresql.index.name",
"type": "tag"
}
],
"having": {
"expression": ""
},
"legend": "{{postgresql_index_name}}",
"legend": "{{postgresql.index.name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
@@ -1143,7 +1143,7 @@
{
"aggregations": [
{
"metricName": "postgresql_rows",
"metricName": "postgresql.rows",
"reduceTo": "avg",
"spaceAggregation": "avg",
"temporality": null,
@@ -1154,16 +1154,16 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "(state = 'dead' AND postgresql_database_name IN $db_name AND postgresql_table_name IN $table_name)"
"expression": "(state = 'dead' AND postgresql.database.name IN $db_name AND postgresql.table.name IN $table_name)"
},
"functions": [],
"groupBy": [
{
"dataType": "string",
"id": "postgresql_table_name--string--tag--false",
"id": "postgresql.table.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "postgresql_table_name",
"key": "postgresql.table.name",
"type": "tag"
}
],
@@ -1179,7 +1179,7 @@
{
"aggregations": [
{
"metricName": "postgresql_rows",
"metricName": "postgresql.rows",
"reduceTo": "avg",
"spaceAggregation": "avg",
"temporality": null,
@@ -1190,16 +1190,16 @@
"disabled": false,
"expression": "B",
"filter": {
"expression": "(state = 'live' AND postgresql_database_name IN $db_name AND postgresql_table_name IN $table_name)"
"expression": "(state = 'live' AND postgresql.database.name IN $db_name AND postgresql.table.name IN $table_name)"
},
"functions": [],
"groupBy": [
{
"dataType": "string",
"id": "postgresql_table_name--string--tag--false",
"id": "postgresql.table.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "postgresql_table_name",
"key": "postgresql.table.name",
"type": "tag"
}
],
@@ -1215,7 +1215,7 @@
{
"aggregations": [
{
"metricName": "postgresql_index_scans",
"metricName": "postgresql.index.scans",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
@@ -1226,16 +1226,16 @@
"disabled": false,
"expression": "C",
"filter": {
"expression": "(postgresql_database_name IN $db_name AND postgresql_table_name IN $table_name)"
"expression": "(postgresql.database.name IN $db_name AND postgresql.table.name IN $table_name)"
},
"functions": [],
"groupBy": [
{
"dataType": "string",
"id": "postgresql_table_name--string--tag--false",
"id": "postgresql.table.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "postgresql_table_name",
"key": "postgresql.table.name",
"type": "tag"
}
],
@@ -1251,7 +1251,7 @@
{
"aggregations": [
{
"metricName": "postgresql_table_size",
"metricName": "postgresql.table.size",
"reduceTo": "avg",
"spaceAggregation": "avg",
"temporality": null,
@@ -1262,16 +1262,16 @@
"disabled": true,
"expression": "D",
"filter": {
"expression": "(postgresql_database_name IN $db_name AND postgresql_table_name IN $table_name)"
"expression": "(postgresql.database.name IN $db_name AND postgresql.table.name IN $table_name)"
},
"functions": [],
"groupBy": [
{
"dataType": "string",
"id": "postgresql_table_name--string--tag--false",
"id": "postgresql.table.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "postgresql_table_name",
"key": "postgresql.table.name",
"type": "tag"
}
],

View File

@@ -80,9 +80,9 @@
"id": "a2c21714-a814-4d31-9b56-7367c3208801",
"modificationUUID": "448e675a-4531-45b1-b434-a9ee809470d6",
"multiSelect": true,
"name": "host_name",
"name": "host.name",
"order": 0,
"queryValue": "SELECT JSONExtractString(labels, 'host_name') AS host_name\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'mongodb_memory_usage'\nGROUP BY host_name",
"queryValue": "SELECT JSONExtractString(labels, 'host.name') AS `host.name`\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'mongodb_memory_usage'\nGROUP BY `host.name`",
"selectedValue": [
"Srikanths-MacBook-Pro.local"
],
@@ -108,7 +108,7 @@
{
"aggregations": [
{
"metricName": "mongodb_operation_count",
"metricName": "mongodb.operation.count",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
@@ -119,7 +119,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "host_name IN $host_name"
"expression": "host.name IN $host.name"
},
"groupBy": [
{
@@ -183,7 +183,7 @@
{
"aggregations": [
{
"metricName": "mongodb_operation_time",
"metricName": "mongodb.operation.time",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
@@ -194,7 +194,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "host_name IN $host_name"
"expression": "host.name IN $host.name"
},
"groupBy": [
{
@@ -258,7 +258,7 @@
{
"aggregations": [
{
"metricName": "mongodb_cache_operations",
"metricName": "mongodb.cache.operations",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
@@ -269,7 +269,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "host_name IN $host_name"
"expression": "host.name IN $host.name"
},
"groupBy": [
{
@@ -333,7 +333,7 @@
{
"aggregations": [
{
"metricName": "mongodb_operation_latency_time",
"metricName": "mongodb.operation.latency.time",
"reduceTo": "sum",
"spaceAggregation": "max",
"temporality": null,
@@ -344,7 +344,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "(operation = 'read' AND host_name IN $host_name)"
"expression": "(operation = 'read' AND host.name IN $host.name)"
},
"groupBy": [],
"having": {
@@ -399,7 +399,7 @@
{
"aggregations": [
{
"metricName": "mongodb_operation_latency_time",
"metricName": "mongodb.operation.latency.time",
"reduceTo": "sum",
"spaceAggregation": "max",
"temporality": null,
@@ -410,7 +410,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "(host_name IN $host_name AND operation = 'write')"
"expression": "(host.name IN $host.name AND operation = 'write')"
},
"groupBy": [],
"having": {
@@ -465,7 +465,7 @@
{
"aggregations": [
{
"metricName": "mongodb_operation_latency_time",
"metricName": "mongodb.operation.latency.time",
"reduceTo": "sum",
"spaceAggregation": "max",
"temporality": null,
@@ -476,7 +476,7 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "(host_name IN $host_name AND operation = 'command')"
"expression": "(host.name IN $host.name AND operation = 'command')"
},
"groupBy": [],
"having": {
@@ -531,7 +531,7 @@
{
"aggregations": [
{
"metricName": "mongodb_network_io_receive",
"metricName": "mongodb.network.io.receive",
"reduceTo": "sum",
"spaceAggregation": "avg",
"temporality": null,
@@ -542,22 +542,22 @@
"disabled": false,
"expression": "A",
"filter": {
"expression": "host_name IN $host_name"
"expression": "host.name IN $host.name"
},
"groupBy": [
{
"dataType": "string",
"id": "host_name--string--tag--false",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host_name",
"key": "host.name",
"type": "tag"
}
],
"having": {
"expression": ""
},
"legend": "Bytes received :: {{host_name}}",
"legend": "Bytes received :: {{host.name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
@@ -566,7 +566,7 @@
{
"aggregations": [
{
"metricName": "mongodb_network_io_transmit",
"metricName": "mongodb.network.io.transmit",
"reduceTo": "sum",
"spaceAggregation": "avg",
"temporality": null,
@@ -577,22 +577,22 @@
"disabled": false,
"expression": "B",
"filter": {
"expression": "host_name IN $host_name"
"expression": "host.name IN $host.name"
},
"groupBy": [
{
"dataType": "string",
"id": "host_name--string--tag--false",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host_name",
"key": "host.name",
"type": "tag"
}
],
"having": {
"expression": ""
},
"legend": "Bytes transmitted :: {{host_name}}",
"legend": "Bytes transmitted :: {{host.name}}",
"limit": null,
"orderBy": [],
"queryName": "B",

View File

@@ -1,631 +0,0 @@
{
"description": "This dashboard provides a high-level overview of your MongoDB. It includes read/write performance, most-used replicas, collection metrics etc...",
"id": "mongo-overview",
"layout": [
{
"h": 3,
"i": "0c3d2b15-89be-4d62-a821-b26d93332ed3",
"moved": false,
"static": false,
"w": 6,
"x": 6,
"y": 3
},
{
"h": 3,
"i": "14504a3c-4a05-4d22-bab3-e22e94f51380",
"moved": false,
"static": false,
"w": 6,
"x": 0,
"y": 6
},
{
"h": 3,
"i": "dcfb3829-c3f2-44bb-907d-8dc8a6dc4aab",
"moved": false,
"static": false,
"w": 6,
"x": 0,
"y": 3
},
{
"h": 3,
"i": "bfc9e80b-02bf-4122-b3da-3dd943d35012",
"moved": false,
"static": false,
"w": 6,
"x": 6,
"y": 0
},
{
"h": 3,
"i": "4c07a7d2-893a-46c2-bcdb-a19b6efeac3a",
"moved": false,
"static": false,
"w": 6,
"x": 0,
"y": 0
},
{
"h": 3,
"i": "a5a64eec-1034-4aa6-8cb1-05673c4426c6",
"moved": false,
"static": false,
"w": 6,
"x": 6,
"y": 6
},
{
"h": 3,
"i": "503af589-ef4d-4fe3-8934-c8f7eb480d9a",
"moved": false,
"static": false,
"w": 6,
"x": 0,
"y": 9
}
],
"name": "",
"tags": [
"mongo",
"database"
],
"title": "Mongo overview",
"variables": {
"a2c21714-a814-4d31-9b56-7367c3208801": {
"allSelected": true,
"customValue": "",
"description": "List of hosts sending mongo metrics",
"id": "a2c21714-a814-4d31-9b56-7367c3208801",
"modificationUUID": "448e675a-4531-45b1-b434-a9ee809470d6",
"multiSelect": true,
"name": "host.name",
"order": 0,
"queryValue": "SELECT JSONExtractString(labels, 'host.name') AS `host.name`\nFROM signoz_metrics.distributed_time_series_v4_1day\nWHERE metric_name = 'mongodb_memory_usage'\nGROUP BY `host.name`",
"selectedValue": [
"Srikanths-MacBook-Pro.local"
],
"showALLOption": true,
"sort": "ASC",
"textboxValue": "",
"type": "QUERY"
}
},
"version": "v5",
"widgets": [
{
"description": "Total number of operations",
"fillSpans": false,
"id": "4c07a7d2-893a-46c2-bcdb-a19b6efeac3a",
"isStacked": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "mongodb.operation.count",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
"timeAggregation": "rate"
}
],
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "host.name IN $host.name"
},
"groupBy": [
{
"dataType": "string",
"id": "operation--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "operation",
"type": "tag"
}
],
"having": {
"expression": ""
},
"legend": "{{operation}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"stepInterval": 60
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "7da5d899-8b06-4139-9a89-47baf9551ff8",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"softMax": null,
"softMin": null,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Operations count",
"yAxisUnit": "none"
},
{
"description": "The total time spent performing operations.",
"fillSpans": false,
"id": "bfc9e80b-02bf-4122-b3da-3dd943d35012",
"isStacked": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "mongodb.operation.time",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
"timeAggregation": "rate"
}
],
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "host.name IN $host.name"
},
"groupBy": [
{
"dataType": "string",
"id": "operation--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "operation",
"type": "tag"
}
],
"having": {
"expression": ""
},
"legend": "{{operation}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"stepInterval": 60
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "2ca35957-894a-46ae-a2a6-95d7e400d8e1",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"softMax": null,
"softMin": null,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Total operations time",
"yAxisUnit": "ms"
},
{
"description": "The number of cache operations",
"fillSpans": false,
"id": "dcfb3829-c3f2-44bb-907d-8dc8a6dc4aab",
"isStacked": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "mongodb.cache.operations",
"reduceTo": "sum",
"spaceAggregation": "sum",
"temporality": null,
"timeAggregation": "rate"
}
],
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "host.name IN $host.name"
},
"groupBy": [
{
"dataType": "string",
"id": "type--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "type",
"type": "tag"
}
],
"having": {
"expression": ""
},
"legend": "{{type}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"stepInterval": 60
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "bb439198-dcf5-4767-b0d0-ab5785159b8d",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"softMax": null,
"softMin": null,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Cache operations",
"yAxisUnit": "none"
},
{
"description": "",
"fillSpans": false,
"id": "14504a3c-4a05-4d22-bab3-e22e94f51380",
"isStacked": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "mongodb.operation.latency.time",
"reduceTo": "sum",
"spaceAggregation": "max",
"temporality": null,
"timeAggregation": "max"
}
],
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "(operation = 'read' AND host.name IN $host.name)"
},
"groupBy": [],
"having": {
"expression": ""
},
"legend": "Latency",
"limit": null,
"orderBy": [],
"queryName": "A",
"stepInterval": 60
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "4a9cafe8-778b-476c-b825-c04e165bf285",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"softMax": null,
"softMin": null,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Read latency",
"yAxisUnit": "µs"
},
{
"description": "",
"fillSpans": false,
"id": "a5a64eec-1034-4aa6-8cb1-05673c4426c6",
"isStacked": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "mongodb.operation.latency.time",
"reduceTo": "sum",
"spaceAggregation": "max",
"temporality": null,
"timeAggregation": "max"
}
],
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "(host.name IN $host.name AND operation = 'write')"
},
"groupBy": [],
"having": {
"expression": ""
},
"legend": "Latency",
"limit": null,
"orderBy": [],
"queryName": "A",
"stepInterval": 60
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "446827eb-a4f2-4ff3-966b-fb65288c983b",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"softMax": null,
"softMin": null,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Write latency",
"yAxisUnit": "µs"
},
{
"description": "",
"fillSpans": false,
"id": "503af589-ef4d-4fe3-8934-c8f7eb480d9a",
"isStacked": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "mongodb.operation.latency.time",
"reduceTo": "sum",
"spaceAggregation": "max",
"temporality": null,
"timeAggregation": "max"
}
],
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "(host.name IN $host.name AND operation = 'command')"
},
"groupBy": [],
"having": {
"expression": ""
},
"legend": "Latency",
"limit": null,
"orderBy": [],
"queryName": "A",
"stepInterval": 60
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "7b7b977d-0921-4552-8cfe-d82dfde63ef4",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"softMax": null,
"softMin": null,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Command latency",
"yAxisUnit": "µs"
},
{
"description": "",
"fillSpans": false,
"id": "0c3d2b15-89be-4d62-a821-b26d93332ed3",
"isStacked": false,
"nullZeroValues": "zero",
"opacity": "1",
"panelTypes": "graph",
"query": {
"builder": {
"queryData": [
{
"aggregations": [
{
"metricName": "mongodb.network.io.receive",
"reduceTo": "sum",
"spaceAggregation": "avg",
"temporality": null,
"timeAggregation": "avg"
}
],
"dataSource": "metrics",
"disabled": false,
"expression": "A",
"filter": {
"expression": "host.name IN $host.name"
},
"groupBy": [
{
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
}
],
"having": {
"expression": ""
},
"legend": "Bytes received :: {{host.name}}",
"limit": null,
"orderBy": [],
"queryName": "A",
"stepInterval": 60
},
{
"aggregations": [
{
"metricName": "mongodb.network.io.transmit",
"reduceTo": "sum",
"spaceAggregation": "avg",
"temporality": null,
"timeAggregation": "avg"
}
],
"dataSource": "metrics",
"disabled": false,
"expression": "B",
"filter": {
"expression": "host.name IN $host.name"
},
"groupBy": [
{
"dataType": "string",
"id": "host.name--string--tag--false",
"isColumn": false,
"isJSON": false,
"key": "host.name",
"type": "tag"
}
],
"having": {
"expression": ""
},
"legend": "Bytes transmitted :: {{host.name}}",
"limit": null,
"orderBy": [],
"queryName": "B",
"stepInterval": 60
}
],
"queryFormulas": []
},
"clickhouse_sql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"id": "41eea5bc-f9cf-45c2-92fb-ef226d6b540b",
"promql": [
{
"disabled": false,
"legend": "",
"name": "A",
"query": ""
}
],
"queryType": "builder"
},
"softMax": null,
"softMin": null,
"thresholds": [],
"timePreferance": "GLOBAL_TIME",
"title": "Network IO",
"yAxisUnit": "bytes"
}
]
}

Some files were not shown because too many files have changed in this diff Show More