mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-07-31 18:11:57 +08:00
refactor(query_builder): remove old codebase (#2686)
Co-authored-by: Palash Gupta <palashgdev@gmail.com>
This commit is contained in:
parent
76331001b7
commit
9da399023b
@ -1,5 +1,3 @@
|
|||||||
import { EAggregateOperator, EReduceOperator } from 'types/common/dashboard';
|
|
||||||
|
|
||||||
export const PromQLQueryTemplate = {
|
export const PromQLQueryTemplate = {
|
||||||
query: '',
|
query: '',
|
||||||
legend: '',
|
legend: '',
|
||||||
@ -11,24 +9,3 @@ export const ClickHouseQueryTemplate = {
|
|||||||
legend: '',
|
legend: '',
|
||||||
disabled: false,
|
disabled: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const QueryBuilderQueryTemplate = {
|
|
||||||
metricName: null,
|
|
||||||
aggregateOperator: EAggregateOperator.NOOP,
|
|
||||||
tagFilters: {
|
|
||||||
op: 'AND',
|
|
||||||
items: [],
|
|
||||||
},
|
|
||||||
legend: '',
|
|
||||||
disabled: false,
|
|
||||||
// Specific to TIME_SERIES type graph
|
|
||||||
groupBy: [],
|
|
||||||
// Specific to VALUE type graph
|
|
||||||
reduceTo: EReduceOperator['Latest of values in timeframe'],
|
|
||||||
};
|
|
||||||
|
|
||||||
export const QueryBuilderFormulaTemplate = {
|
|
||||||
expression: '',
|
|
||||||
disabled: false,
|
|
||||||
legend: '',
|
|
||||||
};
|
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
import { EAggregateOperator } from 'types/common/dashboard';
|
|
||||||
|
|
||||||
export const AggregateFunctions = Object.keys(EAggregateOperator)
|
|
||||||
.filter((key) => Number.isNaN(parseInt(key, 10)))
|
|
||||||
.map((key) => ({
|
|
||||||
label: key,
|
|
||||||
value: EAggregateOperator[key as keyof typeof EAggregateOperator],
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const TagKeyOperator = [
|
|
||||||
{ label: 'In', value: 'IN' },
|
|
||||||
{ label: 'Not In', value: 'NIN' },
|
|
||||||
{ label: 'Like', value: 'LIKE' },
|
|
||||||
{ label: 'Not Like', value: 'NLIKE' },
|
|
||||||
// { label: 'Equal', value: 'EQ' },
|
|
||||||
// { label: 'Not Equal', value: 'NEQ' },
|
|
||||||
// { label: 'REGEX', value: 'REGEX' },
|
|
||||||
];
|
|
@ -3,8 +3,8 @@ import { ClickHouseQueryTemplate } from 'constants/dashboard';
|
|||||||
import GetQueryName from 'lib/query/GetQueryName';
|
import GetQueryName from 'lib/query/GetQueryName';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Query } from 'types/api/dashboard/getAll';
|
import { Query } from 'types/api/dashboard/getAll';
|
||||||
|
import { EQueryType } from 'types/common/dashboard';
|
||||||
|
|
||||||
import { WIDGET_CLICKHOUSE_QUERY_KEY_NAME } from '../../constants';
|
|
||||||
import { QueryButton } from '../../styles';
|
import { QueryButton } from '../../styles';
|
||||||
import { IHandleUpdatedQuery } from '../../types';
|
import { IHandleUpdatedQuery } from '../../types';
|
||||||
import ClickHouseQueryBuilder from './query';
|
import ClickHouseQueryBuilder from './query';
|
||||||
@ -35,7 +35,7 @@ function ClickHouseQueryContainer({
|
|||||||
// hence, this method is only applies when queryIndex is in number format.
|
// hence, this method is only applies when queryIndex is in number format.
|
||||||
|
|
||||||
if (typeof queryIndex === 'number') {
|
if (typeof queryIndex === 'number') {
|
||||||
const allQueries = queryData[WIDGET_CLICKHOUSE_QUERY_KEY_NAME];
|
const allQueries = queryData[EQueryType.CLICKHOUSE];
|
||||||
|
|
||||||
const currentIndexQuery = allQueries[queryIndex];
|
const currentIndexQuery = allQueries[queryIndex];
|
||||||
|
|
||||||
@ -57,8 +57,8 @@ function ClickHouseQueryContainer({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
const addQueryHandler = (): void => {
|
const addQueryHandler = (): void => {
|
||||||
queryData[WIDGET_CLICKHOUSE_QUERY_KEY_NAME].push({
|
queryData[EQueryType.CLICKHOUSE].push({
|
||||||
name: GetQueryName(queryData[WIDGET_CLICKHOUSE_QUERY_KEY_NAME]) || '',
|
name: GetQueryName(queryData[EQueryType.CLICKHOUSE]) || '',
|
||||||
...ClickHouseQueryTemplate,
|
...ClickHouseQueryTemplate,
|
||||||
});
|
});
|
||||||
updateQueryData({ updatedQuery: { ...queryData } });
|
updateQueryData({ updatedQuery: { ...queryData } });
|
||||||
|
@ -3,8 +3,8 @@ import { PromQLQueryTemplate } from 'constants/dashboard';
|
|||||||
import GetQueryName from 'lib/query/GetQueryName';
|
import GetQueryName from 'lib/query/GetQueryName';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { IPromQLQuery, Query } from 'types/api/dashboard/getAll';
|
import { IPromQLQuery, Query } from 'types/api/dashboard/getAll';
|
||||||
|
import { EQueryType } from 'types/common/dashboard';
|
||||||
|
|
||||||
import { WIDGET_PROMQL_QUERY_KEY_NAME } from '../../constants';
|
|
||||||
import { QueryButton } from '../../styles';
|
import { QueryButton } from '../../styles';
|
||||||
import { IHandleUpdatedQuery } from '../../types';
|
import { IHandleUpdatedQuery } from '../../types';
|
||||||
import PromQLQueryBuilder from './query';
|
import PromQLQueryBuilder from './query';
|
||||||
@ -28,7 +28,7 @@ function PromQLQueryContainer({
|
|||||||
toggleDisable,
|
toggleDisable,
|
||||||
toggleDelete,
|
toggleDelete,
|
||||||
}: IPromQLQueryHandleChange): void => {
|
}: IPromQLQueryHandleChange): void => {
|
||||||
const allQueries = queryData[WIDGET_PROMQL_QUERY_KEY_NAME];
|
const allQueries = queryData[EQueryType.PROM];
|
||||||
const currentIndexQuery = allQueries[queryIndex as number];
|
const currentIndexQuery = allQueries[queryIndex as number];
|
||||||
if (query !== undefined) currentIndexQuery.query = query;
|
if (query !== undefined) currentIndexQuery.query = query;
|
||||||
if (legend !== undefined) currentIndexQuery.legend = legend;
|
if (legend !== undefined) currentIndexQuery.legend = legend;
|
||||||
@ -42,8 +42,8 @@ function PromQLQueryContainer({
|
|||||||
updateQueryData({ updatedQuery: { ...queryData } });
|
updateQueryData({ updatedQuery: { ...queryData } });
|
||||||
};
|
};
|
||||||
const addQueryHandler = (): void => {
|
const addQueryHandler = (): void => {
|
||||||
queryData[WIDGET_PROMQL_QUERY_KEY_NAME].push({
|
queryData[EQueryType.PROM].push({
|
||||||
name: GetQueryName(queryData[WIDGET_PROMQL_QUERY_KEY_NAME]) || '',
|
name: GetQueryName(queryData[EQueryType.PROM]) || '',
|
||||||
...PromQLQueryTemplate,
|
...PromQLQueryTemplate,
|
||||||
});
|
});
|
||||||
updateQueryData({ updatedQuery: { ...queryData } });
|
updateQueryData({ updatedQuery: { ...queryData } });
|
||||||
|
@ -1,61 +0,0 @@
|
|||||||
import { createMachine } from 'xstate';
|
|
||||||
|
|
||||||
export const ResourceAttributesFilterMachine =
|
|
||||||
/** @xstate-layout N4IgpgJg5mDOIC5QBECGsAWAjA9qgThAAQDKYBAxhkQIIB2xAYgJYA2ALmPgHQAqqUANJgAngGIAcgFEAGr0SgADjljN2zHHQUgAHogAcAFgAM3AOz6ATAEYAzJdsA2Y4cOWAnABoQIxAFpDR2tuQ319AFYTcKdbFycAX3jvNExcAmIySmp6JjZOHn4hUTFNACFWAFd8bWVVdU1tPQQzY1MXY2tDdzNHM3dHd0NvXwR7biMTa313S0i+63DE5PRsPEJScnwqWgYiFg4uPgFhcQAlKRIpeSQQWrUNLRumx3Czbg8TR0sbS31jfUcw38fW47gBHmm4XCVms3SWIBSq3SGyyO1yBx4AHlFFxUOwcPhJLJrkoVPcGk9ENYFuF3i5YR0wtEHECEAEgiEmV8zH1DLYzHZ4Yi0utMltsrt9vluNjcfjCWVKtUbnd6o9QE1rMYBtxbGFvsZ3NrZj1WdYOfotUZLX0XEFHEKViKMpttjk9nlDrL8HiCWJzpcSbcyWrGoh3NCQj0zK53P1ph1WeFLLqnJZ2s5vmZLA6kginWsXaj3VLDoUAGqoSpgEp0cpVGohh5hhDWDy0sz8zruakzamWVm-Qyg362V5-AZOayO1KFlHitEejFHKCV6v+i5XRt1ZuU1s52zjNOOaZfdOWIY+RDZ0Hc6ZmKEXqyLPPCudit2Sz08ACSEFYNbSHI27kuquiIOEjiONwjJgrM3RWJYZisgEIJgnYPTmuEdi2OaiR5nQOAQHA2hvsiH4Sui0qFCcIGhnuLSmP0YJuJ2xjJsmKELG8XZTK0tjdHG06vgW5GupRS7St6vrKqSO4UhqVL8TBWp8o4eqdl0A5Xmy3G6gK56-B4uERDOSKiuJi6lgUAhrhUYB0buimtrEKZBDYrxaS0OZca8+ltheybOI4hivGZzrzp+VGHH+AGOQp4EIHy+ghNYnawtG4TsbYvk8QKfHGAJfQ9uF76WSW37xWBTSGJ0qXpd0vRZdEKGPqC2YeO2-zfO4+HxEAA */
|
|
||||||
createMachine({
|
|
||||||
tsTypes: {} as import('./MetricTagKey.machine.typegen').Typegen0,
|
|
||||||
initial: 'Idle',
|
|
||||||
states: {
|
|
||||||
TagKey: {
|
|
||||||
on: {
|
|
||||||
NEXT: {
|
|
||||||
actions: 'onSelectOperator',
|
|
||||||
target: 'Operator',
|
|
||||||
},
|
|
||||||
onBlur: {
|
|
||||||
actions: 'onBlurPurge',
|
|
||||||
target: 'Idle',
|
|
||||||
},
|
|
||||||
RESET: {
|
|
||||||
target: 'Idle',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Operator: {
|
|
||||||
on: {
|
|
||||||
NEXT: {
|
|
||||||
actions: 'onSelectTagValue',
|
|
||||||
target: 'TagValue',
|
|
||||||
},
|
|
||||||
// onBlur: {
|
|
||||||
// actions: 'onBlurPurge',
|
|
||||||
// target: 'Idle',
|
|
||||||
// },
|
|
||||||
RESET: {
|
|
||||||
target: 'Idle',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
TagValue: {
|
|
||||||
on: {
|
|
||||||
onBlur: {
|
|
||||||
actions: ['onValidateQuery'],
|
|
||||||
// target: 'Idle',
|
|
||||||
},
|
|
||||||
RESET: {
|
|
||||||
target: 'Idle',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Idle: {
|
|
||||||
on: {
|
|
||||||
NEXT: {
|
|
||||||
actions: 'onSelectTagKey',
|
|
||||||
description: 'Select Category',
|
|
||||||
target: 'TagKey',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
id: 'Dashboard Search And Filter',
|
|
||||||
});
|
|
@ -1,32 +0,0 @@
|
|||||||
// This file was automatically generated. Edits will be overwritten
|
|
||||||
|
|
||||||
export interface Typegen0 {
|
|
||||||
'@@xstate/typegen': true;
|
|
||||||
internalEvents: {
|
|
||||||
'xstate.init': { type: 'xstate.init' };
|
|
||||||
};
|
|
||||||
invokeSrcNameMap: {};
|
|
||||||
missingImplementations: {
|
|
||||||
actions:
|
|
||||||
| 'onBlurPurge'
|
|
||||||
| 'onSelectOperator'
|
|
||||||
| 'onSelectTagKey'
|
|
||||||
| 'onSelectTagValue'
|
|
||||||
| 'onValidateQuery';
|
|
||||||
delays: never;
|
|
||||||
guards: never;
|
|
||||||
services: never;
|
|
||||||
};
|
|
||||||
eventsCausingActions: {
|
|
||||||
onBlurPurge: 'onBlur';
|
|
||||||
onSelectOperator: 'NEXT';
|
|
||||||
onSelectTagKey: 'NEXT';
|
|
||||||
onSelectTagValue: 'NEXT';
|
|
||||||
onValidateQuery: 'onBlur';
|
|
||||||
};
|
|
||||||
eventsCausingDelays: {};
|
|
||||||
eventsCausingGuards: {};
|
|
||||||
eventsCausingServices: {};
|
|
||||||
matchesStates: 'Idle' | 'Operator' | 'TagKey' | 'TagValue';
|
|
||||||
tags: never;
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
import { QueryChipContainer, QueryChipItem } from './styles';
|
|
||||||
import { ITagKeyValueQuery } from './types';
|
|
||||||
|
|
||||||
interface IQueryChipProps {
|
|
||||||
queryData: ITagKeyValueQuery;
|
|
||||||
onClose: (id: string) => void;
|
|
||||||
disabled?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function QueryChip({
|
|
||||||
queryData,
|
|
||||||
onClose,
|
|
||||||
disabled,
|
|
||||||
}: IQueryChipProps): JSX.Element {
|
|
||||||
return (
|
|
||||||
<QueryChipContainer>
|
|
||||||
<QueryChipItem>{queryData.key}</QueryChipItem>
|
|
||||||
<QueryChipItem>{queryData.op}</QueryChipItem>
|
|
||||||
<QueryChipItem
|
|
||||||
closable={!disabled}
|
|
||||||
onClose={(): void => {
|
|
||||||
if (!disabled) onClose(queryData.id);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{queryData.value.join(', ')}
|
|
||||||
</QueryChipItem>
|
|
||||||
</QueryChipContainer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
QueryChip.defaultProps = {
|
|
||||||
disabled: false,
|
|
||||||
};
|
|
@ -1,209 +0,0 @@
|
|||||||
import { CloseCircleFilled } from '@ant-design/icons';
|
|
||||||
import { useMachine } from '@xstate/react';
|
|
||||||
import { Button, Select, Spin } from 'antd';
|
|
||||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
|
||||||
import { map } from 'lodash-es';
|
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
|
||||||
import { IMetricsBuilderQuery } from 'types/api/dashboard/getAll';
|
|
||||||
import { v4 as uuid } from 'uuid';
|
|
||||||
|
|
||||||
import { ResourceAttributesFilterMachine } from './MetricTagKey.machine';
|
|
||||||
import QueryChip from './QueryChip';
|
|
||||||
import { QueryChipItem, SearchContainer } from './styles';
|
|
||||||
import { IOption, ITagKeyValueQuery } from './types';
|
|
||||||
import {
|
|
||||||
createQuery,
|
|
||||||
GetTagKeys,
|
|
||||||
GetTagValues,
|
|
||||||
OperatorSchema,
|
|
||||||
SingleValueOperators,
|
|
||||||
} from './utils';
|
|
||||||
|
|
||||||
interface IMetricTagKeyFilterProps {
|
|
||||||
metricName: IMetricsBuilderQuery['metricName'];
|
|
||||||
onSetQuery: (args: IMetricsBuilderQuery['tagFilters']['items']) => void;
|
|
||||||
selectedTagFilters: IMetricsBuilderQuery['tagFilters']['items'];
|
|
||||||
}
|
|
||||||
|
|
||||||
function MetricTagKeyFilter({
|
|
||||||
metricName,
|
|
||||||
onSetQuery,
|
|
||||||
selectedTagFilters: selectedTagQueries,
|
|
||||||
}: IMetricTagKeyFilterProps): JSX.Element | null {
|
|
||||||
const isDarkMode = useIsDarkMode();
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
const [selectedValues, setSelectedValues] = useState<string[]>([]);
|
|
||||||
const [staging, setStaging] = useState<string[]>([]);
|
|
||||||
const [queries, setQueries] = useState<ITagKeyValueQuery[]>([]);
|
|
||||||
const [optionsData, setOptionsData] = useState<{
|
|
||||||
mode: undefined | 'tags' | 'multiple';
|
|
||||||
options: IOption[];
|
|
||||||
}>({
|
|
||||||
mode: undefined,
|
|
||||||
options: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
const dispatchQueries = (
|
|
||||||
updatedQueries: IMetricsBuilderQuery['tagFilters']['items'],
|
|
||||||
): void => {
|
|
||||||
onSetQuery(updatedQueries);
|
|
||||||
setQueries(updatedQueries);
|
|
||||||
};
|
|
||||||
const handleLoading = (isLoading: boolean): void => {
|
|
||||||
setLoading(isLoading);
|
|
||||||
if (isLoading) {
|
|
||||||
setOptionsData({ mode: undefined, options: [] });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const [state, send] = useMachine(ResourceAttributesFilterMachine, {
|
|
||||||
actions: {
|
|
||||||
onSelectTagKey: () => {
|
|
||||||
handleLoading(true);
|
|
||||||
GetTagKeys(metricName || '')
|
|
||||||
.then((tagKeys) => setOptionsData({ options: tagKeys, mode: undefined }))
|
|
||||||
.finally(() => {
|
|
||||||
handleLoading(false);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onSelectOperator: () => {
|
|
||||||
setOptionsData({ options: OperatorSchema, mode: undefined });
|
|
||||||
},
|
|
||||||
onSelectTagValue: () => {
|
|
||||||
handleLoading(true);
|
|
||||||
|
|
||||||
GetTagValues(staging[0], metricName || '')
|
|
||||||
.then((tagValuesOptions) =>
|
|
||||||
setOptionsData({ options: tagValuesOptions, mode: 'tags' }),
|
|
||||||
)
|
|
||||||
.finally(() => {
|
|
||||||
handleLoading(false);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onBlurPurge: () => {
|
|
||||||
setSelectedValues([]);
|
|
||||||
setStaging([]);
|
|
||||||
},
|
|
||||||
onValidateQuery: (): void => {
|
|
||||||
if (staging.length < 2 || selectedValues.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const generatedQuery = createQuery([...staging, selectedValues]);
|
|
||||||
|
|
||||||
if (generatedQuery) {
|
|
||||||
dispatchQueries([...queries, generatedQuery]);
|
|
||||||
setSelectedValues([]);
|
|
||||||
setStaging([]);
|
|
||||||
send('RESET');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setQueries(selectedTagQueries);
|
|
||||||
}, [selectedTagQueries]);
|
|
||||||
|
|
||||||
const handleFocus = (): void => {
|
|
||||||
if (state.value === 'Idle') {
|
|
||||||
send('NEXT');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleBlur = useCallback((): void => {
|
|
||||||
send('onBlur');
|
|
||||||
}, [send]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
handleBlur();
|
|
||||||
}, [handleBlur, metricName]);
|
|
||||||
|
|
||||||
const handleChange = (value: never | string[]): void => {
|
|
||||||
if (!optionsData.mode) {
|
|
||||||
setStaging((prevStaging) => [...prevStaging, String(value)]);
|
|
||||||
setSelectedValues([]);
|
|
||||||
send('NEXT');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
state.value === 'TagValue' &&
|
|
||||||
SingleValueOperators.includes(staging[staging.length - 1]) &&
|
|
||||||
Array.isArray(value)
|
|
||||||
) {
|
|
||||||
setSelectedValues([value[value.length - 1]]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setSelectedValues([...value]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClose = (id: string): void => {
|
|
||||||
dispatchQueries(queries.filter((queryData) => queryData.id !== id));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClearAll = (): void => {
|
|
||||||
send('RESET');
|
|
||||||
dispatchQueries([]);
|
|
||||||
setStaging([]);
|
|
||||||
setSelectedValues([]);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SearchContainer isDarkMode={isDarkMode}>
|
|
||||||
<div style={{ display: 'inline-flex', flexWrap: 'wrap' }}>
|
|
||||||
{queries.length > 0 &&
|
|
||||||
map(
|
|
||||||
queries,
|
|
||||||
(query): JSX.Element => (
|
|
||||||
<QueryChip key={query.id} queryData={query} onClose={handleClose} />
|
|
||||||
),
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{map(staging, (item) => (
|
|
||||||
<QueryChipItem key={uuid()}>{item}</QueryChipItem>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ display: 'flex', width: '100%' }}>
|
|
||||||
<Select
|
|
||||||
disabled={!metricName}
|
|
||||||
placeholder={`Select ${
|
|
||||||
state.value === 'Idle' ? 'Tag Key Pair' : state.value
|
|
||||||
}`}
|
|
||||||
onChange={handleChange}
|
|
||||||
bordered={false}
|
|
||||||
value={selectedValues as never}
|
|
||||||
style={{ flex: 1 }}
|
|
||||||
options={optionsData.options}
|
|
||||||
mode={optionsData?.mode}
|
|
||||||
showArrow={false}
|
|
||||||
onFocus={handleFocus}
|
|
||||||
onBlur={handleBlur}
|
|
||||||
notFoundContent={
|
|
||||||
loading ? (
|
|
||||||
<span>
|
|
||||||
<Spin size="small" /> Loading...{' '}
|
|
||||||
</span>
|
|
||||||
) : (
|
|
||||||
<span>
|
|
||||||
No resource attributes available to filter. Please refer docs to send
|
|
||||||
attributes.
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{queries.length || staging.length || selectedValues.length ? (
|
|
||||||
<Button
|
|
||||||
onClick={handleClearAll}
|
|
||||||
icon={<CloseCircleFilled />}
|
|
||||||
type="text"
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</SearchContainer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MetricTagKeyFilter;
|
|
@ -1,30 +0,0 @@
|
|||||||
import { grey } from '@ant-design/colors';
|
|
||||||
import { Tag } from 'antd';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
export const SearchContainer = styled.div<{
|
|
||||||
isDarkMode: boolean;
|
|
||||||
disabled?: boolean;
|
|
||||||
}>`
|
|
||||||
background: ${({ isDarkMode }): string => (isDarkMode ? '#000' : '#fff')};
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 0.2rem;
|
|
||||||
border: 1px solid #ccc5;
|
|
||||||
${({ disabled }): string => (disabled ? `cursor: not-allowed;` : '')}
|
|
||||||
`;
|
|
||||||
export const QueryChipContainer = styled.span`
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-right: 0.5rem;
|
|
||||||
&:hover {
|
|
||||||
& > * {
|
|
||||||
background: ${grey.primary}44;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const QueryChipItem = styled(Tag)`
|
|
||||||
margin-right: 0.1rem;
|
|
||||||
`;
|
|
@ -1,18 +0,0 @@
|
|||||||
export interface IOption {
|
|
||||||
label: string;
|
|
||||||
value: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IMetricBuilderTagKeyQuery {
|
|
||||||
id: string;
|
|
||||||
tagKey: string;
|
|
||||||
operator: string;
|
|
||||||
tagValue: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ITagKeyValueQuery {
|
|
||||||
id: string;
|
|
||||||
key: string;
|
|
||||||
op: string;
|
|
||||||
value: string[];
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
import {
|
|
||||||
getResourceAttributesTagKeys,
|
|
||||||
getResourceAttributesTagValues,
|
|
||||||
} from 'api/metrics/getResourceAttributes';
|
|
||||||
import { v4 as uuid } from 'uuid';
|
|
||||||
|
|
||||||
import { TagKeyOperator } from '../../Options';
|
|
||||||
import { IOption, ITagKeyValueQuery } from './types';
|
|
||||||
|
|
||||||
export const OperatorSchema: IOption[] = TagKeyOperator;
|
|
||||||
|
|
||||||
export const GetTagKeys = async (metricName: string): Promise<IOption[]> => {
|
|
||||||
const { payload } = await getResourceAttributesTagKeys({ metricName });
|
|
||||||
if (!payload || !payload?.data) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
return payload.data.map((tagKey: string) => ({
|
|
||||||
label: tagKey,
|
|
||||||
value: tagKey,
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const GetTagValues = async (
|
|
||||||
tagKey: string,
|
|
||||||
metricName: string,
|
|
||||||
): Promise<IOption[]> => {
|
|
||||||
const { payload } = await getResourceAttributesTagValues({
|
|
||||||
tagKey,
|
|
||||||
metricName,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!payload || !payload?.data) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
return payload.data.map((tagValue: string) => ({
|
|
||||||
label: tagValue,
|
|
||||||
value: tagValue,
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const createQuery = (
|
|
||||||
selectedItems: Array<string | string[]> = [],
|
|
||||||
): ITagKeyValueQuery | null => {
|
|
||||||
if (selectedItems.length === 3) {
|
|
||||||
return {
|
|
||||||
id: uuid().slice(0, 8),
|
|
||||||
key: typeof selectedItems[0] === 'string' ? selectedItems[0] : '',
|
|
||||||
op: typeof selectedItems[1] === 'string' ? selectedItems[1] : '',
|
|
||||||
value: selectedItems[2] as string[],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const SingleValueOperators = ['LIKE', 'NLIKE'];
|
|
@ -1,52 +0,0 @@
|
|||||||
import { Input } from 'antd';
|
|
||||||
import React from 'react';
|
|
||||||
import { IMetricsBuilderFormula } from 'types/api/dashboard/getAll';
|
|
||||||
|
|
||||||
import QueryHeader from '../QueryHeader';
|
|
||||||
import { IQueryBuilderFormulaHandleChange } from './types';
|
|
||||||
|
|
||||||
const { TextArea } = Input;
|
|
||||||
|
|
||||||
interface IMetricsBuilderFormulaProps {
|
|
||||||
formulaData: IMetricsBuilderFormula;
|
|
||||||
formulaIndex: number | string;
|
|
||||||
handleFormulaChange: (args: IQueryBuilderFormulaHandleChange) => void;
|
|
||||||
}
|
|
||||||
function MetricsBuilderFormula({
|
|
||||||
formulaData,
|
|
||||||
formulaIndex,
|
|
||||||
handleFormulaChange,
|
|
||||||
}: IMetricsBuilderFormulaProps): JSX.Element {
|
|
||||||
return (
|
|
||||||
<QueryHeader
|
|
||||||
name={formulaData.name}
|
|
||||||
disabled={formulaData.disabled}
|
|
||||||
onDisable={(): void =>
|
|
||||||
handleFormulaChange({ formulaIndex, toggleDisable: true })
|
|
||||||
}
|
|
||||||
onDelete={(): void => {
|
|
||||||
handleFormulaChange({ formulaIndex, toggleDelete: true });
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<TextArea
|
|
||||||
onChange={(event): void =>
|
|
||||||
handleFormulaChange({ formulaIndex, expression: event.target.value })
|
|
||||||
}
|
|
||||||
size="middle"
|
|
||||||
defaultValue={formulaData.expression}
|
|
||||||
style={{ marginBottom: '0.5rem' }}
|
|
||||||
rows={2}
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
onChange={(event): void => {
|
|
||||||
handleFormulaChange({ formulaIndex, legend: event.target.value });
|
|
||||||
}}
|
|
||||||
size="middle"
|
|
||||||
defaultValue={formulaData.legend}
|
|
||||||
addonBefore="Legend Format"
|
|
||||||
/>
|
|
||||||
</QueryHeader>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MetricsBuilderFormula;
|
|
@ -1,191 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
// TODO: fix it after merge actual functionality
|
|
||||||
// @ts-nocheck
|
|
||||||
import { PlusOutlined } from '@ant-design/icons';
|
|
||||||
import {
|
|
||||||
QueryBuilderFormulaTemplate,
|
|
||||||
QueryBuilderQueryTemplate,
|
|
||||||
} from 'constants/dashboard';
|
|
||||||
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
|
||||||
import GetFormulaName from 'lib/query/GetFormulaName';
|
|
||||||
import GetQueryName from 'lib/query/GetQueryName';
|
|
||||||
import React from 'react';
|
|
||||||
import { Query } from 'types/api/dashboard/getAll';
|
|
||||||
|
|
||||||
import {
|
|
||||||
WIDGET_QUERY_BUILDER_FORMULA_KEY_NAME,
|
|
||||||
WIDGET_QUERY_BUILDER_QUERY_KEY_NAME,
|
|
||||||
} from '../../constants';
|
|
||||||
import { QueryButton } from '../../styles';
|
|
||||||
import { IHandleUpdatedQuery } from '../../types';
|
|
||||||
import MetricsBuilderFormula from './formula';
|
|
||||||
import MetricsBuilder from './query';
|
|
||||||
import {
|
|
||||||
IQueryBuilderFormulaHandleChange,
|
|
||||||
IQueryBuilderQueryHandleChange,
|
|
||||||
} from './types';
|
|
||||||
import { canCreateQueryAndFormula } from './utils';
|
|
||||||
|
|
||||||
interface IQueryBuilderQueryContainerProps {
|
|
||||||
queryData: Query;
|
|
||||||
updateQueryData: (args: IHandleUpdatedQuery) => void;
|
|
||||||
metricsBuilderQueries: Query['metricsBuilder'];
|
|
||||||
selectedGraph: GRAPH_TYPES;
|
|
||||||
}
|
|
||||||
|
|
||||||
function QueryBuilderQueryContainer({
|
|
||||||
queryData,
|
|
||||||
updateQueryData,
|
|
||||||
metricsBuilderQueries,
|
|
||||||
selectedGraph,
|
|
||||||
}: IQueryBuilderQueryContainerProps): JSX.Element | null {
|
|
||||||
const { notifications } = useNotifications();
|
|
||||||
const handleQueryBuilderQueryChange = ({
|
|
||||||
queryIndex,
|
|
||||||
aggregateFunction,
|
|
||||||
metricName,
|
|
||||||
tagFilters,
|
|
||||||
groupBy,
|
|
||||||
legend,
|
|
||||||
toggleDisable,
|
|
||||||
toggleDelete,
|
|
||||||
reduceTo,
|
|
||||||
}: IQueryBuilderQueryHandleChange): void => {
|
|
||||||
const allQueries =
|
|
||||||
queryData[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME].queryBuilder;
|
|
||||||
const currentIndexQuery = allQueries[queryIndex as number];
|
|
||||||
if (aggregateFunction) {
|
|
||||||
currentIndexQuery.aggregateOperator = aggregateFunction;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (metricName !== undefined) {
|
|
||||||
currentIndexQuery.metricName = metricName;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tagFilters) {
|
|
||||||
currentIndexQuery.tagFilters.items = tagFilters;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (groupBy) {
|
|
||||||
currentIndexQuery.groupBy = groupBy;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reduceTo) {
|
|
||||||
currentIndexQuery.reduceTo = reduceTo;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (legend !== undefined) {
|
|
||||||
currentIndexQuery.legend = legend;
|
|
||||||
}
|
|
||||||
if (toggleDisable) {
|
|
||||||
currentIndexQuery.disabled = !currentIndexQuery.disabled;
|
|
||||||
}
|
|
||||||
if (toggleDelete) {
|
|
||||||
allQueries.splice(queryIndex as number, 1);
|
|
||||||
}
|
|
||||||
updateQueryData({ updatedQuery: { ...queryData } });
|
|
||||||
};
|
|
||||||
const handleQueryBuilderFormulaChange = ({
|
|
||||||
formulaIndex,
|
|
||||||
expression,
|
|
||||||
legend,
|
|
||||||
toggleDisable,
|
|
||||||
toggleDelete,
|
|
||||||
}: IQueryBuilderFormulaHandleChange): void => {
|
|
||||||
const allFormulas =
|
|
||||||
queryData[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME][
|
|
||||||
WIDGET_QUERY_BUILDER_FORMULA_KEY_NAME
|
|
||||||
];
|
|
||||||
const currentIndexFormula = allFormulas[formulaIndex as number];
|
|
||||||
|
|
||||||
if (expression !== undefined) {
|
|
||||||
currentIndexFormula.expression = expression;
|
|
||||||
}
|
|
||||||
if (legend !== undefined) {
|
|
||||||
currentIndexFormula.legend = legend;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (toggleDisable) {
|
|
||||||
currentIndexFormula.disabled = !currentIndexFormula.disabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (toggleDelete) {
|
|
||||||
allFormulas.splice(formulaIndex as number, 1);
|
|
||||||
}
|
|
||||||
updateQueryData({ updatedQuery: { ...queryData } });
|
|
||||||
};
|
|
||||||
const addQueryHandler = (): void => {
|
|
||||||
if (!canCreateQueryAndFormula(queryData)) {
|
|
||||||
notifications.error({
|
|
||||||
message:
|
|
||||||
'Unable to create query. You can create at max 10 queries and formulae.',
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
queryData[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME].queryBuilder.push({
|
|
||||||
name:
|
|
||||||
GetQueryName(queryData[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME].queryBuilder) ||
|
|
||||||
'',
|
|
||||||
...QueryBuilderQueryTemplate,
|
|
||||||
});
|
|
||||||
updateQueryData({ updatedQuery: { ...queryData } });
|
|
||||||
};
|
|
||||||
|
|
||||||
const addFormulaHandler = (): void => {
|
|
||||||
if (!canCreateQueryAndFormula(queryData)) {
|
|
||||||
notifications.error({
|
|
||||||
message:
|
|
||||||
'Unable to create formula. You can create at max 10 queries and formulae.',
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
queryData[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME][
|
|
||||||
WIDGET_QUERY_BUILDER_FORMULA_KEY_NAME
|
|
||||||
].push({
|
|
||||||
name:
|
|
||||||
GetFormulaName(
|
|
||||||
queryData[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME][
|
|
||||||
WIDGET_QUERY_BUILDER_FORMULA_KEY_NAME
|
|
||||||
],
|
|
||||||
) || '',
|
|
||||||
...QueryBuilderFormulaTemplate,
|
|
||||||
});
|
|
||||||
updateQueryData({ updatedQuery: { ...queryData } });
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!metricsBuilderQueries) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{metricsBuilderQueries.queryBuilder.map((q, idx) => (
|
|
||||||
<MetricsBuilder
|
|
||||||
key={q.name}
|
|
||||||
queryIndex={idx}
|
|
||||||
queryData={q}
|
|
||||||
handleQueryChange={handleQueryBuilderQueryChange}
|
|
||||||
selectedGraph={selectedGraph}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
<QueryButton onClick={addQueryHandler} icon={<PlusOutlined />}>
|
|
||||||
Query
|
|
||||||
</QueryButton>
|
|
||||||
<div style={{ marginTop: '1rem' }}>
|
|
||||||
{metricsBuilderQueries.formulas.map((f, idx) => (
|
|
||||||
<MetricsBuilderFormula
|
|
||||||
key={f.name}
|
|
||||||
formulaIndex={idx}
|
|
||||||
formulaData={f}
|
|
||||||
handleFormulaChange={handleQueryBuilderFormulaChange}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
<QueryButton onClick={addFormulaHandler} icon={<PlusOutlined />}>
|
|
||||||
Formula
|
|
||||||
</QueryButton>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default QueryBuilderQueryContainer;
|
|
@ -1,215 +0,0 @@
|
|||||||
import { AutoComplete, Col, Input, Row, Select, Spin } from 'antd';
|
|
||||||
import { getMetricName } from 'api/metrics/getMetricName';
|
|
||||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
|
||||||
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
|
||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import { IMetricsBuilderQuery } from 'types/api/dashboard/getAll';
|
|
||||||
import { EReduceOperator } from 'types/common/dashboard';
|
|
||||||
|
|
||||||
import { AggregateFunctions } from '../Options';
|
|
||||||
import QueryHeader from '../QueryHeader';
|
|
||||||
import MetricTagKeyFilter from './MetricTagKeyFilter';
|
|
||||||
import { IOption } from './MetricTagKeyFilter/types';
|
|
||||||
import { GetTagKeys } from './MetricTagKeyFilter/utils';
|
|
||||||
import { IQueryBuilderQueryHandleChange } from './types';
|
|
||||||
|
|
||||||
const { Option } = Select;
|
|
||||||
|
|
||||||
interface IMetricsBuilderProps {
|
|
||||||
queryIndex: number | string;
|
|
||||||
selectedGraph: GRAPH_TYPES;
|
|
||||||
queryData: IMetricsBuilderQuery;
|
|
||||||
handleQueryChange: (args: IQueryBuilderQueryHandleChange) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
function MetricsBuilder({
|
|
||||||
queryIndex,
|
|
||||||
selectedGraph,
|
|
||||||
queryData,
|
|
||||||
handleQueryChange,
|
|
||||||
}: IMetricsBuilderProps): JSX.Element {
|
|
||||||
const [groupByOptions, setGroupByOptions] = useState<IOption[]>([]);
|
|
||||||
const [metricName, setMetricName] = useState<string | null>(
|
|
||||||
queryData.metricName,
|
|
||||||
);
|
|
||||||
|
|
||||||
const [metricNameList, setMetricNameList] = useState<string[]>([]);
|
|
||||||
const [metricNameLoading, setMetricNameLoading] = useState(false);
|
|
||||||
|
|
||||||
const handleMetricNameSelect = (e: string): void => {
|
|
||||||
handleQueryChange({ queryIndex, metricName: e });
|
|
||||||
setMetricName(e);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleMetricNameSearch = async (searchQuery = ''): Promise<void> => {
|
|
||||||
handleMetricNameSelect(searchQuery);
|
|
||||||
setMetricNameList([]);
|
|
||||||
setMetricNameLoading(true);
|
|
||||||
const { payload } = await getMetricName(searchQuery);
|
|
||||||
setMetricNameLoading(false);
|
|
||||||
if (!payload || !payload.data) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setMetricNameList(payload.data);
|
|
||||||
};
|
|
||||||
const [aggregateFunctionList, setAggregateFunctionList] = useState(
|
|
||||||
AggregateFunctions,
|
|
||||||
);
|
|
||||||
const handleAggregateFunctionsSearch = (searchQuery = ''): void => {
|
|
||||||
setAggregateFunctionList(
|
|
||||||
AggregateFunctions.filter(({ label }) =>
|
|
||||||
label.includes(searchQuery.toUpperCase()),
|
|
||||||
) || [],
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
GetTagKeys(metricName || '').then((tagKeys) => {
|
|
||||||
setGroupByOptions(tagKeys);
|
|
||||||
});
|
|
||||||
}, [metricName]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<QueryHeader
|
|
||||||
name={queryData.name}
|
|
||||||
disabled={queryData.disabled}
|
|
||||||
onDisable={(): void =>
|
|
||||||
handleQueryChange({ queryIndex, toggleDisable: true })
|
|
||||||
}
|
|
||||||
onDelete={(): void => {
|
|
||||||
handleQueryChange({ queryIndex, toggleDelete: true });
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', padding: '0.5rem' }}>
|
|
||||||
<div>
|
|
||||||
<Select
|
|
||||||
onChange={(e): void =>
|
|
||||||
handleQueryChange({ queryIndex, aggregateFunction: e })
|
|
||||||
}
|
|
||||||
defaultValue={queryData.aggregateOperator || AggregateFunctions[0]}
|
|
||||||
style={{ minWidth: 150 }}
|
|
||||||
options={aggregateFunctionList}
|
|
||||||
showSearch
|
|
||||||
onSearch={handleAggregateFunctionsSearch}
|
|
||||||
filterOption={false}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Row style={{ gap: '3%', margin: '0.5rem 0' }}>
|
|
||||||
<Row style={{ flex: 2, gap: '3%' }}>
|
|
||||||
<Select
|
|
||||||
defaultValue="metrics"
|
|
||||||
showArrow={false}
|
|
||||||
dropdownStyle={{ display: 'none' }}
|
|
||||||
>
|
|
||||||
<Option value="metrics">Metrics</Option>
|
|
||||||
</Select>
|
|
||||||
|
|
||||||
<AutoComplete
|
|
||||||
showSearch
|
|
||||||
placeholder="Metric Name (Start typing to get suggestions)"
|
|
||||||
style={{ flex: 1, minWidth: 200 }}
|
|
||||||
showArrow={false}
|
|
||||||
filterOption={false}
|
|
||||||
onSearch={handleMetricNameSearch}
|
|
||||||
notFoundContent={metricNameLoading ? <Spin size="small" /> : null}
|
|
||||||
options={metricNameList.map((option) => ({
|
|
||||||
label: option,
|
|
||||||
value: option,
|
|
||||||
}))}
|
|
||||||
defaultValue={queryData.metricName}
|
|
||||||
value={metricName}
|
|
||||||
onSelect={handleMetricNameSelect}
|
|
||||||
/>
|
|
||||||
</Row>
|
|
||||||
<Col style={{ flex: 3 }}>
|
|
||||||
<Row style={{ gap: '3%', marginBottom: '1rem' }}>
|
|
||||||
<Select
|
|
||||||
defaultValue="WHERE"
|
|
||||||
showArrow={false}
|
|
||||||
dropdownStyle={{ display: 'none' }}
|
|
||||||
>
|
|
||||||
<Option value="WHERE">WHERE</Option>
|
|
||||||
</Select>
|
|
||||||
<MetricTagKeyFilter
|
|
||||||
metricName={metricName}
|
|
||||||
selectedTagFilters={queryData.tagFilters.items}
|
|
||||||
onSetQuery={(
|
|
||||||
updatedTagFilters: IMetricsBuilderQuery['tagFilters']['items'],
|
|
||||||
): void =>
|
|
||||||
handleQueryChange({ queryIndex, tagFilters: updatedTagFilters })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Row>
|
|
||||||
<Row style={{ gap: '3%', marginBottom: '1rem' }}>
|
|
||||||
{selectedGraph === PANEL_TYPES.TIME_SERIES ? (
|
|
||||||
<>
|
|
||||||
{' '}
|
|
||||||
<Select
|
|
||||||
defaultValue="GROUP BY"
|
|
||||||
showArrow={false}
|
|
||||||
dropdownStyle={{ display: 'none' }}
|
|
||||||
>
|
|
||||||
<Option value="GROUP BY">GROUP BY</Option>
|
|
||||||
</Select>
|
|
||||||
<Select
|
|
||||||
mode="multiple"
|
|
||||||
showSearch
|
|
||||||
style={{ flex: 1 }}
|
|
||||||
defaultActiveFirstOption={false}
|
|
||||||
filterOption={false}
|
|
||||||
notFoundContent={metricNameLoading ? <Spin size="small" /> : null}
|
|
||||||
options={groupByOptions}
|
|
||||||
defaultValue={queryData.groupBy}
|
|
||||||
onChange={(e): void => {
|
|
||||||
handleQueryChange({ queryIndex, groupBy: e });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<Select
|
|
||||||
defaultValue="REDUCE TO"
|
|
||||||
showArrow={false}
|
|
||||||
dropdownStyle={{ display: 'none' }}
|
|
||||||
>
|
|
||||||
<Option value="GROUP BY">REDUCE TO</Option>
|
|
||||||
</Select>
|
|
||||||
<Select
|
|
||||||
placeholder="Latest of values in timeframe"
|
|
||||||
style={{ flex: 1 }}
|
|
||||||
options={Object.keys(EReduceOperator)
|
|
||||||
.filter((op) => !(parseInt(op, 10) >= 0))
|
|
||||||
.map((op) => ({
|
|
||||||
label: op,
|
|
||||||
value: EReduceOperator[op as keyof typeof EReduceOperator],
|
|
||||||
}))}
|
|
||||||
defaultValue={
|
|
||||||
EReduceOperator[
|
|
||||||
(queryData.reduceTo as unknown) as keyof typeof EReduceOperator
|
|
||||||
]
|
|
||||||
}
|
|
||||||
onChange={(e): void => {
|
|
||||||
handleQueryChange({ queryIndex, reduceTo: e });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Row>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Row style={{ margin: '0.5rem 0' }}>
|
|
||||||
<Input
|
|
||||||
onChange={(e): void => {
|
|
||||||
handleQueryChange({ queryIndex, legend: e.target.value });
|
|
||||||
}}
|
|
||||||
size="middle"
|
|
||||||
defaultValue={queryData.legend}
|
|
||||||
addonBefore="Legend Format"
|
|
||||||
/>
|
|
||||||
</Row>
|
|
||||||
</div>
|
|
||||||
</QueryHeader>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MetricsBuilder;
|
|
@ -1,24 +0,0 @@
|
|||||||
import {
|
|
||||||
IMetricsBuilderFormula,
|
|
||||||
IMetricsBuilderQuery,
|
|
||||||
} from 'types/api/dashboard/getAll';
|
|
||||||
|
|
||||||
export interface IQueryBuilderQueryHandleChange {
|
|
||||||
queryIndex: number | string;
|
|
||||||
aggregateFunction?: IMetricsBuilderQuery['aggregateOperator'];
|
|
||||||
metricName?: IMetricsBuilderQuery['metricName'];
|
|
||||||
tagFilters?: IMetricsBuilderQuery['tagFilters']['items'];
|
|
||||||
groupBy?: IMetricsBuilderQuery['groupBy'];
|
|
||||||
legend?: IMetricsBuilderQuery['legend'];
|
|
||||||
toggleDisable?: boolean;
|
|
||||||
toggleDelete?: boolean;
|
|
||||||
reduceTo?: IMetricsBuilderQuery['reduceTo'];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IQueryBuilderFormulaHandleChange {
|
|
||||||
formulaIndex: number | string;
|
|
||||||
expression?: IMetricsBuilderFormula['expression'];
|
|
||||||
toggleDisable?: IMetricsBuilderFormula['disabled'];
|
|
||||||
legend?: IMetricsBuilderFormula['legend'];
|
|
||||||
toggleDelete?: boolean;
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
import { Query } from 'types/api/dashboard/getAll';
|
|
||||||
|
|
||||||
import { WIDGET_QUERY_BUILDER_QUERY_KEY_NAME } from '../../constants';
|
|
||||||
|
|
||||||
const QUERY_AND_FORMULA_LIMIT = 10;
|
|
||||||
|
|
||||||
export const canCreateQueryAndFormula = (query: Query): boolean => {
|
|
||||||
const queries = query[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME].queryData;
|
|
||||||
const formulas = query[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME].queryFormulas;
|
|
||||||
|
|
||||||
return queries.length + formulas.length < QUERY_AND_FORMULA_LIMIT;
|
|
||||||
};
|
|
@ -1,10 +0,0 @@
|
|||||||
import { EQueryType } from 'types/common/dashboard';
|
|
||||||
|
|
||||||
export const WIDGET_PROMQL_QUERY_KEY_NAME = EQueryType.PROM;
|
|
||||||
|
|
||||||
export const WIDGET_CLICKHOUSE_QUERY_KEY_NAME = EQueryType.CLICKHOUSE;
|
|
||||||
|
|
||||||
export const WIDGET_QUERY_BUILDER_QUERY_KEY_NAME = EQueryType.QUERY_BUILDER;
|
|
||||||
|
|
||||||
type TFormulas = 'formulas';
|
|
||||||
export const WIDGET_QUERY_BUILDER_FORMULA_KEY_NAME: TFormulas = 'formulas';
|
|
@ -20,10 +20,6 @@ import { EQueryType } from 'types/common/dashboard';
|
|||||||
import DashboardReducer from 'types/reducer/dashboards';
|
import DashboardReducer from 'types/reducer/dashboards';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
import {
|
|
||||||
WIDGET_CLICKHOUSE_QUERY_KEY_NAME,
|
|
||||||
WIDGET_PROMQL_QUERY_KEY_NAME,
|
|
||||||
} from './constants';
|
|
||||||
import ClickHouseQueryContainer from './QueryBuilder/clickHouse';
|
import ClickHouseQueryContainer from './QueryBuilder/clickHouse';
|
||||||
import PromQLQueryContainer from './QueryBuilder/promQL';
|
import PromQLQueryContainer from './QueryBuilder/promQL';
|
||||||
import { IHandleUpdatedQuery } from './types';
|
import { IHandleUpdatedQuery } from './types';
|
||||||
@ -126,7 +122,7 @@ function QuerySection({ updateQuery, selectedGraph }: QueryProps): JSX.Element {
|
|||||||
updateQueryData={({ updatedQuery }: IHandleUpdatedQuery): void => {
|
updateQueryData={({ updatedQuery }: IHandleUpdatedQuery): void => {
|
||||||
handleLocalQueryUpdate({ updatedQuery });
|
handleLocalQueryUpdate({ updatedQuery });
|
||||||
}}
|
}}
|
||||||
clickHouseQueries={localQueryChanges[WIDGET_CLICKHOUSE_QUERY_KEY_NAME]}
|
clickHouseQueries={localQueryChanges[EQueryType.CLICKHOUSE]}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@ -141,7 +137,7 @@ function QuerySection({ updateQuery, selectedGraph }: QueryProps): JSX.Element {
|
|||||||
updateQueryData={({ updatedQuery }: IHandleUpdatedQuery): void => {
|
updateQueryData={({ updatedQuery }: IHandleUpdatedQuery): void => {
|
||||||
handleLocalQueryUpdate({ updatedQuery });
|
handleLocalQueryUpdate({ updatedQuery });
|
||||||
}}
|
}}
|
||||||
promQLQueries={localQueryChanges[WIDGET_PROMQL_QUERY_KEY_NAME]}
|
promQLQueries={localQueryChanges[EQueryType.PROM]}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
@ -1,12 +1,6 @@
|
|||||||
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
||||||
import {
|
import { IClickHouseQuery, IPromQLQuery } from 'types/api/dashboard/getAll';
|
||||||
IClickHouseQuery,
|
import { EQueryType } from 'types/common/dashboard';
|
||||||
IMetricsBuilderFormula,
|
|
||||||
IMetricsBuilderQuery,
|
|
||||||
IPromQLQuery,
|
|
||||||
IQueryBuilderTagFilters,
|
|
||||||
} from 'types/api/dashboard/getAll';
|
|
||||||
import { EAggregateOperator, EQueryType } from 'types/common/dashboard';
|
|
||||||
import { QueryDataResourse } from 'types/common/queryBuilderMappers.types';
|
import { QueryDataResourse } from 'types/common/queryBuilderMappers.types';
|
||||||
|
|
||||||
export interface ICompositeMetricQuery {
|
export interface ICompositeMetricQuery {
|
||||||
@ -32,44 +26,3 @@ export interface IPromQuery extends IPromQLQuery {
|
|||||||
export interface IPromQueries {
|
export interface IPromQueries {
|
||||||
[key: string]: IPromQuery;
|
[key: string]: IPromQuery;
|
||||||
}
|
}
|
||||||
export interface IBuilderQueries {
|
|
||||||
[key: string]: IBuilderQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
// IBuilderQuery combines IMetricQuery and IFormulaQuery
|
|
||||||
// for api calls
|
|
||||||
export interface IBuilderQuery
|
|
||||||
extends Omit<
|
|
||||||
IMetricQuery,
|
|
||||||
'aggregateOperator' | 'legend' | 'metricName' | 'tagFilters'
|
|
||||||
> {
|
|
||||||
aggregateOperator: EAggregateOperator | undefined;
|
|
||||||
disabled: boolean;
|
|
||||||
name: string;
|
|
||||||
legend?: string;
|
|
||||||
metricName: string | null;
|
|
||||||
groupBy?: string[];
|
|
||||||
expression?: string;
|
|
||||||
tagFilters?: IQueryBuilderTagFilters;
|
|
||||||
toggleDisable?: boolean;
|
|
||||||
toggleDelete?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IFormulaQueries {
|
|
||||||
[key: string]: IFormulaQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IFormulaQuery extends IMetricsBuilderFormula {
|
|
||||||
formulaOnly: boolean;
|
|
||||||
queryName: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IMetricQueries {
|
|
||||||
[key: string]: IMetricQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IMetricQuery extends IMetricsBuilderQuery {
|
|
||||||
formulaOnly: boolean;
|
|
||||||
expression?: string;
|
|
||||||
queryName: string;
|
|
||||||
}
|
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
||||||
import { timePreferenceType } from 'container/NewWidget/RightContainer/timeItems';
|
import { timePreferenceType } from 'container/NewWidget/RightContainer/timeItems';
|
||||||
import { Layout } from 'react-grid-layout';
|
import { Layout } from 'react-grid-layout';
|
||||||
import {
|
import { EQueryType } from 'types/common/dashboard';
|
||||||
EAggregateOperator,
|
|
||||||
EQueryType,
|
|
||||||
EReduceOperator,
|
|
||||||
} from 'types/common/dashboard';
|
|
||||||
import { QueryBuilderData } from 'types/common/queryBuilder';
|
import { QueryBuilderData } from 'types/common/queryBuilder';
|
||||||
|
|
||||||
import { QueryData } from '../widgets/getQuery';
|
import { QueryData } from '../widgets/getQuery';
|
||||||
@ -97,28 +93,6 @@ export interface Query {
|
|||||||
clickhouse_sql: IClickHouseQuery[];
|
clickhouse_sql: IClickHouseQuery[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IMetricsBuilderFormula {
|
|
||||||
expression: string;
|
|
||||||
disabled: boolean;
|
|
||||||
name: string;
|
|
||||||
legend: string;
|
|
||||||
}
|
|
||||||
export interface IMetricsBuilderQuery {
|
|
||||||
aggregateOperator: EAggregateOperator;
|
|
||||||
disabled: boolean;
|
|
||||||
name: string;
|
|
||||||
legend: string;
|
|
||||||
metricName: string | null;
|
|
||||||
groupBy?: string[];
|
|
||||||
tagFilters: IQueryBuilderTagFilters;
|
|
||||||
reduceTo?: EReduceOperator;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IQueryBuilderTagFilters {
|
|
||||||
op: string;
|
|
||||||
items: IQueryBuilderTagFilterItems[] | [];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IClickHouseQuery {
|
export interface IClickHouseQuery {
|
||||||
name: string;
|
name: string;
|
||||||
rawQuery: string;
|
rawQuery: string;
|
||||||
|
@ -3,47 +3,3 @@ export enum EQueryType {
|
|||||||
CLICKHOUSE = 'clickhouse_sql',
|
CLICKHOUSE = 'clickhouse_sql',
|
||||||
PROM = 'promql',
|
PROM = 'promql',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum EAggregateOperator {
|
|
||||||
NOOP = 1,
|
|
||||||
COUNT = 2,
|
|
||||||
COUNT_DISTINCT = 3,
|
|
||||||
SUM = 4,
|
|
||||||
AVG = 5,
|
|
||||||
MAX = 6,
|
|
||||||
MIN = 7,
|
|
||||||
P05 = 8,
|
|
||||||
P10 = 9,
|
|
||||||
P20 = 10,
|
|
||||||
P25 = 11,
|
|
||||||
P50 = 12,
|
|
||||||
P75 = 13,
|
|
||||||
P90 = 14,
|
|
||||||
P95 = 15,
|
|
||||||
P99 = 16,
|
|
||||||
RATE = 17,
|
|
||||||
SUM_RATE = 18,
|
|
||||||
// leaving gap for possible future {X}_RATE
|
|
||||||
RATE_SUM = 22,
|
|
||||||
RATE_AVG = 23,
|
|
||||||
RATE_MAX = 24,
|
|
||||||
RATE_MIN = 25,
|
|
||||||
HIST_QUANTILE_50 = 26,
|
|
||||||
HIST_QUANTILE_75 = 27,
|
|
||||||
HIST_QUANTILE_90 = 28,
|
|
||||||
HIST_QUANTILE_95 = 29,
|
|
||||||
HIST_QUANTILE_99 = 30,
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum EPanelType {
|
|
||||||
GRAPH = 'graph',
|
|
||||||
VALUE = 'value',
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum EReduceOperator {
|
|
||||||
'Latest of values in timeframe' = 1, // LAST
|
|
||||||
'Sum of values in timeframe', // SUM
|
|
||||||
'Average of values in timeframe', // AVG
|
|
||||||
'Max of values in timeframe', // MAX
|
|
||||||
'Min of values in timeframe', // MIN
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user