Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2df36c11f6 | ||
|
|
a33b13c166 | ||
|
|
700e021228 | ||
|
|
5a0b850e8c | ||
|
|
70d542e3fe | ||
|
|
cd525221e5 | ||
|
|
7128d78415 | ||
|
|
9edc1fd180 | ||
|
|
de36acf5f1 | ||
|
|
299c3c1135 | ||
|
|
46cf13cd2f |
@@ -82,12 +82,14 @@ function QuerySearch({
|
||||
dataSource,
|
||||
onRun,
|
||||
signalSource,
|
||||
isMetricsExplorer = false,
|
||||
}: {
|
||||
onChange: (value: string) => void;
|
||||
queryData: IBuilderQuery;
|
||||
dataSource: DataSource;
|
||||
signalSource?: string;
|
||||
onRun?: (query: string) => void;
|
||||
isMetricsExplorer?: boolean;
|
||||
}): JSX.Element {
|
||||
const isDarkMode = useIsDarkMode();
|
||||
const [query, setQuery] = useState<string>(queryData.filter?.expression || '');
|
||||
@@ -208,7 +210,8 @@ function QuerySearch({
|
||||
async (searchText?: string): Promise<void> => {
|
||||
if (
|
||||
dataSource === DataSource.METRICS &&
|
||||
!queryData.aggregateAttribute?.key
|
||||
!queryData.aggregateAttribute?.key &&
|
||||
!isMetricsExplorer
|
||||
) {
|
||||
setKeySuggestions([]);
|
||||
return;
|
||||
@@ -249,6 +252,7 @@ function QuerySearch({
|
||||
toggleSuggestions,
|
||||
queryData.aggregateAttribute?.key,
|
||||
signalSource,
|
||||
isMetricsExplorer,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -1453,6 +1457,7 @@ function QuerySearch({
|
||||
QuerySearch.defaultProps = {
|
||||
onRun: undefined,
|
||||
signalSource: '',
|
||||
isMetricsExplorer: false,
|
||||
};
|
||||
|
||||
export default QuerySearch;
|
||||
|
||||
@@ -33,7 +33,7 @@ function ExpandedView({
|
||||
options,
|
||||
spaceAggregationSeriesMap,
|
||||
step,
|
||||
metricInspectionOptions,
|
||||
appliedMetricInspectionOptions,
|
||||
timeAggregatedSeriesMap,
|
||||
}: ExpandedViewProps): JSX.Element {
|
||||
const [
|
||||
@@ -44,17 +44,17 @@ function ExpandedView({
|
||||
useEffect(() => {
|
||||
logEvent(MetricsExplorerEvents.InspectPointClicked, {
|
||||
[MetricsExplorerEventKeys.Modal]: 'inspect',
|
||||
[MetricsExplorerEventKeys.Filters]: metricInspectionOptions.filters,
|
||||
[MetricsExplorerEventKeys.Filters]: appliedMetricInspectionOptions.filters,
|
||||
[MetricsExplorerEventKeys.TimeAggregationInterval]:
|
||||
metricInspectionOptions.timeAggregationInterval,
|
||||
appliedMetricInspectionOptions.timeAggregationInterval,
|
||||
[MetricsExplorerEventKeys.TimeAggregationOption]:
|
||||
metricInspectionOptions.timeAggregationOption,
|
||||
appliedMetricInspectionOptions.timeAggregationOption,
|
||||
[MetricsExplorerEventKeys.SpaceAggregationOption]:
|
||||
metricInspectionOptions.spaceAggregationOption,
|
||||
appliedMetricInspectionOptions.spaceAggregationOption,
|
||||
[MetricsExplorerEventKeys.SpaceAggregationLabels]:
|
||||
metricInspectionOptions.spaceAggregationLabels,
|
||||
appliedMetricInspectionOptions.spaceAggregationLabels,
|
||||
});
|
||||
}, [metricInspectionOptions]);
|
||||
}, [appliedMetricInspectionOptions]);
|
||||
|
||||
useEffect(() => {
|
||||
if (step !== InspectionStep.COMPLETED) {
|
||||
@@ -167,7 +167,7 @@ function ExpandedView({
|
||||
<Typography.Text strong>
|
||||
{`${absoluteValue} is the ${
|
||||
SPACE_AGGREGATION_OPTIONS_FOR_EXPANDED_VIEW[
|
||||
metricInspectionOptions.spaceAggregationOption ??
|
||||
appliedMetricInspectionOptions.spaceAggregationOption ??
|
||||
SpaceAggregationOptions.SUM_BY
|
||||
]
|
||||
} of`}
|
||||
@@ -240,7 +240,7 @@ function ExpandedView({
|
||||
)?.value ?? options?.value
|
||||
} is the ${
|
||||
TIME_AGGREGATION_OPTIONS[
|
||||
metricInspectionOptions.timeAggregationOption ??
|
||||
appliedMetricInspectionOptions.timeAggregationOption ??
|
||||
TimeAggregationOptions.SUM
|
||||
]
|
||||
} of`
|
||||
@@ -299,7 +299,7 @@ function ExpandedView({
|
||||
<Typography.Text strong>
|
||||
{`${absoluteValue} is the ${
|
||||
TIME_AGGREGATION_OPTIONS[
|
||||
metricInspectionOptions.timeAggregationOption ??
|
||||
appliedMetricInspectionOptions.timeAggregationOption ??
|
||||
TimeAggregationOptions.SUM
|
||||
]
|
||||
} of`}
|
||||
|
||||
@@ -29,7 +29,7 @@ function GraphView({
|
||||
popoverOptions,
|
||||
setShowExpandedView,
|
||||
setExpandedViewOptions,
|
||||
metricInspectionOptions,
|
||||
appliedMetricInspectionOptions,
|
||||
isInspectMetricsRefetching,
|
||||
}: GraphViewProps): JSX.Element {
|
||||
const isDarkMode = useIsDarkMode();
|
||||
@@ -233,7 +233,7 @@ function GraphView({
|
||||
inspectMetricsTimeSeries={inspectMetricsTimeSeries}
|
||||
setShowExpandedView={setShowExpandedView}
|
||||
setExpandedViewOptions={setExpandedViewOptions}
|
||||
metricInspectionOptions={metricInspectionOptions}
|
||||
appliedMetricInspectionOptions={appliedMetricInspectionOptions}
|
||||
isInspectMetricsRefetching={isInspectMetricsRefetching}
|
||||
/>
|
||||
)}
|
||||
@@ -255,7 +255,7 @@ function GraphView({
|
||||
<HoverPopover
|
||||
options={hoverPopoverOptions}
|
||||
step={inspectionStep}
|
||||
metricInspectionOptions={metricInspectionOptions}
|
||||
appliedMetricInspectionOptions={appliedMetricInspectionOptions}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -122,6 +122,10 @@
|
||||
gap: 4px;
|
||||
|
||||
.inspect-metrics-query-builder-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.query-builder-button-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -11,6 +11,7 @@ import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { Compass } from 'lucide-react';
|
||||
import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
import { MetricsExplorerEventKeys, MetricsExplorerEvents } from '../events';
|
||||
@@ -24,6 +25,7 @@ import {
|
||||
MetricInspectionAction,
|
||||
} from './types';
|
||||
import { useInspectMetrics } from './useInspectMetrics';
|
||||
import { useMetricName } from './utils';
|
||||
|
||||
function Inspect({
|
||||
metricName: defaultMetricName,
|
||||
@@ -31,7 +33,12 @@ function Inspect({
|
||||
onClose,
|
||||
}: InspectProps): JSX.Element {
|
||||
const isDarkMode = useIsDarkMode();
|
||||
const [metricName, setMetricName] = useState<string | null>(defaultMetricName);
|
||||
const {
|
||||
currentMetricName,
|
||||
setCurrentMetricName,
|
||||
appliedMetricName,
|
||||
setAppliedMetricName,
|
||||
} = useMetricName(defaultMetricName);
|
||||
const [
|
||||
popoverOptions,
|
||||
setPopoverOptions,
|
||||
@@ -42,9 +49,12 @@ function Inspect({
|
||||
] = useState<GraphPopoverOptions | null>(null);
|
||||
const [showExpandedView, setShowExpandedView] = useState(false);
|
||||
|
||||
const { data: metricDetailsData } = useGetMetricDetails(metricName ?? '', {
|
||||
enabled: !!metricName,
|
||||
});
|
||||
const { data: metricDetailsData } = useGetMetricDetails(
|
||||
appliedMetricName ?? '',
|
||||
{
|
||||
enabled: !!appliedMetricName,
|
||||
},
|
||||
);
|
||||
|
||||
const { currentQuery } = useQueryBuilder();
|
||||
const { handleChangeQueryData } = useQueryOperations({
|
||||
@@ -97,25 +107,16 @@ function Inspect({
|
||||
aggregatedTimeSeries,
|
||||
timeAggregatedSeriesMap,
|
||||
reset,
|
||||
} = useInspectMetrics(metricName);
|
||||
} = useInspectMetrics(appliedMetricName);
|
||||
|
||||
const handleDispatchMetricInspectionOptions = useCallback(
|
||||
(action: MetricInspectionAction): void => {
|
||||
dispatchMetricInspectionOptions(action);
|
||||
logEvent(MetricsExplorerEvents.InspectQueryChanged, {
|
||||
[MetricsExplorerEventKeys.Modal]: 'inspect',
|
||||
[MetricsExplorerEventKeys.Filters]: metricInspectionOptions.filters,
|
||||
[MetricsExplorerEventKeys.TimeAggregationInterval]:
|
||||
metricInspectionOptions.timeAggregationInterval,
|
||||
[MetricsExplorerEventKeys.TimeAggregationOption]:
|
||||
metricInspectionOptions.timeAggregationOption,
|
||||
[MetricsExplorerEventKeys.SpaceAggregationOption]:
|
||||
metricInspectionOptions.spaceAggregationOption,
|
||||
[MetricsExplorerEventKeys.SpaceAggregationLabels]:
|
||||
metricInspectionOptions.spaceAggregationLabels,
|
||||
});
|
||||
},
|
||||
[dispatchMetricInspectionOptions, metricInspectionOptions],
|
||||
[dispatchMetricInspectionOptions],
|
||||
);
|
||||
|
||||
const selectedMetricType = useMemo(
|
||||
@@ -128,18 +129,30 @@ function Inspect({
|
||||
[metricDetailsData],
|
||||
);
|
||||
|
||||
const aggregateAttribute = useMemo(
|
||||
() => ({
|
||||
key: currentMetricName ?? '',
|
||||
dataType: DataTypes.String,
|
||||
type: selectedMetricType as string,
|
||||
isColumn: true,
|
||||
isJSON: false,
|
||||
id: `${currentMetricName}--${DataTypes.String}--${selectedMetricType}--true`,
|
||||
}),
|
||||
[currentMetricName, selectedMetricType],
|
||||
);
|
||||
|
||||
const [currentQueryData, setCurrentQueryData] = useState<IBuilderQuery>({
|
||||
...searchQuery,
|
||||
aggregateAttribute,
|
||||
});
|
||||
|
||||
const resetInspection = useCallback(() => {
|
||||
setShowExpandedView(false);
|
||||
setPopoverOptions(null);
|
||||
setExpandedViewOptions(null);
|
||||
setCurrentQueryData(searchQuery as IBuilderQuery);
|
||||
reset();
|
||||
}, [reset]);
|
||||
|
||||
// Reset inspection when the selected metric changes
|
||||
useEffect(() => {
|
||||
resetInspection();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [metricName]);
|
||||
}, [reset, searchQuery]);
|
||||
|
||||
// Hide expanded view whenever inspection step changes
|
||||
useEffect(() => {
|
||||
@@ -193,7 +206,7 @@ function Inspect({
|
||||
inspectMetricsTimeSeries={aggregatedTimeSeries}
|
||||
formattedInspectMetricsTimeSeries={formattedInspectMetricsTimeSeries}
|
||||
resetInspection={resetInspection}
|
||||
metricName={metricName}
|
||||
metricName={appliedMetricName}
|
||||
metricUnit={selectedMetricUnit}
|
||||
metricType={selectedMetricType}
|
||||
spaceAggregationSeriesMap={spaceAggregationSeriesMap}
|
||||
@@ -203,19 +216,20 @@ function Inspect({
|
||||
showExpandedView={showExpandedView}
|
||||
setExpandedViewOptions={setExpandedViewOptions}
|
||||
popoverOptions={popoverOptions}
|
||||
metricInspectionOptions={metricInspectionOptions}
|
||||
appliedMetricInspectionOptions={metricInspectionOptions.appliedOptions}
|
||||
isInspectMetricsRefetching={isInspectMetricsRefetching}
|
||||
/>
|
||||
<QueryBuilder
|
||||
metricName={metricName}
|
||||
metricType={selectedMetricType}
|
||||
setMetricName={setMetricName}
|
||||
currentMetricName={currentMetricName}
|
||||
setCurrentMetricName={setCurrentMetricName}
|
||||
setAppliedMetricName={setAppliedMetricName}
|
||||
spaceAggregationLabels={spaceAggregationLabels}
|
||||
metricInspectionOptions={metricInspectionOptions}
|
||||
currentMetricInspectionOptions={metricInspectionOptions.currentOptions}
|
||||
dispatchMetricInspectionOptions={handleDispatchMetricInspectionOptions}
|
||||
inspectionStep={inspectionStep}
|
||||
inspectMetricsTimeSeries={inspectMetricsTimeSeries}
|
||||
searchQuery={searchQuery as IBuilderQuery}
|
||||
currentQuery={currentQueryData}
|
||||
setCurrentQuery={setCurrentQueryData}
|
||||
/>
|
||||
</div>
|
||||
<div className="inspect-metrics-content-second-col">
|
||||
@@ -228,7 +242,7 @@ function Inspect({
|
||||
options={expandedViewOptions}
|
||||
spaceAggregationSeriesMap={spaceAggregationSeriesMap}
|
||||
step={inspectionStep}
|
||||
metricInspectionOptions={metricInspectionOptions}
|
||||
appliedMetricInspectionOptions={metricInspectionOptions.appliedOptions}
|
||||
timeAggregatedSeriesMap={timeAggregatedSeriesMap}
|
||||
/>
|
||||
)}
|
||||
@@ -244,17 +258,21 @@ function Inspect({
|
||||
aggregatedTimeSeries,
|
||||
formattedInspectMetricsTimeSeries,
|
||||
resetInspection,
|
||||
metricName,
|
||||
appliedMetricName,
|
||||
selectedMetricUnit,
|
||||
selectedMetricType,
|
||||
spaceAggregationSeriesMap,
|
||||
inspectionStep,
|
||||
showExpandedView,
|
||||
popoverOptions,
|
||||
metricInspectionOptions,
|
||||
metricInspectionOptions.appliedOptions,
|
||||
metricInspectionOptions.currentOptions,
|
||||
currentMetricName,
|
||||
setCurrentMetricName,
|
||||
setAppliedMetricName,
|
||||
spaceAggregationLabels,
|
||||
handleDispatchMetricInspectionOptions,
|
||||
searchQuery,
|
||||
currentQueryData,
|
||||
expandedViewOptions,
|
||||
timeAggregatedSeriesMap,
|
||||
]);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Button, Card } from 'antd';
|
||||
import { Atom } from 'lucide-react';
|
||||
import { Atom, Play } from 'lucide-react';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { QueryBuilderProps } from './types';
|
||||
import {
|
||||
@@ -10,16 +11,24 @@ import {
|
||||
} from './utils';
|
||||
|
||||
function QueryBuilder({
|
||||
metricName,
|
||||
setMetricName,
|
||||
currentMetricName,
|
||||
setCurrentMetricName,
|
||||
setAppliedMetricName,
|
||||
spaceAggregationLabels,
|
||||
metricInspectionOptions,
|
||||
currentMetricInspectionOptions,
|
||||
dispatchMetricInspectionOptions,
|
||||
inspectionStep,
|
||||
inspectMetricsTimeSeries,
|
||||
searchQuery,
|
||||
metricType,
|
||||
currentQuery,
|
||||
setCurrentQuery,
|
||||
}: QueryBuilderProps): JSX.Element {
|
||||
const applyInspectionOptions = useCallback(() => {
|
||||
setAppliedMetricName(currentMetricName ?? '');
|
||||
dispatchMetricInspectionOptions({
|
||||
type: 'APPLY_INSPECTION_OPTIONS',
|
||||
});
|
||||
}, [currentMetricName, setAppliedMetricName, dispatchMetricInspectionOptions]);
|
||||
|
||||
return (
|
||||
<div className="inspect-metrics-query-builder">
|
||||
<div className="inspect-metrics-query-builder-header">
|
||||
@@ -31,25 +40,36 @@ function QueryBuilder({
|
||||
>
|
||||
Query Builder
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
className="stage-run-query"
|
||||
icon={<Play size={14} />}
|
||||
onClick={applyInspectionOptions}
|
||||
data-testid="apply-query-button"
|
||||
>
|
||||
Stage & Run Query
|
||||
</Button>
|
||||
</div>
|
||||
<Card className="inspect-metrics-query-builder-content">
|
||||
<MetricNameSearch metricName={metricName} setMetricName={setMetricName} />
|
||||
<MetricNameSearch
|
||||
currentMetricName={currentMetricName}
|
||||
setCurrentMetricName={setCurrentMetricName}
|
||||
/>
|
||||
<MetricFilters
|
||||
dispatchMetricInspectionOptions={dispatchMetricInspectionOptions}
|
||||
searchQuery={searchQuery}
|
||||
metricName={metricName}
|
||||
metricType={metricType || null}
|
||||
currentQuery={currentQuery}
|
||||
setCurrentQuery={setCurrentQuery}
|
||||
/>
|
||||
<MetricTimeAggregation
|
||||
inspectionStep={inspectionStep}
|
||||
metricInspectionOptions={metricInspectionOptions}
|
||||
currentMetricInspectionOptions={currentMetricInspectionOptions}
|
||||
dispatchMetricInspectionOptions={dispatchMetricInspectionOptions}
|
||||
inspectMetricsTimeSeries={inspectMetricsTimeSeries}
|
||||
/>
|
||||
<MetricSpaceAggregation
|
||||
inspectionStep={inspectionStep}
|
||||
spaceAggregationLabels={spaceAggregationLabels}
|
||||
metricInspectionOptions={metricInspectionOptions}
|
||||
currentMetricInspectionOptions={currentMetricInspectionOptions}
|
||||
dispatchMetricInspectionOptions={dispatchMetricInspectionOptions}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
@@ -11,13 +11,13 @@ function TableView({
|
||||
setShowExpandedView,
|
||||
setExpandedViewOptions,
|
||||
isInspectMetricsRefetching,
|
||||
metricInspectionOptions,
|
||||
appliedMetricInspectionOptions,
|
||||
}: TableViewProps): JSX.Element {
|
||||
const isSpaceAggregatedWithoutLabel = useMemo(
|
||||
() =>
|
||||
!!metricInspectionOptions.spaceAggregationOption &&
|
||||
metricInspectionOptions.spaceAggregationLabels.length === 0,
|
||||
[metricInspectionOptions],
|
||||
!!appliedMetricInspectionOptions.spaceAggregationOption &&
|
||||
appliedMetricInspectionOptions.spaceAggregationLabels.length === 0,
|
||||
[appliedMetricInspectionOptions],
|
||||
);
|
||||
const labelKeys = useMemo(() => {
|
||||
if (isSpaceAggregatedWithoutLabel) {
|
||||
|
||||
@@ -10,7 +10,7 @@ import ExpandedView from '../ExpandedView';
|
||||
import {
|
||||
GraphPopoverData,
|
||||
InspectionStep,
|
||||
MetricInspectionOptions,
|
||||
InspectOptions,
|
||||
SpaceAggregationOptions,
|
||||
TimeAggregationOptions,
|
||||
} from '../types';
|
||||
@@ -62,7 +62,7 @@ describe('ExpandedView', () => {
|
||||
],
|
||||
]);
|
||||
|
||||
const mockMetricInspectionOptions: MetricInspectionOptions = {
|
||||
const mockMetricInspectionOptions: InspectOptions = {
|
||||
timeAggregationOption: TimeAggregationOptions.MAX,
|
||||
timeAggregationInterval: 60,
|
||||
spaceAggregationOption: SpaceAggregationOptions.MAX_BY,
|
||||
@@ -79,7 +79,7 @@ describe('ExpandedView', () => {
|
||||
options={mockOptions}
|
||||
spaceAggregationSeriesMap={mockSpaceAggregationSeriesMap}
|
||||
step={InspectionStep.TIME_AGGREGATION}
|
||||
metricInspectionOptions={mockMetricInspectionOptions}
|
||||
appliedMetricInspectionOptions={mockMetricInspectionOptions}
|
||||
timeAggregatedSeriesMap={mockTimeAggregatedSeriesMap}
|
||||
/>,
|
||||
);
|
||||
@@ -96,7 +96,7 @@ describe('ExpandedView', () => {
|
||||
options={mockOptions}
|
||||
spaceAggregationSeriesMap={mockSpaceAggregationSeriesMap}
|
||||
step={InspectionStep.SPACE_AGGREGATION}
|
||||
metricInspectionOptions={{
|
||||
appliedMetricInspectionOptions={{
|
||||
...mockMetricInspectionOptions,
|
||||
timeAggregationInterval: TIME_AGGREGATION_INTERVAL,
|
||||
}}
|
||||
@@ -127,7 +127,7 @@ describe('ExpandedView', () => {
|
||||
options={mockOptions}
|
||||
spaceAggregationSeriesMap={mockSpaceAggregationSeriesMap}
|
||||
step={InspectionStep.COMPLETED}
|
||||
metricInspectionOptions={mockMetricInspectionOptions}
|
||||
appliedMetricInspectionOptions={mockMetricInspectionOptions}
|
||||
timeAggregatedSeriesMap={mockTimeAggregatedSeriesMap}
|
||||
/>,
|
||||
);
|
||||
@@ -153,7 +153,7 @@ describe('ExpandedView', () => {
|
||||
options={mockOptions}
|
||||
spaceAggregationSeriesMap={mockSpaceAggregationSeriesMap}
|
||||
step={InspectionStep.TIME_AGGREGATION}
|
||||
metricInspectionOptions={mockMetricInspectionOptions}
|
||||
appliedMetricInspectionOptions={mockMetricInspectionOptions}
|
||||
timeAggregatedSeriesMap={mockTimeAggregatedSeriesMap}
|
||||
/>,
|
||||
);
|
||||
|
||||
@@ -60,7 +60,7 @@ describe('GraphView', () => {
|
||||
setExpandedViewOptions: jest.fn(),
|
||||
resetInspection: jest.fn(),
|
||||
showExpandedView: false,
|
||||
metricInspectionOptions: {
|
||||
appliedMetricInspectionOptions: {
|
||||
timeAggregationInterval: 60,
|
||||
spaceAggregationOption: SpaceAggregationOptions.MAX_BY,
|
||||
spaceAggregationLabels: ['host_name'],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable react/jsx-props-no-spreading */
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import { MetricType } from 'api/metricsExplorer/getMetricsList';
|
||||
import * as appContextHooks from 'providers/App/App';
|
||||
import { QueryClient, QueryClientProvider } from 'react-query';
|
||||
@@ -22,6 +22,27 @@ jest.mock('react-router-dom', () => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('container/QueryBuilder/filters', () => ({
|
||||
AggregatorFilter: ({ onSelect, onChange, defaultValue }: any): JSX.Element => (
|
||||
<div data-testid="mock-aggregator-filter">
|
||||
<input
|
||||
data-testid="metric-name-input"
|
||||
defaultValue={defaultValue}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>): void =>
|
||||
onChange({ key: e.target.value })
|
||||
}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
data-testid="select-metric-button"
|
||||
onClick={(): void => onSelect({ key: 'test_metric_2' })}
|
||||
>
|
||||
Select Metric
|
||||
</button>
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
|
||||
jest.spyOn(appContextHooks, 'useAppContext').mockReturnValue({
|
||||
user: {
|
||||
role: 'admin',
|
||||
@@ -48,12 +69,16 @@ jest.spyOn(appContextHooks, 'useAppContext').mockReturnValue({
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
const mockSetCurrentMetricName = jest.fn();
|
||||
const mockSetAppliedMetricName = jest.fn();
|
||||
|
||||
describe('QueryBuilder', () => {
|
||||
const defaultProps = {
|
||||
metricName: 'test_metric',
|
||||
setMetricName: jest.fn(),
|
||||
currentMetricName: 'test_metric',
|
||||
setCurrentMetricName: mockSetCurrentMetricName,
|
||||
setAppliedMetricName: mockSetAppliedMetricName,
|
||||
spaceAggregationLabels: ['label1', 'label2'],
|
||||
metricInspectionOptions: {
|
||||
currentMetricInspectionOptions: {
|
||||
timeAggregationInterval: 60,
|
||||
timeAggregationOption: TimeAggregationOptions.AVG,
|
||||
spaceAggregationLabels: [],
|
||||
@@ -67,12 +92,13 @@ describe('QueryBuilder', () => {
|
||||
metricType: MetricType.SUM,
|
||||
inspectionStep: InspectionStep.TIME_AGGREGATION,
|
||||
inspectMetricsTimeSeries: [],
|
||||
searchQuery: {
|
||||
currentQuery: {
|
||||
filters: {
|
||||
items: [],
|
||||
op: 'and',
|
||||
},
|
||||
} as any,
|
||||
setCurrentQuery: jest.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -133,4 +159,57 @@ describe('QueryBuilder', () => {
|
||||
);
|
||||
expect(screen.getByTestId('metric-space-aggregation')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should call setCurrentMetricName when metric name is selected', () => {
|
||||
render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<Provider store={store}>
|
||||
<QueryBuilder {...defaultProps} />
|
||||
</Provider>
|
||||
</QueryClientProvider>,
|
||||
);
|
||||
|
||||
const metricNameSearch = screen.getByTestId('metric-name-search');
|
||||
expect(metricNameSearch).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('From')).toBeInTheDocument();
|
||||
|
||||
const selectButton = screen.getByTestId('select-metric-button');
|
||||
fireEvent.click(selectButton);
|
||||
|
||||
expect(mockSetCurrentMetricName).toHaveBeenCalledWith('test_metric_2');
|
||||
});
|
||||
|
||||
it('should call setAppliedMetricName when query is applied', () => {
|
||||
render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<Provider store={store}>
|
||||
<QueryBuilder {...defaultProps} />
|
||||
</Provider>
|
||||
</QueryClientProvider>,
|
||||
);
|
||||
|
||||
const applyQueryButton = screen.getByTestId('apply-query-button');
|
||||
fireEvent.click(applyQueryButton);
|
||||
|
||||
expect(mockSetCurrentMetricName).toHaveBeenCalledTimes(0);
|
||||
expect(mockSetAppliedMetricName).toHaveBeenCalledWith('test_metric');
|
||||
});
|
||||
|
||||
it('should apply inspect options when query is applied', () => {
|
||||
render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<Provider store={store}>
|
||||
<QueryBuilder {...defaultProps} />
|
||||
</Provider>
|
||||
</QueryClientProvider>,
|
||||
);
|
||||
|
||||
const applyQueryButton = screen.getByTestId('apply-query-button');
|
||||
fireEvent.click(applyQueryButton);
|
||||
|
||||
expect(defaultProps.dispatchMetricInspectionOptions).toHaveBeenCalledWith({
|
||||
type: 'APPLY_INSPECTION_OPTIONS',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -49,7 +49,7 @@ describe('TableView', () => {
|
||||
inspectMetricsTimeSeries: mockTimeSeries,
|
||||
setShowExpandedView: jest.fn(),
|
||||
setExpandedViewOptions: jest.fn(),
|
||||
metricInspectionOptions: {
|
||||
appliedMetricInspectionOptions: {
|
||||
timeAggregationInterval: 60,
|
||||
timeAggregationOption: TimeAggregationOptions.MAX,
|
||||
spaceAggregationOption: SpaceAggregationOptions.MAX_BY,
|
||||
|
||||
@@ -72,13 +72,25 @@ export const SPACE_AGGREGATION_OPTIONS_FOR_EXPANDED_VIEW: Record<
|
||||
};
|
||||
|
||||
export const INITIAL_INSPECT_METRICS_OPTIONS: MetricInspectionOptions = {
|
||||
timeAggregationOption: undefined,
|
||||
timeAggregationInterval: undefined,
|
||||
spaceAggregationOption: undefined,
|
||||
spaceAggregationLabels: [],
|
||||
filters: {
|
||||
items: [],
|
||||
op: 'AND',
|
||||
currentOptions: {
|
||||
timeAggregationOption: undefined,
|
||||
timeAggregationInterval: undefined,
|
||||
spaceAggregationOption: undefined,
|
||||
spaceAggregationLabels: [],
|
||||
filters: {
|
||||
items: [],
|
||||
op: 'AND',
|
||||
},
|
||||
},
|
||||
appliedOptions: {
|
||||
timeAggregationOption: undefined,
|
||||
timeAggregationInterval: undefined,
|
||||
spaceAggregationOption: undefined,
|
||||
spaceAggregationLabels: [],
|
||||
filters: {
|
||||
items: [],
|
||||
op: 'AND',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -43,36 +43,36 @@ export interface GraphViewProps {
|
||||
showExpandedView: boolean;
|
||||
setShowExpandedView: (showExpandedView: boolean) => void;
|
||||
setExpandedViewOptions: (options: GraphPopoverOptions | null) => void;
|
||||
metricInspectionOptions: MetricInspectionOptions;
|
||||
appliedMetricInspectionOptions: InspectOptions;
|
||||
isInspectMetricsRefetching: boolean;
|
||||
}
|
||||
|
||||
export interface QueryBuilderProps {
|
||||
metricName: string | null;
|
||||
setMetricName: (metricName: string) => void;
|
||||
metricType: MetricType | undefined;
|
||||
currentMetricName: string | null;
|
||||
setCurrentMetricName: (metricName: string) => void;
|
||||
setAppliedMetricName: (metricName: string) => void;
|
||||
spaceAggregationLabels: string[];
|
||||
metricInspectionOptions: MetricInspectionOptions;
|
||||
currentMetricInspectionOptions: InspectOptions;
|
||||
dispatchMetricInspectionOptions: (action: MetricInspectionAction) => void;
|
||||
inspectionStep: InspectionStep;
|
||||
inspectMetricsTimeSeries: InspectMetricsSeries[];
|
||||
searchQuery: IBuilderQuery;
|
||||
currentQuery: IBuilderQuery;
|
||||
setCurrentQuery: (query: IBuilderQuery) => void;
|
||||
}
|
||||
|
||||
export interface MetricNameSearchProps {
|
||||
metricName: string | null;
|
||||
setMetricName: (metricName: string) => void;
|
||||
currentMetricName: string | null;
|
||||
setCurrentMetricName: (metricName: string) => void;
|
||||
}
|
||||
|
||||
export interface MetricFiltersProps {
|
||||
searchQuery: IBuilderQuery;
|
||||
dispatchMetricInspectionOptions: (action: MetricInspectionAction) => void;
|
||||
metricName: string | null;
|
||||
metricType: MetricType | null;
|
||||
currentQuery: IBuilderQuery;
|
||||
setCurrentQuery: (query: IBuilderQuery) => void;
|
||||
}
|
||||
|
||||
export interface MetricTimeAggregationProps {
|
||||
metricInspectionOptions: MetricInspectionOptions;
|
||||
currentMetricInspectionOptions: InspectOptions;
|
||||
dispatchMetricInspectionOptions: (action: MetricInspectionAction) => void;
|
||||
inspectionStep: InspectionStep;
|
||||
inspectMetricsTimeSeries: InspectMetricsSeries[];
|
||||
@@ -80,7 +80,7 @@ export interface MetricTimeAggregationProps {
|
||||
|
||||
export interface MetricSpaceAggregationProps {
|
||||
spaceAggregationLabels: string[];
|
||||
metricInspectionOptions: MetricInspectionOptions;
|
||||
currentMetricInspectionOptions: InspectOptions;
|
||||
dispatchMetricInspectionOptions: (action: MetricInspectionAction) => void;
|
||||
inspectionStep: InspectionStep;
|
||||
}
|
||||
@@ -101,7 +101,7 @@ export enum SpaceAggregationOptions {
|
||||
AVG_BY = 'avg_by',
|
||||
}
|
||||
|
||||
export interface MetricInspectionOptions {
|
||||
export interface InspectOptions {
|
||||
timeAggregationOption: TimeAggregationOptions | undefined;
|
||||
timeAggregationInterval: number | undefined;
|
||||
spaceAggregationOption: SpaceAggregationOptions | undefined;
|
||||
@@ -109,13 +109,19 @@ export interface MetricInspectionOptions {
|
||||
filters: TagFilter;
|
||||
}
|
||||
|
||||
export interface MetricInspectionOptions {
|
||||
currentOptions: InspectOptions;
|
||||
appliedOptions: InspectOptions;
|
||||
}
|
||||
|
||||
export type MetricInspectionAction =
|
||||
| { type: 'SET_TIME_AGGREGATION_OPTION'; payload: TimeAggregationOptions }
|
||||
| { type: 'SET_TIME_AGGREGATION_INTERVAL'; payload: number }
|
||||
| { type: 'SET_SPACE_AGGREGATION_OPTION'; payload: SpaceAggregationOptions }
|
||||
| { type: 'SET_SPACE_AGGREGATION_LABELS'; payload: string[] }
|
||||
| { type: 'SET_FILTERS'; payload: TagFilter }
|
||||
| { type: 'RESET_INSPECTION' };
|
||||
| { type: 'RESET_INSPECTION' }
|
||||
| { type: 'APPLY_INSPECTION_OPTIONS' };
|
||||
|
||||
export enum InspectionStep {
|
||||
TIME_AGGREGATION = 1,
|
||||
@@ -156,7 +162,7 @@ export interface ExpandedViewProps {
|
||||
options: GraphPopoverOptions | null;
|
||||
spaceAggregationSeriesMap: Map<string, InspectMetricsSeries[]>;
|
||||
step: InspectionStep;
|
||||
metricInspectionOptions: MetricInspectionOptions;
|
||||
appliedMetricInspectionOptions: InspectOptions;
|
||||
timeAggregatedSeriesMap: Map<number, GraphPopoverData[]>;
|
||||
}
|
||||
|
||||
@@ -165,7 +171,7 @@ export interface TableViewProps {
|
||||
inspectMetricsTimeSeries: InspectMetricsSeries[];
|
||||
setShowExpandedView: (showExpandedView: boolean) => void;
|
||||
setExpandedViewOptions: (options: GraphPopoverOptions | null) => void;
|
||||
metricInspectionOptions: MetricInspectionOptions;
|
||||
appliedMetricInspectionOptions: InspectOptions;
|
||||
isInspectMetricsRefetching: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,30 +27,55 @@ const metricInspectionReducer = (
|
||||
case 'SET_TIME_AGGREGATION_OPTION':
|
||||
return {
|
||||
...state,
|
||||
timeAggregationOption: action.payload,
|
||||
currentOptions: {
|
||||
...state.currentOptions,
|
||||
timeAggregationOption: action.payload,
|
||||
},
|
||||
};
|
||||
case 'SET_TIME_AGGREGATION_INTERVAL':
|
||||
return {
|
||||
...state,
|
||||
timeAggregationInterval: action.payload,
|
||||
currentOptions: {
|
||||
...state.currentOptions,
|
||||
timeAggregationInterval: action.payload,
|
||||
},
|
||||
};
|
||||
case 'SET_SPACE_AGGREGATION_OPTION':
|
||||
return {
|
||||
...state,
|
||||
spaceAggregationOption: action.payload,
|
||||
currentOptions: {
|
||||
...state.currentOptions,
|
||||
spaceAggregationOption: action.payload,
|
||||
},
|
||||
};
|
||||
case 'SET_SPACE_AGGREGATION_LABELS':
|
||||
return {
|
||||
...state,
|
||||
spaceAggregationLabels: action.payload,
|
||||
currentOptions: {
|
||||
...state.currentOptions,
|
||||
spaceAggregationLabels: action.payload,
|
||||
},
|
||||
};
|
||||
case 'SET_FILTERS':
|
||||
return {
|
||||
...state,
|
||||
filters: action.payload,
|
||||
currentOptions: {
|
||||
...state.currentOptions,
|
||||
filters: action.payload,
|
||||
},
|
||||
};
|
||||
case 'APPLY_INSPECTION_OPTIONS':
|
||||
return {
|
||||
...state,
|
||||
appliedOptions: {
|
||||
...state.appliedOptions,
|
||||
...state.currentOptions,
|
||||
},
|
||||
};
|
||||
case 'RESET_INSPECTION':
|
||||
return { ...INITIAL_INSPECT_METRICS_OPTIONS };
|
||||
return {
|
||||
...INITIAL_INSPECT_METRICS_OPTIONS,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
@@ -84,7 +109,7 @@ export function useInspectMetrics(
|
||||
metricName: metricName ?? '',
|
||||
start,
|
||||
end,
|
||||
filters: metricInspectionOptions.filters,
|
||||
filters: metricInspectionOptions.appliedOptions.filters,
|
||||
},
|
||||
{
|
||||
enabled: !!metricName,
|
||||
@@ -117,13 +142,26 @@ export function useInspectMetrics(
|
||||
);
|
||||
|
||||
// Evaluate inspection step
|
||||
const inspectionStep = useMemo(() => {
|
||||
if (metricInspectionOptions.spaceAggregationOption) {
|
||||
const currentInspectionStep = useMemo(() => {
|
||||
if (metricInspectionOptions.currentOptions.spaceAggregationOption) {
|
||||
return InspectionStep.COMPLETED;
|
||||
}
|
||||
if (
|
||||
metricInspectionOptions.timeAggregationOption &&
|
||||
metricInspectionOptions.timeAggregationInterval
|
||||
metricInspectionOptions.currentOptions.timeAggregationOption &&
|
||||
metricInspectionOptions.currentOptions.timeAggregationInterval
|
||||
) {
|
||||
return InspectionStep.SPACE_AGGREGATION;
|
||||
}
|
||||
return InspectionStep.TIME_AGGREGATION;
|
||||
}, [metricInspectionOptions]);
|
||||
|
||||
const appliedInspectionStep = useMemo(() => {
|
||||
if (metricInspectionOptions.appliedOptions.spaceAggregationOption) {
|
||||
return InspectionStep.COMPLETED;
|
||||
}
|
||||
if (
|
||||
metricInspectionOptions.appliedOptions.timeAggregationOption &&
|
||||
metricInspectionOptions.appliedOptions.timeAggregationInterval
|
||||
) {
|
||||
return InspectionStep.SPACE_AGGREGATION;
|
||||
}
|
||||
@@ -149,23 +187,26 @@ export function useInspectMetrics(
|
||||
|
||||
// Apply time aggregation once required options are set
|
||||
if (
|
||||
inspectionStep >= InspectionStep.SPACE_AGGREGATION &&
|
||||
metricInspectionOptions.timeAggregationOption &&
|
||||
metricInspectionOptions.timeAggregationInterval
|
||||
appliedInspectionStep >= InspectionStep.SPACE_AGGREGATION &&
|
||||
metricInspectionOptions.appliedOptions.timeAggregationOption &&
|
||||
metricInspectionOptions.appliedOptions.timeAggregationInterval
|
||||
) {
|
||||
const {
|
||||
timeAggregatedSeries,
|
||||
timeAggregatedSeriesMap,
|
||||
} = applyTimeAggregation(inspectMetricsTimeSeries, metricInspectionOptions);
|
||||
} = applyTimeAggregation(
|
||||
inspectMetricsTimeSeries,
|
||||
metricInspectionOptions.appliedOptions,
|
||||
);
|
||||
timeSeries = timeAggregatedSeries;
|
||||
setTimeAggregatedSeriesMap(timeAggregatedSeriesMap);
|
||||
setAggregatedTimeSeries(timeSeries);
|
||||
}
|
||||
// Apply space aggregation
|
||||
if (inspectionStep === InspectionStep.COMPLETED) {
|
||||
if (appliedInspectionStep === InspectionStep.COMPLETED) {
|
||||
const { aggregatedSeries, spaceAggregatedSeriesMap } = applySpaceAggregation(
|
||||
timeSeries,
|
||||
metricInspectionOptions,
|
||||
metricInspectionOptions.appliedOptions,
|
||||
);
|
||||
timeSeries = aggregatedSeries;
|
||||
setSpaceAggregatedSeriesMap(spaceAggregatedSeriesMap);
|
||||
@@ -186,7 +227,7 @@ export function useInspectMetrics(
|
||||
|
||||
const rawData = [timestamps, ...timeseriesArray];
|
||||
return rawData.map((series) => new Float64Array(series));
|
||||
}, [inspectMetricsTimeSeries, inspectionStep, metricInspectionOptions]);
|
||||
}, [inspectMetricsTimeSeries, appliedInspectionStep, metricInspectionOptions]);
|
||||
|
||||
const spaceAggregationLabels = useMemo(() => {
|
||||
const labels = new Set<string>();
|
||||
@@ -216,7 +257,7 @@ export function useInspectMetrics(
|
||||
spaceAggregationLabels,
|
||||
metricInspectionOptions,
|
||||
dispatchMetricInspectionOptions,
|
||||
inspectionStep,
|
||||
inspectionStep: currentInspectionStep,
|
||||
isInspectMetricsRefetching,
|
||||
spaceAggregatedSeriesMap,
|
||||
aggregatedTimeSeries,
|
||||
|
||||
@@ -4,16 +4,12 @@ import logEvent from 'api/common/logEvent';
|
||||
import { InspectMetricsSeries } from 'api/metricsExplorer/getInspectMetricsDetails';
|
||||
import { MetricType } from 'api/metricsExplorer/getMetricsList';
|
||||
import classNames from 'classnames';
|
||||
import QuerySearch from 'components/QueryBuilderV2/QueryV2/QuerySearch/QuerySearch';
|
||||
import { convertExpressionToFilters } from 'components/QueryBuilderV2/utils';
|
||||
import { initialQueriesMap } from 'constants/queryBuilder';
|
||||
import { AggregatorFilter } from 'container/QueryBuilder/filters';
|
||||
import QueryBuilderSearch from 'container/QueryBuilder/filters/QueryBuilderSearch';
|
||||
import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations';
|
||||
import { HardHat } from 'lucide-react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import {
|
||||
BaseAutocompleteData,
|
||||
DataTypes,
|
||||
} from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
@@ -26,8 +22,8 @@ import {
|
||||
GraphPopoverData,
|
||||
GraphPopoverOptions,
|
||||
InspectionStep,
|
||||
InspectOptions,
|
||||
MetricFiltersProps,
|
||||
MetricInspectionOptions,
|
||||
MetricNameSearchProps,
|
||||
MetricSpaceAggregationProps,
|
||||
MetricTimeAggregationProps,
|
||||
@@ -71,13 +67,13 @@ export function getDefaultTimeAggregationInterval(
|
||||
}
|
||||
|
||||
export function MetricNameSearch({
|
||||
metricName,
|
||||
setMetricName,
|
||||
currentMetricName,
|
||||
setCurrentMetricName,
|
||||
}: MetricNameSearchProps): JSX.Element {
|
||||
const [searchText, setSearchText] = useState(metricName);
|
||||
const [searchText, setSearchText] = useState(currentMetricName);
|
||||
|
||||
const handleSetMetricName = (value: BaseAutocompleteData): void => {
|
||||
setMetricName(value.key);
|
||||
setCurrentMetricName(value.key);
|
||||
};
|
||||
|
||||
const handleChange = (value: BaseAutocompleteData): void => {
|
||||
@@ -102,27 +98,31 @@ export function MetricNameSearch({
|
||||
|
||||
export function MetricFilters({
|
||||
dispatchMetricInspectionOptions,
|
||||
searchQuery,
|
||||
metricName,
|
||||
metricType,
|
||||
currentQuery,
|
||||
setCurrentQuery,
|
||||
}: MetricFiltersProps): JSX.Element {
|
||||
const { handleChangeQueryData } = useQueryOperations({
|
||||
index: 0,
|
||||
query: searchQuery,
|
||||
entityVersion: '',
|
||||
});
|
||||
|
||||
const aggregateAttribute = useMemo(
|
||||
() => ({
|
||||
key: metricName ?? '',
|
||||
dataType: DataTypes.String,
|
||||
type: metricType,
|
||||
isColumn: true,
|
||||
isJSON: false,
|
||||
id: `${metricName}--${DataTypes.String}--${metricType}--true`,
|
||||
}),
|
||||
[metricName, metricType],
|
||||
);
|
||||
const handleOnChange = (expression: string): void => {
|
||||
logEvent(MetricsExplorerEvents.FilterApplied, {
|
||||
[MetricsExplorerEventKeys.Modal]: 'inspect',
|
||||
});
|
||||
const tagFilter = {
|
||||
items: convertExpressionToFilters(expression),
|
||||
op: 'AND',
|
||||
};
|
||||
setCurrentQuery({
|
||||
...currentQuery,
|
||||
filters: tagFilter,
|
||||
filter: {
|
||||
...currentQuery.filter,
|
||||
expression,
|
||||
},
|
||||
expression,
|
||||
});
|
||||
dispatchMetricInspectionOptions({
|
||||
type: 'SET_FILTERS',
|
||||
payload: tagFilter,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -130,30 +130,19 @@ export function MetricFilters({
|
||||
className="inspect-metrics-input-group metric-filters"
|
||||
>
|
||||
<Typography.Text>Where</Typography.Text>
|
||||
<QueryBuilderSearch
|
||||
query={{
|
||||
...searchQuery,
|
||||
aggregateAttribute,
|
||||
}}
|
||||
onChange={(value): void => {
|
||||
handleChangeQueryData('filters', value);
|
||||
logEvent(MetricsExplorerEvents.FilterApplied, {
|
||||
[MetricsExplorerEventKeys.Modal]: 'inspect',
|
||||
});
|
||||
dispatchMetricInspectionOptions({
|
||||
type: 'SET_FILTERS',
|
||||
payload: value,
|
||||
});
|
||||
}}
|
||||
suffixIcon={<HardHat size={16} />}
|
||||
disableNavigationShortcuts
|
||||
/>
|
||||
{currentQuery && (
|
||||
<QuerySearch
|
||||
queryData={currentQuery}
|
||||
onChange={handleOnChange}
|
||||
dataSource={DataSource.METRICS}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function MetricTimeAggregation({
|
||||
metricInspectionOptions,
|
||||
currentMetricInspectionOptions,
|
||||
dispatchMetricInspectionOptions,
|
||||
inspectionStep,
|
||||
inspectMetricsTimeSeries,
|
||||
@@ -174,14 +163,14 @@ export function MetricTimeAggregation({
|
||||
<div className="inspect-metrics-input-group">
|
||||
<Typography.Text>Align with</Typography.Text>
|
||||
<Select
|
||||
value={metricInspectionOptions.timeAggregationOption}
|
||||
value={currentMetricInspectionOptions.timeAggregationOption}
|
||||
onChange={(value): void => {
|
||||
dispatchMetricInspectionOptions({
|
||||
type: 'SET_TIME_AGGREGATION_OPTION',
|
||||
payload: value,
|
||||
});
|
||||
// set the time aggregation interval to the default value if it is not set
|
||||
if (!metricInspectionOptions.timeAggregationInterval) {
|
||||
if (!currentMetricInspectionOptions.timeAggregationInterval) {
|
||||
dispatchMetricInspectionOptions({
|
||||
type: 'SET_TIME_AGGREGATION_INTERVAL',
|
||||
payload: getDefaultTimeAggregationInterval(
|
||||
@@ -205,7 +194,7 @@ export function MetricTimeAggregation({
|
||||
<Input
|
||||
type="number"
|
||||
className="no-arrows-input"
|
||||
value={metricInspectionOptions.timeAggregationInterval}
|
||||
value={currentMetricInspectionOptions.timeAggregationInterval}
|
||||
placeholder="Select interval..."
|
||||
suffix="seconds"
|
||||
onChange={(e): void => {
|
||||
@@ -224,7 +213,7 @@ export function MetricTimeAggregation({
|
||||
|
||||
export function MetricSpaceAggregation({
|
||||
spaceAggregationLabels,
|
||||
metricInspectionOptions,
|
||||
currentMetricInspectionOptions,
|
||||
dispatchMetricInspectionOptions,
|
||||
inspectionStep,
|
||||
}: MetricSpaceAggregationProps): JSX.Element {
|
||||
@@ -243,7 +232,7 @@ export function MetricSpaceAggregation({
|
||||
<div className="metric-space-aggregation-content">
|
||||
<div className="metric-space-aggregation-content-left">
|
||||
<Select
|
||||
value={metricInspectionOptions.spaceAggregationOption}
|
||||
value={currentMetricInspectionOptions.spaceAggregationOption}
|
||||
placeholder="Select option"
|
||||
onChange={(value): void => {
|
||||
dispatchMetricInspectionOptions({
|
||||
@@ -266,7 +255,7 @@ export function MetricSpaceAggregation({
|
||||
mode="multiple"
|
||||
style={{ width: '100%' }}
|
||||
placeholder="Search for attributes..."
|
||||
value={metricInspectionOptions.spaceAggregationLabels}
|
||||
value={currentMetricInspectionOptions.spaceAggregationLabels}
|
||||
onChange={(value): void => {
|
||||
dispatchMetricInspectionOptions({
|
||||
type: 'SET_SPACE_AGGREGATION_LABELS',
|
||||
@@ -322,7 +311,7 @@ export function applyFilters(
|
||||
|
||||
export function applyTimeAggregation(
|
||||
inspectMetricsTimeSeries: InspectMetricsSeries[],
|
||||
metricInspectionOptions: MetricInspectionOptions,
|
||||
appliedMetricInspectionOptions: InspectOptions,
|
||||
): {
|
||||
timeAggregatedSeries: InspectMetricsSeries[];
|
||||
timeAggregatedSeriesMap: Map<number, GraphPopoverData[]>;
|
||||
@@ -330,7 +319,7 @@ export function applyTimeAggregation(
|
||||
const {
|
||||
timeAggregationOption,
|
||||
timeAggregationInterval,
|
||||
} = metricInspectionOptions;
|
||||
} = appliedMetricInspectionOptions;
|
||||
|
||||
if (!timeAggregationInterval) {
|
||||
return {
|
||||
@@ -415,7 +404,7 @@ export function applyTimeAggregation(
|
||||
|
||||
export function applySpaceAggregation(
|
||||
inspectMetricsTimeSeries: InspectMetricsSeries[],
|
||||
metricInspectionOptions: MetricInspectionOptions,
|
||||
appliedMetricInspectionOptions: InspectOptions,
|
||||
): {
|
||||
aggregatedSeries: InspectMetricsSeries[];
|
||||
spaceAggregatedSeriesMap: Map<string, InspectMetricsSeries[]>;
|
||||
@@ -425,7 +414,7 @@ export function applySpaceAggregation(
|
||||
|
||||
inspectMetricsTimeSeries.forEach((series) => {
|
||||
// Create composite key from selected labels
|
||||
const key = metricInspectionOptions.spaceAggregationLabels
|
||||
const key = appliedMetricInspectionOptions.spaceAggregationLabels
|
||||
.map((label) => `${label}:${series.labels[label]}`)
|
||||
.join(',');
|
||||
|
||||
@@ -460,7 +449,7 @@ export function applySpaceAggregation(
|
||||
([timestamp, values]) => {
|
||||
let aggregatedValue: number;
|
||||
|
||||
switch (metricInspectionOptions.spaceAggregationOption) {
|
||||
switch (appliedMetricInspectionOptions.spaceAggregationOption) {
|
||||
case SpaceAggregationOptions.SUM_BY:
|
||||
aggregatedValue = values.reduce((sum, val) => sum + val, 0);
|
||||
break;
|
||||
@@ -714,11 +703,11 @@ export function getTimeSeriesLabel(
|
||||
export function HoverPopover({
|
||||
options,
|
||||
step,
|
||||
metricInspectionOptions,
|
||||
appliedMetricInspectionOptions,
|
||||
}: {
|
||||
options: GraphPopoverOptions;
|
||||
step: InspectionStep;
|
||||
metricInspectionOptions: MetricInspectionOptions;
|
||||
appliedMetricInspectionOptions: InspectOptions;
|
||||
}): JSX.Element {
|
||||
const closestTimestamp = useMemo(() => {
|
||||
if (!options.timeSeries) {
|
||||
@@ -746,7 +735,7 @@ export function HoverPopover({
|
||||
const title = useMemo(() => {
|
||||
if (
|
||||
step === InspectionStep.COMPLETED &&
|
||||
metricInspectionOptions.spaceAggregationLabels.length === 0
|
||||
appliedMetricInspectionOptions.spaceAggregationLabels.length === 0
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -760,7 +749,7 @@ export function HoverPopover({
|
||||
options.timeSeries,
|
||||
options.timeSeries?.strokeColor,
|
||||
);
|
||||
}, [step, options.timeSeries, metricInspectionOptions]);
|
||||
}, [step, options.timeSeries, appliedMetricInspectionOptions]);
|
||||
|
||||
return (
|
||||
<Card
|
||||
@@ -830,3 +819,26 @@ export function onGraphHover(
|
||||
timeSeries: series,
|
||||
});
|
||||
}
|
||||
|
||||
export function useMetricName(
|
||||
metricName: string | null,
|
||||
): {
|
||||
currentMetricName: string | null;
|
||||
setCurrentMetricName: (metricName: string | null) => void;
|
||||
appliedMetricName: string | null;
|
||||
setAppliedMetricName: (metricName: string | null) => void;
|
||||
} {
|
||||
const [currentMetricName, setCurrentMetricName] = useState<string | null>(
|
||||
metricName,
|
||||
);
|
||||
const [appliedMetricName, setAppliedMetricName] = useState<string | null>(
|
||||
metricName,
|
||||
);
|
||||
|
||||
return {
|
||||
currentMetricName,
|
||||
setCurrentMetricName,
|
||||
appliedMetricName,
|
||||
setAppliedMetricName,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,27 +1,97 @@
|
||||
import { Tooltip } from 'antd';
|
||||
import QueryBuilderSearch from 'container/QueryBuilder/filters/QueryBuilderSearch';
|
||||
import { Button, Tooltip } from 'antd';
|
||||
import QuerySearch from 'components/QueryBuilderV2/QueryV2/QuerySearch/QuerySearch';
|
||||
import { convertExpressionToFilters } from 'components/QueryBuilderV2/utils';
|
||||
import DateTimeSelectionV2 from 'container/TopNav/DateTimeSelectionV2';
|
||||
import { HardHat, Info } from 'lucide-react';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { Info, Play } from 'lucide-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import {
|
||||
IBuilderQuery,
|
||||
TagFilter,
|
||||
} from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
import { MetricsSearchProps } from './types';
|
||||
|
||||
function MetricsSearch({ query, onChange }: MetricsSearchProps): JSX.Element {
|
||||
function MetricsSearch({ onChange, query }: MetricsSearchProps): JSX.Element {
|
||||
const [contextQuery, setContextQuery] = useState<IBuilderQuery | undefined>(
|
||||
query,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setContextQuery(query);
|
||||
}, [query]);
|
||||
|
||||
const handleRunQuery = (expression: string): void => {
|
||||
let updatedContextQuery = cloneDeep(contextQuery);
|
||||
if (!updatedContextQuery) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newFilters: TagFilter = {
|
||||
items: expression ? convertExpressionToFilters(expression) : [],
|
||||
op: 'AND',
|
||||
};
|
||||
updatedContextQuery = {
|
||||
...updatedContextQuery,
|
||||
filter: {
|
||||
...updatedContextQuery.filter,
|
||||
expression,
|
||||
},
|
||||
filters: {
|
||||
...updatedContextQuery.filters,
|
||||
...newFilters,
|
||||
op: updatedContextQuery.filters?.op ?? 'AND',
|
||||
},
|
||||
};
|
||||
setContextQuery(updatedContextQuery);
|
||||
|
||||
onChange(newFilters);
|
||||
};
|
||||
|
||||
const handleOnChange = (expression: string): void => {
|
||||
let updatedContextQuery = cloneDeep(contextQuery);
|
||||
if (updatedContextQuery) {
|
||||
updatedContextQuery = {
|
||||
...updatedContextQuery,
|
||||
filter: {
|
||||
...updatedContextQuery.filter,
|
||||
expression,
|
||||
},
|
||||
};
|
||||
setContextQuery(updatedContextQuery);
|
||||
}
|
||||
};
|
||||
|
||||
const handleStageAndRunQuery = (): void =>
|
||||
handleRunQuery(contextQuery?.filter?.expression || '');
|
||||
return (
|
||||
<div className="metrics-search-container">
|
||||
<div className="qb-search-container">
|
||||
<div data-testid="qb-search-container" className="qb-search-container">
|
||||
<Tooltip
|
||||
title="Use filters to refine metrics based on attributes. Example: service_name=api - Shows all metrics associated with the API service"
|
||||
placement="right"
|
||||
>
|
||||
<Info size={16} />
|
||||
</Tooltip>
|
||||
<QueryBuilderSearch
|
||||
query={query}
|
||||
onChange={onChange}
|
||||
suffixIcon={<HardHat size={16} />}
|
||||
isMetricsExplorer
|
||||
/>
|
||||
{contextQuery && (
|
||||
<QuerySearch
|
||||
onChange={handleOnChange}
|
||||
dataSource={DataSource.METRICS}
|
||||
queryData={contextQuery}
|
||||
onRun={handleRunQuery}
|
||||
isMetricsExplorer
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={handleStageAndRunQuery}
|
||||
className="stage-run-query"
|
||||
icon={<Play size={14} />}
|
||||
>
|
||||
Stage & Run Query
|
||||
</Button>
|
||||
<div className="metrics-search-options">
|
||||
<DateTimeSelectionV2
|
||||
showAutoRefresh={false}
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
|
||||
.metrics-search-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
|
||||
.metrics-search-options {
|
||||
|
||||
@@ -3,6 +3,7 @@ import './Summary.styles.scss';
|
||||
|
||||
import * as Sentry from '@sentry/react';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { convertFiltersToExpression } from 'components/QueryBuilderV2/utils';
|
||||
import { initialQueriesMap } from 'constants/queryBuilder';
|
||||
import { usePageSize } from 'container/InfraMonitoringK8s/utils';
|
||||
import NoLogs from 'container/NoLogs/NoLogs';
|
||||
@@ -184,6 +185,7 @@ function Summary(): JSX.Element {
|
||||
() => ({
|
||||
...initialQueriesMap.metrics.builder.queryData[0],
|
||||
filters: queryFilters,
|
||||
filter: convertFiltersToExpression(queryFilters),
|
||||
}),
|
||||
[queryFilters],
|
||||
);
|
||||
@@ -290,18 +292,11 @@ function Summary(): JSX.Element {
|
||||
],
|
||||
);
|
||||
|
||||
console.log({
|
||||
isMetricsListDataEmpty,
|
||||
isMetricsTreeMapDataEmpty,
|
||||
treeMapData,
|
||||
sec: treeMapData?.payload?.data[heatmapView],
|
||||
});
|
||||
|
||||
return (
|
||||
<Sentry.ErrorBoundary fallback={<ErrorBoundaryFallback />}>
|
||||
<div className="metrics-explorer-summary-tab">
|
||||
<MetricsSearch query={searchQuery} onChange={handleFilterChange} />
|
||||
{isMetricsLoading || isTreeMapLoading ? (
|
||||
{isMetricsLoading && isTreeMapLoading ? (
|
||||
<MetricsLoading />
|
||||
) : isMetricsListDataEmpty && isMetricsTreeMapDataEmpty ? (
|
||||
<NoLogs dataSource={DataSource.METRICS} />
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import { initialQueriesMap } from 'constants/queryBuilder';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
import MetricsSearch from '../MetricsSearch';
|
||||
|
||||
jest.mock('container/TopNav/DateTimeSelectionV2', () => ({
|
||||
__esModule: true,
|
||||
default: (): JSX.Element => (
|
||||
<div data-testid="date-time-selection">DateTime</div>
|
||||
),
|
||||
}));
|
||||
|
||||
const mockQuery: IBuilderQuery = {
|
||||
...initialQueriesMap.metrics.builder.queryData[0],
|
||||
};
|
||||
const mockOnChange = jest.fn();
|
||||
|
||||
describe('MetricsSearch', () => {
|
||||
it('should render the search bar, run button and date-time selector', () => {
|
||||
render(<MetricsSearch query={mockQuery} onChange={mockOnChange} />);
|
||||
expect(screen.getByText('DateTime')).toBeInTheDocument();
|
||||
expect(screen.getByText('Stage & Run Query')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('qb-search-container')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should call onChange with parsed filters when Stage & Run is clicked and expression is present', () => {
|
||||
render(<MetricsSearch query={mockQuery} onChange={mockOnChange} />);
|
||||
fireEvent.click(screen.getByText('Stage & Run Query'));
|
||||
expect(mockOnChange).toHaveBeenCalledWith({
|
||||
items: [],
|
||||
op: 'AND',
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user