Compare commits

...

3 Commits

Author SHA1 Message Date
amlannandy
79f62c0851 chore: minor fix 2026-02-18 18:02:11 +07:00
amlannandy
e1d2613f21 chore: minor fix 2026-02-18 17:53:27 +07:00
amlannandy
a352f61172 chore: metrics explorer v2 api migration in inspect page 2026-02-18 17:39:45 +07:00
15 changed files with 403 additions and 182 deletions

View File

@@ -86,6 +86,7 @@ interface QuerySearchProps {
signalSource?: string;
hardcodedAttributeKeys?: QueryKeyDataSuggestionsProps[];
onRun?: (query: string) => void;
showFilterSuggestionsWithoutMetric?: boolean;
}
function QuerySearch({
@@ -96,6 +97,7 @@ function QuerySearch({
onRun,
signalSource,
hardcodedAttributeKeys,
showFilterSuggestionsWithoutMetric,
}: QuerySearchProps): JSX.Element {
const isDarkMode = useIsDarkMode();
const [valueSuggestions, setValueSuggestions] = useState<any[]>([]);
@@ -252,7 +254,8 @@ function QuerySearch({
async (searchText?: string): Promise<void> => {
if (
dataSource === DataSource.METRICS &&
!queryData.aggregateAttribute?.key
!queryData.aggregateAttribute?.key &&
!showFilterSuggestionsWithoutMetric
) {
setKeySuggestions([]);
return;
@@ -301,6 +304,7 @@ function QuerySearch({
queryData.aggregateAttribute?.key,
signalSource,
hardcodedAttributeKeys,
showFilterSuggestionsWithoutMetric,
],
);
@@ -1562,6 +1566,7 @@ QuerySearch.defaultProps = {
hardcodedAttributeKeys: undefined,
placeholder:
"Enter your filter query (e.g., http.status_code >= 500 AND service.name = 'frontend')",
showFilterSuggestionsWithoutMetric: false,
};
export default QuerySearch;

View File

@@ -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`}

View File

@@ -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>

View File

@@ -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;
@@ -257,6 +261,21 @@
.completed-checklist-container {
margin-left: 20px;
.completed-checklist-item,
.whats-next-checklist-item {
.completed-checklist-item-title,
.whats-next-checklist-item-title {
display: flex;
align-items: center;
gap: 4px;
.ant-typography {
display: flex;
align-items: center;
gap: 4px;
}
}
}
}
.completed-message-container {

View File

@@ -9,6 +9,7 @@ import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations
import { useIsDarkMode } from 'hooks/useDarkMode';
import { Compass } from 'lucide-react';
import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback';
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import { MetricsExplorerEventKeys, MetricsExplorerEvents } from '../events';
@@ -22,6 +23,7 @@ import {
MetricInspectionAction,
} from './types';
import { useInspectMetrics } from './useInspectMetrics';
import { useMetricName } from './utils';
import './Inspect.styles.scss';
@@ -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,39 @@ 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,
});
useEffect(() => {
if (searchQuery) {
setCurrentQueryData({
...searchQuery,
aggregateAttribute,
});
}
}, [aggregateAttribute, searchQuery]);
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 +215,7 @@ function Inspect({
inspectMetricsTimeSeries={aggregatedTimeSeries}
formattedInspectMetricsTimeSeries={formattedInspectMetricsTimeSeries}
resetInspection={resetInspection}
metricName={metricName}
metricName={appliedMetricName}
metricUnit={selectedMetricUnit}
metricType={selectedMetricType}
spaceAggregationSeriesMap={spaceAggregationSeriesMap}
@@ -203,19 +225,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 +251,7 @@ function Inspect({
options={expandedViewOptions}
spaceAggregationSeriesMap={spaceAggregationSeriesMap}
step={inspectionStep}
metricInspectionOptions={metricInspectionOptions}
appliedMetricInspectionOptions={metricInspectionOptions.appliedOptions}
timeAggregatedSeriesMap={timeAggregatedSeriesMap}
/>
)}
@@ -244,17 +267,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,
]);

View File

@@ -1,5 +1,6 @@
import { useCallback } from 'react';
import { Button, Card } from 'antd';
import { Atom } from 'lucide-react';
import { Atom, Play } from 'lucide-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>

View File

@@ -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) {

View File

@@ -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}
/>,
);

View File

@@ -54,7 +54,7 @@ describe('GraphView', () => {
setExpandedViewOptions: jest.fn(),
resetInspection: jest.fn(),
showExpandedView: false,
metricInspectionOptions: {
appliedMetricInspectionOptions: {
timeAggregationInterval: 60,
spaceAggregationOption: SpaceAggregationOptions.MAX_BY,
spaceAggregationLabels: ['host_name'],

View File

@@ -1,7 +1,7 @@
/* eslint-disable react/jsx-props-no-spreading */
import { QueryClient, QueryClientProvider } from 'react-query';
import { Provider } from 'react-redux';
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 store from 'store';
@@ -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',
});
});
});

View File

@@ -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,

View File

@@ -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',
},
},
};

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -5,15 +5,11 @@ 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 {
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,25 +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,
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
@@ -128,30 +130,17 @@ 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
<QuerySearch
queryData={currentQuery}
onChange={handleOnChange}
dataSource={DataSource.METRICS}
/>
</div>
);
}
export function MetricTimeAggregation({
metricInspectionOptions,
currentMetricInspectionOptions,
dispatchMetricInspectionOptions,
inspectionStep,
inspectMetricsTimeSeries,
@@ -172,14 +161,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(
@@ -203,7 +192,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 => {
@@ -222,7 +211,7 @@ export function MetricTimeAggregation({
export function MetricSpaceAggregation({
spaceAggregationLabels,
metricInspectionOptions,
currentMetricInspectionOptions,
dispatchMetricInspectionOptions,
inspectionStep,
}: MetricSpaceAggregationProps): JSX.Element {
@@ -241,7 +230,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({
@@ -264,7 +253,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',
@@ -320,7 +309,7 @@ export function applyFilters(
export function applyTimeAggregation(
inspectMetricsTimeSeries: InspectMetricsSeries[],
metricInspectionOptions: MetricInspectionOptions,
appliedMetricInspectionOptions: InspectOptions,
): {
timeAggregatedSeries: InspectMetricsSeries[];
timeAggregatedSeriesMap: Map<number, GraphPopoverData[]>;
@@ -328,7 +317,7 @@ export function applyTimeAggregation(
const {
timeAggregationOption,
timeAggregationInterval,
} = metricInspectionOptions;
} = appliedMetricInspectionOptions;
if (!timeAggregationInterval) {
return {
@@ -413,7 +402,7 @@ export function applyTimeAggregation(
export function applySpaceAggregation(
inspectMetricsTimeSeries: InspectMetricsSeries[],
metricInspectionOptions: MetricInspectionOptions,
appliedMetricInspectionOptions: InspectOptions,
): {
aggregatedSeries: InspectMetricsSeries[];
spaceAggregatedSeriesMap: Map<string, InspectMetricsSeries[]>;
@@ -423,7 +412,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(',');
@@ -458,7 +447,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,
};
}