mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-10-17 21:11:31 +08:00
feat: Connect Query builder with graph (#2611)
* fix: having value data type * feat: connect new builder to dashboard * Fix/query builder filters (#2623) * feat: rename query data type * fix: remove reset of groupBy * fix: filters search * fix: calls autocomplete times * fix: response mapper * fix: removee unnecessary field * fix: no check ts types for old query builder * fix: disable check utils old builder
This commit is contained in:
parent
bbda684e65
commit
8c2f33c95a
@ -1,17 +1,17 @@
|
||||
import { ApiV2Instance as axios } from 'api';
|
||||
import { ApiV3Instance as axios } from 'api';
|
||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import {
|
||||
MetricRangePayloadProps,
|
||||
MetricRangePayloadV3,
|
||||
MetricsRangeProps,
|
||||
} from 'types/api/metrics/getQueryRange';
|
||||
|
||||
export const getMetricsQueryRange = async (
|
||||
props: MetricsRangeProps,
|
||||
): Promise<SuccessResponse<MetricRangePayloadProps> | ErrorResponse> => {
|
||||
): Promise<SuccessResponse<MetricRangePayloadV3> | ErrorResponse> => {
|
||||
try {
|
||||
const response = await axios.post(`/metrics/query_range`, props);
|
||||
const response = await axios.post('/query_range', props);
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
|
@ -11,7 +11,6 @@ export const getAggregateKeys = async ({
|
||||
searchText,
|
||||
dataSource,
|
||||
aggregateAttribute,
|
||||
tagType,
|
||||
}: IGetAttributeKeysPayload): Promise<
|
||||
SuccessResponse<IQueryAutocompleteResponse> | ErrorResponse
|
||||
> => {
|
||||
@ -19,7 +18,7 @@ export const getAggregateKeys = async ({
|
||||
const response: AxiosResponse<{
|
||||
data: IQueryAutocompleteResponse;
|
||||
}> = await ApiV3Instance.get(
|
||||
`autocomplete/attribute_keys?aggregateOperator=${aggregateOperator}&dataSource=${dataSource}&aggregateAttribute=${aggregateAttribute}&tagType=${tagType}&searchText=${searchText}`,
|
||||
`autocomplete/attribute_keys?aggregateOperator=${aggregateOperator}&dataSource=${dataSource}&aggregateAttribute=${aggregateAttribute}&searchText=${searchText}`,
|
||||
);
|
||||
|
||||
return {
|
||||
|
@ -1,10 +1,11 @@
|
||||
// ** Helpers
|
||||
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
||||
import { createNewBuilderItemName } from 'lib/newQueryBuilder/createNewBuilderItemName';
|
||||
import { LocalDataType } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import {
|
||||
Having,
|
||||
HavingForm,
|
||||
IBuilderFormula,
|
||||
IBuilderQueryForm,
|
||||
IBuilderQuery,
|
||||
} from 'types/api/queryBuilder/queryBuilderData';
|
||||
import {
|
||||
BoolOperators,
|
||||
@ -12,9 +13,13 @@ import {
|
||||
LogsAggregatorOperator,
|
||||
MetricAggregateOperator,
|
||||
NumberOperators,
|
||||
PanelTypeKeys,
|
||||
QueryAdditionalFilter,
|
||||
ReduceOperators,
|
||||
StringOperators,
|
||||
TracesAggregatorOperator,
|
||||
} from 'types/common/queryBuilder';
|
||||
import { SelectOption } from 'types/common/select';
|
||||
|
||||
export const MAX_FORMULAS = 20;
|
||||
export const MAX_QUERIES = 26;
|
||||
@ -37,45 +42,69 @@ export const mapOfOperators: Record<DataSource, string[]> = {
|
||||
traces: Object.values(TracesAggregatorOperator),
|
||||
};
|
||||
|
||||
export const mapOfFilters: Record<DataSource, string[]> = {
|
||||
// eslint-disable-next-line sonarjs/no-duplicate-string
|
||||
metrics: ['Aggregation interval', 'Having'],
|
||||
logs: ['Order by', 'Limit', 'Having', 'Aggregation interval'],
|
||||
traces: ['Order by', 'Limit', 'Having', 'Aggregation interval'],
|
||||
export const mapOfFilters: Record<DataSource, QueryAdditionalFilter[]> = {
|
||||
metrics: [
|
||||
// eslint-disable-next-line sonarjs/no-duplicate-string
|
||||
{ text: 'Aggregation interval', field: 'stepInterval' },
|
||||
{ text: 'Having', field: 'having' },
|
||||
],
|
||||
logs: [
|
||||
{ text: 'Order by', field: 'orderBy' },
|
||||
{ text: 'Limit', field: 'limit' },
|
||||
{ text: 'Having', field: 'having' },
|
||||
{ text: 'Aggregation interval', field: 'stepInterval' },
|
||||
],
|
||||
traces: [
|
||||
{ text: 'Order by', field: 'orderBy' },
|
||||
{ text: 'Limit', field: 'limit' },
|
||||
{ text: 'Having', field: 'having' },
|
||||
{ text: 'Aggregation interval', field: 'stepInterval' },
|
||||
],
|
||||
};
|
||||
|
||||
export const initialHavingValues: Having = {
|
||||
export const REDUCE_TO_VALUES: SelectOption<ReduceOperators, string>[] = [
|
||||
{ value: 'last', label: 'Latest of values in timeframe' },
|
||||
{ value: 'sum', label: 'Sum of values in timeframe' },
|
||||
{ value: 'avg', label: 'Average of values in timeframe' },
|
||||
{ value: 'max', label: 'Max of values in timeframe' },
|
||||
{ value: 'min', label: 'Min of values in timeframe' },
|
||||
];
|
||||
|
||||
export const initialHavingValues: HavingForm = {
|
||||
columnName: '',
|
||||
op: '',
|
||||
value: [],
|
||||
};
|
||||
|
||||
export const initialAggregateAttribute: IBuilderQueryForm['aggregateAttribute'] = {
|
||||
export const initialAggregateAttribute: IBuilderQuery['aggregateAttribute'] = {
|
||||
dataType: null,
|
||||
key: '',
|
||||
isColumn: null,
|
||||
type: null,
|
||||
};
|
||||
|
||||
export const initialQueryBuilderFormValues: IBuilderQueryForm = {
|
||||
export const initialQueryBuilderFormValues: IBuilderQuery = {
|
||||
dataSource: DataSource.METRICS,
|
||||
queryName: createNewBuilderItemName({ existNames: [], sourceNames: alphabet }),
|
||||
aggregateOperator: Object.values(MetricAggregateOperator)[0],
|
||||
aggregateAttribute: initialAggregateAttribute,
|
||||
tagFilters: { items: [], op: 'AND' },
|
||||
expression: '',
|
||||
expression: createNewBuilderItemName({
|
||||
existNames: [],
|
||||
sourceNames: alphabet,
|
||||
}),
|
||||
disabled: false,
|
||||
having: [],
|
||||
stepInterval: 30,
|
||||
limit: 10,
|
||||
limit: null,
|
||||
orderBy: [],
|
||||
groupBy: [],
|
||||
legend: '',
|
||||
reduceTo: '',
|
||||
reduceTo: 'sum',
|
||||
};
|
||||
|
||||
export const initialFormulaBuilderFormValues: IBuilderFormula = {
|
||||
label: createNewBuilderItemName({
|
||||
queryName: createNewBuilderItemName({
|
||||
existNames: [],
|
||||
sourceNames: formulasNames,
|
||||
}),
|
||||
@ -90,6 +119,14 @@ export const operatorsByTypes: Record<LocalDataType, string[]> = {
|
||||
bool: Object.values(BoolOperators),
|
||||
};
|
||||
|
||||
export const PANEL_TYPES: Record<PanelTypeKeys, GRAPH_TYPES> = {
|
||||
TIME_SERIES: 'graph',
|
||||
VALUE: 'value',
|
||||
TABLE: 'table',
|
||||
LIST: 'list',
|
||||
EMPTY_WIDGET: 'EMPTY_WIDGET',
|
||||
};
|
||||
|
||||
export type IQueryBuilderState = 'search';
|
||||
|
||||
export const QUERY_BUILDER_SEARCH_VALUES = {
|
||||
@ -104,22 +141,22 @@ export const OPERATORS = {
|
||||
NIN: 'NOT_IN',
|
||||
LIKE: 'LIKE',
|
||||
NLIKE: 'NOT_LIKE',
|
||||
EQUALS: '=',
|
||||
NOT_EQUALS: '!=',
|
||||
'=': '=',
|
||||
'!=': '!=',
|
||||
EXISTS: 'EXISTS',
|
||||
NOT_EXISTS: 'NOT_EXISTS',
|
||||
CONTAINS: 'CONTAINS',
|
||||
NOT_CONTAINS: 'NOT_CONTAINS',
|
||||
GTE: '>=',
|
||||
GT: '>',
|
||||
LTE: '<=',
|
||||
LT: '<',
|
||||
'>=': '>=',
|
||||
'>': '>',
|
||||
'<=': '<=',
|
||||
'<': '<',
|
||||
};
|
||||
|
||||
export const QUERY_BUILDER_OPERATORS_BY_TYPES = {
|
||||
string: [
|
||||
OPERATORS.EQUALS,
|
||||
OPERATORS.NOT_EQUALS,
|
||||
OPERATORS['='],
|
||||
OPERATORS['!='],
|
||||
OPERATORS.IN,
|
||||
OPERATORS.NIN,
|
||||
OPERATORS.LIKE,
|
||||
@ -130,48 +167,48 @@ export const QUERY_BUILDER_OPERATORS_BY_TYPES = {
|
||||
OPERATORS.NOT_EXISTS,
|
||||
],
|
||||
number: [
|
||||
OPERATORS.EQUALS,
|
||||
OPERATORS.NOT_EQUALS,
|
||||
OPERATORS['='],
|
||||
OPERATORS['!='],
|
||||
OPERATORS.IN,
|
||||
OPERATORS.NIN,
|
||||
OPERATORS.EXISTS,
|
||||
OPERATORS.NOT_EXISTS,
|
||||
OPERATORS.GTE,
|
||||
OPERATORS.GT,
|
||||
OPERATORS.LTE,
|
||||
OPERATORS.LT,
|
||||
OPERATORS['>='],
|
||||
OPERATORS['>'],
|
||||
OPERATORS['<='],
|
||||
OPERATORS['<'],
|
||||
],
|
||||
boolean: [
|
||||
OPERATORS.EQUALS,
|
||||
OPERATORS.NOT_EQUALS,
|
||||
OPERATORS['='],
|
||||
OPERATORS['!='],
|
||||
OPERATORS.EXISTS,
|
||||
OPERATORS.NOT_EXISTS,
|
||||
],
|
||||
universal: [
|
||||
OPERATORS.EQUALS,
|
||||
OPERATORS.NOT_EQUALS,
|
||||
OPERATORS['='],
|
||||
OPERATORS['!='],
|
||||
OPERATORS.IN,
|
||||
OPERATORS.NIN,
|
||||
OPERATORS.EXISTS,
|
||||
OPERATORS.NOT_EXISTS,
|
||||
OPERATORS.LIKE,
|
||||
OPERATORS.NLIKE,
|
||||
OPERATORS.GTE,
|
||||
OPERATORS.GT,
|
||||
OPERATORS.LTE,
|
||||
OPERATORS.LT,
|
||||
OPERATORS['>='],
|
||||
OPERATORS['>'],
|
||||
OPERATORS['<='],
|
||||
OPERATORS['<'],
|
||||
OPERATORS.CONTAINS,
|
||||
OPERATORS.NOT_CONTAINS,
|
||||
],
|
||||
};
|
||||
|
||||
export const HAVING_OPERATORS: string[] = [
|
||||
OPERATORS.EQUALS,
|
||||
OPERATORS.NOT_EQUALS,
|
||||
OPERATORS['='],
|
||||
OPERATORS['!='],
|
||||
OPERATORS.IN,
|
||||
OPERATORS.NIN,
|
||||
OPERATORS.GTE,
|
||||
OPERATORS.GT,
|
||||
OPERATORS.LTE,
|
||||
OPERATORS.LT,
|
||||
OPERATORS['>='],
|
||||
OPERATORS['>'],
|
||||
OPERATORS['<='],
|
||||
OPERATORS['<'],
|
||||
];
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||
import { StaticLineProps } from 'components/Graph';
|
||||
import Spinner from 'components/Spinner';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import GridGraphComponent from 'container/GridGraphComponent';
|
||||
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
||||
import { timePreferenceType } from 'container/NewWidget/RightContainer/timeItems';
|
||||
@ -32,7 +33,7 @@ interface QueryResponseError {
|
||||
function ChartPreview({
|
||||
name,
|
||||
query,
|
||||
graphType = 'TIME_SERIES',
|
||||
graphType = PANEL_TYPES.TIME_SERIES,
|
||||
selectedTime = 'GLOBAL_TIME',
|
||||
selectedInterval = '5min',
|
||||
headline,
|
||||
@ -66,8 +67,8 @@ function ChartPreview({
|
||||
);
|
||||
case EQueryType.QUERY_BUILDER:
|
||||
return (
|
||||
query.metricsBuilder?.queryBuilder?.length > 0 &&
|
||||
query.metricsBuilder?.queryBuilder[0].metricName !== ''
|
||||
query.builder.queryData.length > 0 &&
|
||||
query.builder.queryData[0].queryName !== ''
|
||||
);
|
||||
default:
|
||||
return false;
|
||||
@ -85,9 +86,9 @@ function ChartPreview({
|
||||
query: query || {
|
||||
queryType: 1,
|
||||
promQL: [],
|
||||
metricsBuilder: {
|
||||
formulas: [],
|
||||
queryBuilder: [],
|
||||
builder: {
|
||||
queryFormulas: [],
|
||||
queryData: [],
|
||||
},
|
||||
clickHouse: [],
|
||||
},
|
||||
@ -127,7 +128,7 @@ function ChartPreview({
|
||||
title={name}
|
||||
data={chartDataSet}
|
||||
isStacked
|
||||
GRAPH_TYPES={graphType || 'TIME_SERIES'}
|
||||
GRAPH_TYPES={graphType || PANEL_TYPES.TIME_SERIES}
|
||||
name={name || 'Chart Preview'}
|
||||
staticLine={staticLine}
|
||||
/>
|
||||
@ -137,7 +138,7 @@ function ChartPreview({
|
||||
}
|
||||
|
||||
ChartPreview.defaultProps = {
|
||||
graphType: 'TIME_SERIES',
|
||||
graphType: PANEL_TYPES.TIME_SERIES,
|
||||
selectedTime: 'GLOBAL_TIME',
|
||||
selectedInterval: '5min',
|
||||
headline: undefined,
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import { Button, Tabs } from 'antd';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import MetricsBuilderFormula from 'container/NewWidget/LeftContainer/QuerySection/QueryBuilder/queryBuilder/formula';
|
||||
import MetricsBuilder from 'container/NewWidget/LeftContainer/QuerySection/QueryBuilder/queryBuilder/query';
|
||||
import {
|
||||
@ -245,7 +246,7 @@ function QuerySection({
|
||||
key={key}
|
||||
queryIndex={key}
|
||||
queryData={toIMetricsBuilderQuery(current)}
|
||||
selectedGraph="TIME_SERIES"
|
||||
selectedGraph={PANEL_TYPES.TIME_SERIES}
|
||||
handleQueryChange={handleMetricQueryChange}
|
||||
/>
|
||||
);
|
||||
|
@ -1,3 +1,6 @@
|
||||
/* eslint-disable */
|
||||
// @ts-ignore
|
||||
// @ts-nocheck
|
||||
import { Time } from 'container/TopNav/DateTimeSelection/config';
|
||||
import {
|
||||
IBuilderQueries,
|
||||
@ -113,6 +116,7 @@ export const prepareStagedQuery = (
|
||||
return {
|
||||
queryType: t,
|
||||
promQL: promList,
|
||||
// TODO: change it later to actual builder
|
||||
metricsBuilder: {
|
||||
formulas: formulaList,
|
||||
queryBuilder: qbList,
|
||||
|
@ -3,6 +3,7 @@ import { ChartData } from 'chart.js';
|
||||
import Graph, { GraphOnClickHandler, StaticLineProps } from 'components/Graph';
|
||||
import { getYAxisFormattedValue } from 'components/Graph/yAxisConfig';
|
||||
import ValueGraph from 'components/ValueGraph';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
||||
import history from 'lib/history';
|
||||
import React from 'react';
|
||||
@ -25,7 +26,7 @@ function GridGraphComponent({
|
||||
|
||||
const isDashboardPage = location.split('/').length === 3;
|
||||
|
||||
if (GRAPH_TYPES === 'TIME_SERIES') {
|
||||
if (GRAPH_TYPES === PANEL_TYPES.TIME_SERIES) {
|
||||
return (
|
||||
<Graph
|
||||
{...{
|
||||
@ -45,7 +46,7 @@ function GridGraphComponent({
|
||||
);
|
||||
}
|
||||
|
||||
if (GRAPH_TYPES === 'VALUE') {
|
||||
if (GRAPH_TYPES === PANEL_TYPES.VALUE) {
|
||||
const value = (((data.datasets[0] || []).data || [])[0] || 0) as number;
|
||||
|
||||
if (data.datasets.length === 0) {
|
||||
|
@ -3,8 +3,8 @@ import updateDashboardApi from 'api/dashboard/update';
|
||||
import {
|
||||
ClickHouseQueryTemplate,
|
||||
PromQLQueryTemplate,
|
||||
QueryBuilderQueryTemplate,
|
||||
} from 'constants/dashboard';
|
||||
import { initialQueryBuilderFormValues } from 'constants/queryBuilder';
|
||||
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
||||
import GetQueryName from 'lib/query/GetQueryName';
|
||||
import { Layout } from 'react-grid-layout';
|
||||
@ -54,14 +54,9 @@ export const UpdateDashboard = async (
|
||||
...ClickHouseQueryTemplate,
|
||||
},
|
||||
],
|
||||
metricsBuilder: {
|
||||
formulas: [],
|
||||
queryBuilder: [
|
||||
{
|
||||
name: GetQueryName([]) || '',
|
||||
...QueryBuilderQueryTemplate,
|
||||
},
|
||||
],
|
||||
builder: {
|
||||
queryFormulas: [],
|
||||
queryData: [initialQueryBuilderFormValues],
|
||||
},
|
||||
},
|
||||
queryData: {
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
@ -7,7 +8,7 @@ export const getWidgetQueryBuilder = (query: Widgets['query']): Widgets => ({
|
||||
isStacked: false,
|
||||
nullZeroValues: '',
|
||||
opacity: '0',
|
||||
panelTypes: 'TIME_SERIES',
|
||||
panelTypes: PANEL_TYPES.TIME_SERIES,
|
||||
query,
|
||||
queryData: {
|
||||
data: { queryData: [] },
|
||||
|
@ -1,3 +1,5 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
import { Col } from 'antd';
|
||||
import FullView from 'container/GridGraphLayout/Graph/FullView/index.metricsBuilder';
|
||||
import {
|
||||
@ -48,6 +50,7 @@ function DBCall({ getWidgetQueryBuilder }: DBCallProps): JSX.Element {
|
||||
getWidgetQueryBuilder({
|
||||
queryType: 1,
|
||||
promQL: [],
|
||||
// TODO: change it later to actual builder
|
||||
metricsBuilder: databaseCallsRPS({
|
||||
servicename,
|
||||
legend,
|
||||
@ -62,6 +65,7 @@ function DBCall({ getWidgetQueryBuilder }: DBCallProps): JSX.Element {
|
||||
getWidgetQueryBuilder({
|
||||
queryType: 1,
|
||||
promQL: [],
|
||||
// TODO: change it later to actual builder
|
||||
metricsBuilder: databaseCallsAvgDuration({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
|
@ -1,3 +1,5 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
import { Col } from 'antd';
|
||||
import FullView from 'container/GridGraphLayout/Graph/FullView/index.metricsBuilder';
|
||||
import {
|
||||
@ -41,6 +43,7 @@ function External({ getWidgetQueryBuilder }: ExternalProps): JSX.Element {
|
||||
getWidgetQueryBuilder({
|
||||
queryType: 1,
|
||||
promQL: [],
|
||||
// TODO: change it later to actual builder
|
||||
metricsBuilder: externalCallErrorPercent({
|
||||
servicename,
|
||||
legend: legend.address,
|
||||
@ -61,6 +64,7 @@ function External({ getWidgetQueryBuilder }: ExternalProps): JSX.Element {
|
||||
getWidgetQueryBuilder({
|
||||
queryType: 1,
|
||||
promQL: [],
|
||||
// TODO: change it later to actual builder
|
||||
metricsBuilder: externalCallDuration({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
@ -75,6 +79,7 @@ function External({ getWidgetQueryBuilder }: ExternalProps): JSX.Element {
|
||||
getWidgetQueryBuilder({
|
||||
queryType: 1,
|
||||
promQL: [],
|
||||
// TODO: change it later to actual builder
|
||||
metricsBuilder: externalCallRpsByAddress({
|
||||
servicename,
|
||||
legend: legend.address,
|
||||
@ -90,6 +95,7 @@ function External({ getWidgetQueryBuilder }: ExternalProps): JSX.Element {
|
||||
getWidgetQueryBuilder({
|
||||
queryType: 1,
|
||||
promQL: [],
|
||||
// TODO: change it later to actual builder
|
||||
metricsBuilder: externalCallDurationByAddress({
|
||||
servicename,
|
||||
legend: legend.address,
|
||||
|
@ -1,3 +1,5 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
import { ActiveElement, Chart, ChartData, ChartEvent } from 'chart.js';
|
||||
import Graph from 'components/Graph';
|
||||
import { QueryParams } from 'constants/query';
|
||||
@ -84,6 +86,7 @@ function Application({ getWidgetQueryBuilder }: DashboardProps): JSX.Element {
|
||||
getWidgetQueryBuilder({
|
||||
queryType: 1,
|
||||
promQL: [],
|
||||
// TODO: change it later to actual builder
|
||||
metricsBuilder: operationPerSec({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
@ -99,6 +102,7 @@ function Application({ getWidgetQueryBuilder }: DashboardProps): JSX.Element {
|
||||
getWidgetQueryBuilder({
|
||||
queryType: 1,
|
||||
promQL: [],
|
||||
// TODO: change it later to actual builder
|
||||
metricsBuilder: errorPercentage({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
|
@ -1,20 +1,21 @@
|
||||
import TimeSeries from 'assets/Dashboard/TimeSeries';
|
||||
import ValueIcon from 'assets/Dashboard/Value';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
|
||||
const Items: ItemsProps[] = [
|
||||
{
|
||||
name: 'TIME_SERIES',
|
||||
name: PANEL_TYPES.TIME_SERIES,
|
||||
Icon: TimeSeries,
|
||||
display: 'Time Series',
|
||||
},
|
||||
{
|
||||
name: 'VALUE',
|
||||
name: PANEL_TYPES.VALUE,
|
||||
Icon: ValueIcon,
|
||||
display: 'Value',
|
||||
},
|
||||
];
|
||||
|
||||
export type ITEMS = 'TIME_SERIES' | 'VALUE' | 'EMPTY_WIDGET';
|
||||
export type ITEMS = 'graph' | 'value' | 'list' | 'table' | 'EMPTY_WIDGET';
|
||||
|
||||
interface ItemsProps {
|
||||
name: ITEMS;
|
||||
|
@ -1,3 +1,6 @@
|
||||
/* eslint-disable */
|
||||
// TODO: fix it after merge actual functionality
|
||||
// @ts-nocheck
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
QueryBuilderFormulaTemplate,
|
||||
|
@ -1,5 +1,6 @@
|
||||
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';
|
||||
@ -142,7 +143,7 @@ function MetricsBuilder({
|
||||
/>
|
||||
</Row>
|
||||
<Row style={{ gap: '3%', marginBottom: '1rem' }}>
|
||||
{selectedGraph === 'TIME_SERIES' ? (
|
||||
{selectedGraph === PANEL_TYPES.TIME_SERIES ? (
|
||||
<>
|
||||
{' '}
|
||||
<Select
|
||||
|
@ -1,3 +1,6 @@
|
||||
/* eslint-disable */
|
||||
// TODO: fix it after merge actual functionality
|
||||
// @ts-nocheck
|
||||
import { Query } from 'types/api/dashboard/getAll';
|
||||
|
||||
import {
|
||||
|
@ -4,6 +4,7 @@ import TextToolTip from 'components/TextToolTip';
|
||||
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
||||
import { timePreferance } from 'container/NewWidget/RightContainer/timeItems';
|
||||
import { QueryBuilder } from 'container/QueryBuilder';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { cloneDeep, isEqual } from 'lodash-es';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { connect, useSelector } from 'react-redux';
|
||||
@ -24,11 +25,9 @@ import { v4 as uuid } from 'uuid';
|
||||
import {
|
||||
WIDGET_CLICKHOUSE_QUERY_KEY_NAME,
|
||||
WIDGET_PROMQL_QUERY_KEY_NAME,
|
||||
WIDGET_QUERY_BUILDER_QUERY_KEY_NAME,
|
||||
} from './constants';
|
||||
import ClickHouseQueryContainer from './QueryBuilder/clickHouse';
|
||||
import PromQLQueryContainer from './QueryBuilder/promQL';
|
||||
import QueryBuilderQueryContainer from './QueryBuilder/queryBuilder';
|
||||
import TabHeader from './TabHeader';
|
||||
import { IHandleUpdatedQuery } from './types';
|
||||
import { getQueryKey } from './utils/getQueryKey';
|
||||
@ -39,6 +38,7 @@ function QuerySection({
|
||||
updateQuery,
|
||||
selectedGraph,
|
||||
}: QueryProps): JSX.Element {
|
||||
const { queryBuilderData, initQueryBuilderData } = useQueryBuilder();
|
||||
const [localQueryChanges, setLocalQueryChanges] = useState<Query>({} as Query);
|
||||
const [rctTabKey, setRctTabKey] = useState<
|
||||
Record<keyof typeof EQueryType, string>
|
||||
@ -47,9 +47,10 @@ function QuerySection({
|
||||
CLICKHOUSE: uuid(),
|
||||
PROM: uuid(),
|
||||
});
|
||||
const { dashboards } = useSelector<AppState, DashboardReducer>(
|
||||
(state) => state.dashboards,
|
||||
);
|
||||
const { dashboards, isLoadingQueryResult } = useSelector<
|
||||
AppState,
|
||||
DashboardReducer
|
||||
>((state) => state.dashboards);
|
||||
const [selectedDashboards] = dashboards;
|
||||
const { search } = useLocation();
|
||||
const { widgets } = selectedDashboards.data;
|
||||
@ -68,9 +69,9 @@ function QuerySection({
|
||||
|
||||
const { query } = selectedWidget || {};
|
||||
useEffect(() => {
|
||||
initQueryBuilderData(query.builder);
|
||||
setLocalQueryChanges(cloneDeep(query) as Query);
|
||||
}, [query]);
|
||||
|
||||
}, [query, initQueryBuilderData]);
|
||||
const queryDiff = (
|
||||
queryA: Query,
|
||||
queryB: Query,
|
||||
@ -99,7 +100,10 @@ function QuerySection({
|
||||
|
||||
const handleStageQuery = (): void => {
|
||||
updateQuery({
|
||||
updatedQuery: localQueryChanges,
|
||||
updatedQuery: {
|
||||
...localQueryChanges,
|
||||
builder: queryBuilderData,
|
||||
},
|
||||
widgetId: urlQuery.get('widgetId') || '',
|
||||
yAxisUnit: selectedWidget.yAxisUnit,
|
||||
});
|
||||
@ -155,22 +159,7 @@ function QuerySection({
|
||||
)}
|
||||
/>
|
||||
),
|
||||
children: (
|
||||
<QueryBuilderQueryContainer
|
||||
key={rctTabKey.QUERY_BUILDER}
|
||||
queryData={localQueryChanges}
|
||||
updateQueryData={({ updatedQuery }: IHandleUpdatedQuery): void => {
|
||||
handleLocalQueryUpdate({ updatedQuery });
|
||||
}}
|
||||
metricsBuilderQueries={
|
||||
localQueryChanges[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME]
|
||||
}
|
||||
selectedGraph={selectedGraph}
|
||||
/>
|
||||
|
||||
// TODO: uncomment for testing new QueryBuilder
|
||||
// <QueryBuilder panelType={selectedGraph} />
|
||||
),
|
||||
children: <QueryBuilder panelType={selectedGraph} />,
|
||||
},
|
||||
{
|
||||
key: EQueryType.CLICKHOUSE.toString(),
|
||||
@ -234,7 +223,11 @@ function QuerySection({
|
||||
text: `This will temporarily save the current query and graph state. This will persist across tab change`,
|
||||
}}
|
||||
/>
|
||||
<Button type="primary" onClick={handleStageQuery}>
|
||||
<Button
|
||||
loading={isLoadingQueryResult}
|
||||
type="primary"
|
||||
onClick={handleStageQuery}
|
||||
>
|
||||
Stage & Run Query
|
||||
</Button>
|
||||
</span>
|
||||
|
@ -9,7 +9,7 @@ export enum EQueryCategories {
|
||||
}
|
||||
|
||||
export enum EQueryTypeToQueryKeyMapping {
|
||||
QUERY_BUILDER = 'metricsBuilder',
|
||||
QUERY_BUILDER = 'builder',
|
||||
CLICKHOUSE = 'clickHouse',
|
||||
PROM = 'promQL',
|
||||
}
|
||||
|
@ -10,5 +10,5 @@ export type QueryBuilderConfig =
|
||||
|
||||
export type QueryBuilderProps = {
|
||||
config?: QueryBuilderConfig;
|
||||
panelType?: ITEMS;
|
||||
panelType: ITEMS;
|
||||
};
|
||||
|
@ -2,7 +2,7 @@ import { PlusOutlined } from '@ant-design/icons';
|
||||
import { Button, Col, Row } from 'antd';
|
||||
import { MAX_FORMULAS, MAX_QUERIES } from 'constants/queryBuilder';
|
||||
// ** Hooks
|
||||
import { useQueryBuilder } from 'hooks/useQueryBuilder';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
// ** Constants
|
||||
import React, { memo, useEffect, useMemo } from 'react';
|
||||
|
||||
@ -19,6 +19,7 @@ export const QueryBuilder = memo(function QueryBuilder({
|
||||
const {
|
||||
queryBuilderData,
|
||||
setupInitialDataSource,
|
||||
resetQueryBuilderData,
|
||||
addNewQuery,
|
||||
addNewFormula,
|
||||
} = useQueryBuilder();
|
||||
@ -33,6 +34,13 @@ export const QueryBuilder = memo(function QueryBuilder({
|
||||
};
|
||||
}, [config, setupInitialDataSource]);
|
||||
|
||||
useEffect(
|
||||
() => (): void => {
|
||||
resetQueryBuilderData();
|
||||
},
|
||||
[resetQueryBuilderData],
|
||||
);
|
||||
|
||||
const isDisabledQueryButton = useMemo(
|
||||
() => queryBuilderData.queryData.length >= MAX_QUERIES,
|
||||
[queryBuilderData],
|
||||
@ -59,7 +67,7 @@ export const QueryBuilder = memo(function QueryBuilder({
|
||||
</Col>
|
||||
))}
|
||||
{queryBuilderData.queryFormulas.map((formula, index) => (
|
||||
<Col key={formula.label} span={24}>
|
||||
<Col key={formula.queryName} span={24}>
|
||||
<Formula formula={formula} index={index} />
|
||||
</Col>
|
||||
))}
|
||||
|
@ -23,10 +23,12 @@ export const AdditionalFiltersToggler = memo(function AdditionalFiltersToggler({
|
||||
|
||||
const filtersTexts: ReactNode = listOfAdditionalFilter.map((str, index) => {
|
||||
const isNextLast = index + 1 === listOfAdditionalFilter.length - 1;
|
||||
|
||||
if (index === listOfAdditionalFilter.length - 1) {
|
||||
return (
|
||||
<Fragment key={str}>
|
||||
and <StyledLink>{str.toUpperCase()}</StyledLink>
|
||||
{listOfAdditionalFilter.length > 1 && 'and'}{' '}
|
||||
<StyledLink>{str.toUpperCase()}</StyledLink>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import { Col, Input } from 'antd';
|
||||
// ** Components
|
||||
import { ListItemWrapper, ListMarker } from 'container/QueryBuilder/components';
|
||||
// ** Hooks
|
||||
import { useQueryBuilder } from 'hooks/useQueryBuilder';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import React, { ChangeEvent, useCallback } from 'react';
|
||||
import { IBuilderFormula } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
@ -46,7 +46,7 @@ export function Formula({ index, formula }: FormulaProps): JSX.Element {
|
||||
<ListMarker
|
||||
isDisabled={formula.disabled}
|
||||
onDisable={handleToggleDisableFormula}
|
||||
labelName={formula.label}
|
||||
labelName={formula.queryName}
|
||||
index={index}
|
||||
/>
|
||||
</Col>
|
||||
|
@ -0,0 +1,10 @@
|
||||
export type HavingFilterTagProps = {
|
||||
label: React.ReactNode;
|
||||
value: string;
|
||||
disabled: boolean;
|
||||
onClose: (event?: React.MouseEvent<HTMLElement, MouseEvent>) => void;
|
||||
closable: boolean;
|
||||
onUpdate: (value: string) => void;
|
||||
};
|
||||
|
||||
export type HavingTagRenderProps = Omit<HavingFilterTagProps, 'onUpdate'>;
|
@ -0,0 +1,13 @@
|
||||
import { Tag, Typography } from 'antd';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const StyledText = styled(Typography.Text)`
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
export const StyledTag = styled(Tag)`
|
||||
margin-top: 0.125rem;
|
||||
margin-bottom: 0.125rem;
|
||||
padding-left: 0.5rem;
|
||||
display: flex;
|
||||
`;
|
@ -0,0 +1,23 @@
|
||||
import React from 'react';
|
||||
|
||||
import { HavingFilterTagProps } from './HavingFilterTag.interfaces';
|
||||
import { StyledTag, StyledText } from './HavingFilterTag.styled';
|
||||
|
||||
export function HavingFilterTag({
|
||||
value,
|
||||
closable,
|
||||
onClose,
|
||||
onUpdate,
|
||||
}: HavingFilterTagProps): JSX.Element {
|
||||
const handleClick = (): void => {
|
||||
onUpdate(value);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledTag closable={closable} onClose={onClose}>
|
||||
<StyledText ellipsis onClick={handleClick}>
|
||||
{value}
|
||||
</StyledText>
|
||||
</StyledTag>
|
||||
);
|
||||
}
|
@ -0,0 +1 @@
|
||||
export { HavingFilterTag } from './HavingFilterTag';
|
@ -1,10 +1,10 @@
|
||||
import { ITEMS } from 'container/NewDashboard/ComponentsSlider/menuItems';
|
||||
import { IBuilderQueryForm } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
export type QueryProps = {
|
||||
index: number;
|
||||
isAvailableToDisable: boolean;
|
||||
query: IBuilderQueryForm;
|
||||
query: IBuilderQuery;
|
||||
queryVariant: 'static' | 'dropdown';
|
||||
panelType?: ITEMS;
|
||||
panelType: ITEMS;
|
||||
};
|
||||
|
@ -1,11 +1,6 @@
|
||||
import { Col, Input, Row } from 'antd';
|
||||
// ** Constants
|
||||
import {
|
||||
initialAggregateAttribute,
|
||||
initialQueryBuilderFormValues,
|
||||
mapOfFilters,
|
||||
mapOfOperators,
|
||||
} from 'constants/queryBuilder';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
// ** Components
|
||||
import {
|
||||
AdditionalFiltersToggler,
|
||||
@ -25,17 +20,10 @@ import AggregateEveryFilter from 'container/QueryBuilder/filters/AggregateEveryF
|
||||
import LimitFilter from 'container/QueryBuilder/filters/LimitFilter/LimitFilter';
|
||||
import { OrderByFilter } from 'container/QueryBuilder/filters/OrderByFilter';
|
||||
import QueryBuilderSearch from 'container/QueryBuilder/filters/QueryBuilderSearch';
|
||||
import { useQueryBuilder } from 'hooks/useQueryBuilder';
|
||||
import { findDataTypeOfOperator } from 'lib/query/findDataTypeOfOperator';
|
||||
import { useQueryOperations } from 'hooks/queryBuilder/useQueryOperations';
|
||||
// ** Hooks
|
||||
import React, { memo, useCallback, useMemo } from 'react';
|
||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import {
|
||||
Having,
|
||||
IBuilderQueryForm,
|
||||
TagFilter,
|
||||
} from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { DataSource, StringOperators } from 'types/common/queryBuilder';
|
||||
import React, { ChangeEvent, memo, ReactNode, useCallback } from 'react';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { transformToUpperCase } from 'utils/transformToUpperCase';
|
||||
|
||||
// ** Types
|
||||
@ -49,283 +37,82 @@ export const Query = memo(function Query({
|
||||
panelType,
|
||||
}: QueryProps): JSX.Element {
|
||||
const {
|
||||
handleSetQueryData,
|
||||
removeEntityByIndex,
|
||||
initialDataSource,
|
||||
} = useQueryBuilder();
|
||||
operators,
|
||||
isMetricsDataSource,
|
||||
listOfAdditionalFilters,
|
||||
handleChangeAggregatorAttribute,
|
||||
handleChangeDataSource,
|
||||
handleChangeQueryData,
|
||||
handleChangeOperator,
|
||||
handleDeleteQuery,
|
||||
} = useQueryOperations({ index, query, panelType });
|
||||
|
||||
const currentListOfOperators = useMemo(
|
||||
() => mapOfOperators[query.dataSource],
|
||||
[query],
|
||||
);
|
||||
const listOfAdditionalFilters = useMemo(() => mapOfFilters[query.dataSource], [
|
||||
query,
|
||||
]);
|
||||
|
||||
const handleChangeOperator = useCallback(
|
||||
(value: string): void => {
|
||||
const aggregateDataType: BaseAutocompleteData['dataType'] =
|
||||
query.aggregateAttribute.dataType;
|
||||
|
||||
const newQuery: IBuilderQueryForm = {
|
||||
...query,
|
||||
aggregateOperator: value,
|
||||
having: [],
|
||||
limit: null,
|
||||
tagFilters: { items: [], op: 'AND' },
|
||||
};
|
||||
|
||||
if (!aggregateDataType) {
|
||||
handleSetQueryData(index, newQuery);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (aggregateDataType) {
|
||||
case 'string':
|
||||
case 'bool': {
|
||||
const typeOfValue = findDataTypeOfOperator(value);
|
||||
|
||||
handleSetQueryData(index, {
|
||||
...newQuery,
|
||||
...(typeOfValue === 'number'
|
||||
? { aggregateAttribute: initialAggregateAttribute }
|
||||
: {}),
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
case 'float64':
|
||||
case 'int64': {
|
||||
handleSetQueryData(index, newQuery);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
handleSetQueryData(index, newQuery);
|
||||
break;
|
||||
}
|
||||
}
|
||||
const handleChangeAggregateEvery = useCallback(
|
||||
(value: IBuilderQuery['stepInterval']) => {
|
||||
handleChangeQueryData('stepInterval', value);
|
||||
},
|
||||
[index, query, handleSetQueryData],
|
||||
);
|
||||
|
||||
const handleChangeAggregatorAttribute = useCallback(
|
||||
(value: BaseAutocompleteData): void => {
|
||||
const newQuery: IBuilderQueryForm = {
|
||||
...query,
|
||||
aggregateAttribute: value,
|
||||
having: [],
|
||||
};
|
||||
|
||||
handleSetQueryData(index, newQuery);
|
||||
},
|
||||
[index, query, handleSetQueryData],
|
||||
);
|
||||
|
||||
const handleChangeDataSource = useCallback(
|
||||
(nextSource: DataSource): void => {
|
||||
let newQuery: IBuilderQueryForm = {
|
||||
...query,
|
||||
dataSource: nextSource,
|
||||
};
|
||||
|
||||
if (nextSource !== query.dataSource) {
|
||||
const initCopy = {
|
||||
...(initialQueryBuilderFormValues as Partial<IBuilderQueryForm>),
|
||||
};
|
||||
delete initCopy.queryName;
|
||||
|
||||
newQuery = {
|
||||
...newQuery,
|
||||
...initCopy,
|
||||
dataSource: initialDataSource || nextSource,
|
||||
aggregateOperator: mapOfOperators[nextSource][0],
|
||||
};
|
||||
}
|
||||
|
||||
handleSetQueryData(index, newQuery);
|
||||
},
|
||||
[index, query, initialDataSource, handleSetQueryData],
|
||||
);
|
||||
|
||||
const handleToggleDisableQuery = useCallback((): void => {
|
||||
const newQuery: IBuilderQueryForm = {
|
||||
...query,
|
||||
disabled: !query.disabled,
|
||||
};
|
||||
|
||||
handleSetQueryData(index, newQuery);
|
||||
}, [index, query, handleSetQueryData]);
|
||||
|
||||
const handleChangeGroupByKeys = useCallback(
|
||||
(values: BaseAutocompleteData[]): void => {
|
||||
const newQuery: IBuilderQueryForm = {
|
||||
...query,
|
||||
groupBy: values,
|
||||
};
|
||||
|
||||
handleSetQueryData(index, newQuery);
|
||||
},
|
||||
[index, query, handleSetQueryData],
|
||||
);
|
||||
|
||||
const handleChangeQueryLegend = useCallback(
|
||||
(e: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
const newQuery: IBuilderQueryForm = {
|
||||
...query,
|
||||
legend: e.target.value,
|
||||
};
|
||||
handleSetQueryData(index, newQuery);
|
||||
},
|
||||
[index, query, handleSetQueryData],
|
||||
);
|
||||
|
||||
const handleChangeReduceTo = useCallback(
|
||||
(value: string): void => {
|
||||
const newQuery: IBuilderQueryForm = {
|
||||
...query,
|
||||
reduceTo: value,
|
||||
};
|
||||
handleSetQueryData(index, newQuery);
|
||||
},
|
||||
[index, query, handleSetQueryData],
|
||||
);
|
||||
|
||||
const handleChangeHavingFilter = useCallback(
|
||||
(having: Having[]) => {
|
||||
const newQuery: IBuilderQueryForm = { ...query, having };
|
||||
|
||||
handleSetQueryData(index, newQuery);
|
||||
},
|
||||
[index, query, handleSetQueryData],
|
||||
);
|
||||
|
||||
const handleDeleteQuery = useCallback(() => {
|
||||
removeEntityByIndex('queryData', index);
|
||||
}, [removeEntityByIndex, index]);
|
||||
|
||||
const isMatricsDataSource = useMemo(
|
||||
() => query.dataSource === DataSource.METRICS,
|
||||
[query.dataSource],
|
||||
);
|
||||
|
||||
const handleChangeOrderByKeys = useCallback(
|
||||
(values: BaseAutocompleteData[]): void => {
|
||||
const newQuery: IBuilderQueryForm = {
|
||||
...query,
|
||||
orderBy: values,
|
||||
};
|
||||
handleSetQueryData(index, newQuery);
|
||||
},
|
||||
[handleSetQueryData, index, query],
|
||||
[handleChangeQueryData],
|
||||
);
|
||||
|
||||
const handleChangeLimit = useCallback(
|
||||
(value: number | null): void => {
|
||||
const newQuery: IBuilderQueryForm = {
|
||||
...query,
|
||||
limit: value,
|
||||
};
|
||||
handleSetQueryData(index, newQuery);
|
||||
(value: IBuilderQuery['limit']) => {
|
||||
handleChangeQueryData('limit', value);
|
||||
},
|
||||
[index, query, handleSetQueryData],
|
||||
[handleChangeQueryData],
|
||||
);
|
||||
|
||||
const handleChangeAggregateEvery = useCallback(
|
||||
(value: number): void => {
|
||||
const newQuery: IBuilderQueryForm = {
|
||||
...query,
|
||||
stepInterval: value,
|
||||
};
|
||||
handleSetQueryData(index, newQuery);
|
||||
const handleChangeHavingFilter = useCallback(
|
||||
(value: IBuilderQuery['having']) => {
|
||||
handleChangeQueryData('having', value);
|
||||
},
|
||||
[index, query, handleSetQueryData],
|
||||
[handleChangeQueryData],
|
||||
);
|
||||
|
||||
const handleChangeOrderByKeys = useCallback(
|
||||
(value: IBuilderQuery['orderBy']) => {
|
||||
handleChangeQueryData('orderBy', value);
|
||||
},
|
||||
[handleChangeQueryData],
|
||||
);
|
||||
|
||||
const handleToggleDisableQuery = useCallback(() => {
|
||||
handleChangeQueryData('disabled', !query.disabled);
|
||||
}, [handleChangeQueryData, query]);
|
||||
|
||||
const handleChangeTagFilters = useCallback(
|
||||
(value: TagFilter): void => {
|
||||
const newQuery: IBuilderQueryForm = {
|
||||
...query,
|
||||
tagFilters: value,
|
||||
};
|
||||
handleSetQueryData(index, newQuery);
|
||||
(value: IBuilderQuery['tagFilters']) => {
|
||||
handleChangeQueryData('tagFilters', value);
|
||||
},
|
||||
[index, query, handleSetQueryData],
|
||||
[handleChangeQueryData],
|
||||
);
|
||||
|
||||
return (
|
||||
<ListItemWrapper onDelete={handleDeleteQuery}>
|
||||
<Col span={24}>
|
||||
<Row align="middle">
|
||||
<Col>
|
||||
<ListMarker
|
||||
isDisabled={query.disabled}
|
||||
onDisable={handleToggleDisableQuery}
|
||||
labelName={query.queryName}
|
||||
index={index}
|
||||
isAvailableToDisable={isAvailableToDisable}
|
||||
/>
|
||||
{queryVariant === 'dropdown' ? (
|
||||
<DataSourceDropdown
|
||||
onChange={handleChangeDataSource}
|
||||
value={query.dataSource}
|
||||
style={{ marginRight: '0.5rem' }}
|
||||
/>
|
||||
) : (
|
||||
<FilterLabel label={transformToUpperCase(query.dataSource)} />
|
||||
)}
|
||||
</Col>
|
||||
<Col flex="1">
|
||||
<Row gutter={[11, 5]}>
|
||||
{isMatricsDataSource && (
|
||||
<Col>
|
||||
<FilterLabel label="WHERE" />
|
||||
</Col>
|
||||
)}
|
||||
<Col flex="1">
|
||||
<QueryBuilderSearch query={query} onChange={handleChangeTagFilters} />
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
<Col span={11}>
|
||||
<Row gutter={[11, 5]}>
|
||||
<Col flex="5.93rem">
|
||||
<OperatorsSelect
|
||||
value={query.aggregateOperator || currentListOfOperators[0]}
|
||||
onChange={handleChangeOperator}
|
||||
operators={currentListOfOperators}
|
||||
/>
|
||||
</Col>
|
||||
<Col flex="1 1 12.5rem">
|
||||
<AggregatorFilter
|
||||
onChange={handleChangeAggregatorAttribute}
|
||||
query={query}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
const handleChangeReduceTo = useCallback(
|
||||
(value: IBuilderQuery['reduceTo']) => {
|
||||
handleChangeQueryData('reduceTo', value);
|
||||
},
|
||||
[handleChangeQueryData],
|
||||
);
|
||||
|
||||
<Col span={11} offset={2}>
|
||||
<Row gutter={[11, 5]}>
|
||||
<Col flex="5.93rem">
|
||||
<FilterLabel label={panelType === 'VALUE' ? 'Reduce to' : 'Group by'} />
|
||||
</Col>
|
||||
<Col flex="1 1 12.5rem">
|
||||
{panelType === 'VALUE' ? (
|
||||
<ReduceToFilter query={query} onChange={handleChangeReduceTo} />
|
||||
) : (
|
||||
<GroupByFilter query={query} onChange={handleChangeGroupByKeys} />
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<AdditionalFiltersToggler listOfAdditionalFilter={listOfAdditionalFilters}>
|
||||
<Row gutter={[0, 11]} justify="space-between">
|
||||
{!isMatricsDataSource && (
|
||||
const handleChangeGroupByKeys = useCallback(
|
||||
(value: IBuilderQuery['groupBy']) => {
|
||||
handleChangeQueryData('groupBy', value);
|
||||
},
|
||||
[handleChangeQueryData],
|
||||
);
|
||||
|
||||
const handleChangeQueryLegend = useCallback(
|
||||
(event: ChangeEvent<HTMLInputElement>) => {
|
||||
handleChangeQueryData('legend', event.target.value);
|
||||
},
|
||||
[handleChangeQueryData],
|
||||
);
|
||||
|
||||
const renderAdditionalFilters = useCallback((): ReactNode => {
|
||||
switch (panelType) {
|
||||
case PANEL_TYPES.TIME_SERIES: {
|
||||
return (
|
||||
<>
|
||||
{!isMetricsDataSource && (
|
||||
<Col span={11}>
|
||||
<Row gutter={[11, 5]}>
|
||||
<Col flex="5.93rem">
|
||||
@ -337,19 +124,17 @@ export const Query = memo(function Query({
|
||||
</Row>
|
||||
</Col>
|
||||
)}
|
||||
{query.aggregateOperator !== StringOperators.NOOP && (
|
||||
<Col span={11}>
|
||||
<Row gutter={[11, 5]}>
|
||||
<Col flex="5.93rem">
|
||||
<FilterLabel label="HAVING" />
|
||||
</Col>
|
||||
<Col flex="1 1 12.5rem">
|
||||
<HavingFilter onChange={handleChangeHavingFilter} query={query} />
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
)}
|
||||
{!isMatricsDataSource && (
|
||||
<Col span={11}>
|
||||
<Row gutter={[11, 5]}>
|
||||
<Col flex="5.93rem">
|
||||
<FilterLabel label="HAVING" />
|
||||
</Col>
|
||||
<Col flex="1 1 12.5rem">
|
||||
<HavingFilter onChange={handleChangeHavingFilter} query={query} />
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
{!isMetricsDataSource && (
|
||||
<Col span={11}>
|
||||
<Row gutter={[11, 5]}>
|
||||
<Col flex="5.93rem">
|
||||
@ -375,6 +160,132 @@ export const Query = memo(function Query({
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
case PANEL_TYPES.VALUE: {
|
||||
return (
|
||||
<>
|
||||
<Col span={11}>
|
||||
<Row gutter={[11, 5]}>
|
||||
<Col flex="5.93rem">
|
||||
<FilterLabel label="HAVING" />
|
||||
</Col>
|
||||
<Col flex="1 1 12.5rem">
|
||||
<HavingFilter onChange={handleChangeHavingFilter} query={query} />
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
<Col span={11}>
|
||||
<Row gutter={[11, 5]}>
|
||||
<Col flex="5.93rem">
|
||||
<FilterLabel label="Aggregate Every" />
|
||||
</Col>
|
||||
<Col flex="1 1 6rem">
|
||||
<AggregateEveryFilter
|
||||
query={query}
|
||||
onChange={handleChangeAggregateEvery}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
default: {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}, [
|
||||
panelType,
|
||||
query,
|
||||
isMetricsDataSource,
|
||||
handleChangeAggregateEvery,
|
||||
handleChangeHavingFilter,
|
||||
handleChangeLimit,
|
||||
handleChangeOrderByKeys,
|
||||
]);
|
||||
|
||||
return (
|
||||
<ListItemWrapper onDelete={handleDeleteQuery}>
|
||||
<Col span={24}>
|
||||
<Row align="middle">
|
||||
<Col>
|
||||
<ListMarker
|
||||
isDisabled={query.disabled}
|
||||
onDisable={handleToggleDisableQuery}
|
||||
labelName={query.queryName}
|
||||
index={index}
|
||||
isAvailableToDisable={isAvailableToDisable}
|
||||
/>
|
||||
{queryVariant === 'dropdown' ? (
|
||||
<DataSourceDropdown
|
||||
onChange={handleChangeDataSource}
|
||||
value={query.dataSource}
|
||||
style={{ marginRight: '0.5rem', minWidth: '5.625rem' }}
|
||||
/>
|
||||
) : (
|
||||
<FilterLabel label={transformToUpperCase(query.dataSource)} />
|
||||
)}
|
||||
</Col>
|
||||
<Col flex="1">
|
||||
<Row gutter={[11, 5]}>
|
||||
{isMetricsDataSource && (
|
||||
<Col>
|
||||
<FilterLabel label="WHERE" />
|
||||
</Col>
|
||||
)}
|
||||
<Col flex="1">
|
||||
<QueryBuilderSearch query={query} onChange={handleChangeTagFilters} />
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
<Col span={11}>
|
||||
<Row gutter={[11, 5]}>
|
||||
<Col flex="5.93rem">
|
||||
<OperatorsSelect
|
||||
value={query.aggregateOperator}
|
||||
onChange={handleChangeOperator}
|
||||
operators={operators}
|
||||
/>
|
||||
</Col>
|
||||
<Col flex="1 1 12.5rem">
|
||||
<AggregatorFilter
|
||||
onChange={handleChangeAggregatorAttribute}
|
||||
query={query}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
|
||||
<Col span={11} offset={2}>
|
||||
<Row gutter={[11, 5]}>
|
||||
<Col flex="5.93rem">
|
||||
<FilterLabel
|
||||
label={panelType === PANEL_TYPES.VALUE ? 'Reduce to' : 'Group by'}
|
||||
/>
|
||||
</Col>
|
||||
<Col flex="1 1 12.5rem">
|
||||
{panelType === PANEL_TYPES.VALUE ? (
|
||||
<ReduceToFilter query={query} onChange={handleChangeReduceTo} />
|
||||
) : (
|
||||
<GroupByFilter
|
||||
disabled={isMetricsDataSource && !query.aggregateAttribute.key}
|
||||
query={query}
|
||||
onChange={handleChangeGroupByKeys}
|
||||
/>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<AdditionalFiltersToggler listOfAdditionalFilter={listOfAdditionalFilters}>
|
||||
<Row gutter={[0, 11]} justify="space-between">
|
||||
{renderAdditionalFilters()}
|
||||
</Row>
|
||||
</AdditionalFiltersToggler>
|
||||
</Col>
|
||||
|
@ -2,6 +2,7 @@ export { AdditionalFiltersToggler } from './AdditionalFiltersToggler';
|
||||
export { DataSourceDropdown } from './DataSourceDropdown';
|
||||
export { FilterLabel } from './FilterLabel';
|
||||
export { Formula } from './Formula';
|
||||
export { HavingFilterTag } from './HavingFilterTag';
|
||||
export { ListItemWrapper } from './ListItemWrapper';
|
||||
export { ListMarker } from './ListMarker';
|
||||
export { Query } from './Query';
|
||||
|
@ -3,7 +3,7 @@ import getStep from 'lib/getStep';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { IBuilderQueryForm } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
|
||||
import { selectStyle } from '../QueryBuilderSearch/config';
|
||||
@ -47,7 +47,7 @@ function AggregateEveryFilter({
|
||||
placeholder="Enter in seconds"
|
||||
disabled={!query.aggregateAttribute.key}
|
||||
style={selectStyle}
|
||||
defaultValue={stepInterval ?? query.stepInterval}
|
||||
defaultValue={query.stepInterval ?? stepInterval}
|
||||
onChange={(event): void => onChange(Number(event.target.value))}
|
||||
onKeyDown={handleKeyDown}
|
||||
/>
|
||||
@ -56,7 +56,7 @@ function AggregateEveryFilter({
|
||||
|
||||
interface AggregateEveryFilterProps {
|
||||
onChange: (values: number) => void;
|
||||
query: IBuilderQueryForm;
|
||||
query: IBuilderQuery;
|
||||
}
|
||||
|
||||
export default AggregateEveryFilter;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { IBuilderQueryForm } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
export type AgregatorFilterProps = {
|
||||
onChange: (value: BaseAutocompleteData) => void;
|
||||
query: IBuilderQueryForm;
|
||||
query: IBuilderQuery;
|
||||
};
|
||||
|
@ -6,6 +6,7 @@ import { initialAggregateAttribute } from 'constants/queryBuilder';
|
||||
import { transformStringWithPrefix } from 'lib/query/transformStringWithPrefix';
|
||||
import React, { memo, useMemo, useState } from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
import { SelectOption } from 'types/common/select';
|
||||
import { transformToUpperCase } from 'utils/transformToUpperCase';
|
||||
|
||||
@ -53,6 +54,7 @@ export const AggregatorFilter = memo(function AggregatorFilter({
|
||||
(item) => item.key === value,
|
||||
) || { ...initialAggregateAttribute, key: value };
|
||||
|
||||
setSearchText('');
|
||||
onChange(currentAttributeObj);
|
||||
};
|
||||
|
||||
@ -66,10 +68,15 @@ export const AggregatorFilter = memo(function AggregatorFilter({
|
||||
[query],
|
||||
);
|
||||
|
||||
const placeholder: string =
|
||||
query.dataSource === DataSource.METRICS
|
||||
? `${transformToUpperCase(query.dataSource)} name`
|
||||
: 'Aggregate attribute';
|
||||
|
||||
return (
|
||||
<AutoComplete
|
||||
showSearch
|
||||
placeholder={`${transformToUpperCase(query.dataSource)} name`}
|
||||
placeholder={placeholder}
|
||||
style={selectStyle}
|
||||
showArrow={false}
|
||||
filterOption={false}
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { IBuilderQueryForm } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
export type GroupByFilterProps = {
|
||||
query: IBuilderQueryForm;
|
||||
query: IBuilderQuery;
|
||||
onChange: (values: BaseAutocompleteData[]) => void;
|
||||
disabled: boolean;
|
||||
};
|
||||
|
||||
export type GroupByFilterValue = {
|
||||
|
@ -5,10 +5,9 @@ import { QueryBuilderKeys } from 'constants/queryBuilder';
|
||||
// ** Components
|
||||
// ** Helpers
|
||||
import { transformStringWithPrefix } from 'lib/query/transformStringWithPrefix';
|
||||
import React, { memo, useMemo, useState } from 'react';
|
||||
import React, { memo, useState } from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { MetricAggregateOperator } from 'types/common/queryBuilder';
|
||||
import { SelectOption } from 'types/common/select';
|
||||
|
||||
import { selectStyle } from '../QueryBuilderSearch/config';
|
||||
@ -20,26 +19,35 @@ import {
|
||||
export const GroupByFilter = memo(function GroupByFilter({
|
||||
query,
|
||||
onChange,
|
||||
disabled,
|
||||
}: GroupByFilterProps): JSX.Element {
|
||||
const [searchText, setSearchText] = useState<string>('');
|
||||
const [isFocused, setIsFocused] = useState<boolean>(false);
|
||||
|
||||
const { data, isFetching } = useQuery(
|
||||
[QueryBuilderKeys.GET_AGGREGATE_KEYS, searchText],
|
||||
[QueryBuilderKeys.GET_AGGREGATE_KEYS, searchText, isFocused],
|
||||
async () =>
|
||||
getAggregateKeys({
|
||||
aggregateAttribute: query.aggregateAttribute.key,
|
||||
tagType: query.aggregateAttribute.type,
|
||||
dataSource: query.dataSource,
|
||||
aggregateOperator: query.aggregateOperator,
|
||||
searchText,
|
||||
}),
|
||||
{ enabled: !!query.aggregateAttribute.key, keepPreviousData: true },
|
||||
{ enabled: !disabled && isFocused, keepPreviousData: true },
|
||||
);
|
||||
|
||||
const handleSearchKeys = (searchText: string): void => {
|
||||
setSearchText(searchText);
|
||||
};
|
||||
|
||||
const onBlur = (): void => {
|
||||
setIsFocused(false);
|
||||
};
|
||||
|
||||
const onFocus = (): void => {
|
||||
setIsFocused(true);
|
||||
};
|
||||
|
||||
const optionsData: SelectOption<string, string>[] =
|
||||
data?.payload?.attributeKeys?.map((item) => ({
|
||||
label: transformStringWithPrefix({
|
||||
@ -52,10 +60,20 @@ export const GroupByFilter = memo(function GroupByFilter({
|
||||
|
||||
const handleChange = (values: GroupByFilterValue[]): void => {
|
||||
const groupByValues: BaseAutocompleteData[] = values.map((item) => {
|
||||
const iterationArray = data?.payload?.attributeKeys || query.groupBy;
|
||||
const existGroup = iterationArray.find((group) => group.key === item.value);
|
||||
if (existGroup) {
|
||||
return existGroup;
|
||||
const responseKeys = data?.payload?.attributeKeys || [];
|
||||
const existGroupResponse = responseKeys.find(
|
||||
(group) => group.key === item.value,
|
||||
);
|
||||
if (existGroupResponse) {
|
||||
return existGroupResponse;
|
||||
}
|
||||
|
||||
const existGroupQuery = query.groupBy.find(
|
||||
(group) => group.key === item.value,
|
||||
);
|
||||
|
||||
if (existGroupQuery) {
|
||||
return existGroupQuery;
|
||||
}
|
||||
|
||||
return {
|
||||
@ -66,6 +84,7 @@ export const GroupByFilter = memo(function GroupByFilter({
|
||||
};
|
||||
});
|
||||
|
||||
setSearchText('');
|
||||
onChange(groupByValues);
|
||||
};
|
||||
|
||||
@ -81,21 +100,16 @@ export const GroupByFilter = memo(function GroupByFilter({
|
||||
title: undefined,
|
||||
}));
|
||||
|
||||
const isDisabledSelect = useMemo(
|
||||
() =>
|
||||
!query.aggregateAttribute.key ||
|
||||
query.aggregateOperator === MetricAggregateOperator.NOOP,
|
||||
[query.aggregateAttribute.key, query.aggregateOperator],
|
||||
);
|
||||
|
||||
return (
|
||||
<Select
|
||||
mode="tags"
|
||||
style={selectStyle}
|
||||
onSearch={handleSearchKeys}
|
||||
showSearch
|
||||
disabled={isDisabledSelect}
|
||||
disabled={disabled}
|
||||
showArrow={false}
|
||||
onBlur={onBlur}
|
||||
onFocus={onFocus}
|
||||
filterOption={false}
|
||||
options={optionsData}
|
||||
labelInValue
|
||||
|
@ -1,9 +1,6 @@
|
||||
import {
|
||||
Having,
|
||||
IBuilderQueryForm,
|
||||
} from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { Having, IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
export type HavingFilterProps = {
|
||||
query: IBuilderQueryForm;
|
||||
query: IBuilderQuery;
|
||||
onChange: (having: Having[]) => void;
|
||||
};
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { Select } from 'antd';
|
||||
// ** Constants
|
||||
import { HAVING_OPERATORS, initialHavingValues } from 'constants/queryBuilder';
|
||||
import { HavingFilterTag } from 'container/QueryBuilder/components';
|
||||
import { HavingTagRenderProps } from 'container/QueryBuilder/components/HavingFilterTag/HavingFilterTag.interfaces';
|
||||
// ** Hooks
|
||||
import { useTagValidation } from 'hooks/queryBuilder/useTagValidation';
|
||||
import {
|
||||
@ -10,7 +12,7 @@ import {
|
||||
// ** Helpers
|
||||
import { transformStringWithPrefix } from 'lib/query/transformStringWithPrefix';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { Having } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { Having, HavingForm } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { SelectOption } from 'types/common/select';
|
||||
|
||||
// ** Types
|
||||
@ -26,7 +28,7 @@ export function HavingFilter({
|
||||
const [searchText, setSearchText] = useState<string>('');
|
||||
const [options, setOptions] = useState<SelectOption<string, string>[]>([]);
|
||||
const [localValues, setLocalValues] = useState<string[]>([]);
|
||||
const [currentFormValue, setCurrentFormValue] = useState<Having>(
|
||||
const [currentFormValue, setCurrentFormValue] = useState<HavingForm>(
|
||||
initialHavingValues,
|
||||
);
|
||||
|
||||
@ -56,7 +58,7 @@ export function HavingFilter({
|
||||
[columnName],
|
||||
);
|
||||
|
||||
const getHavingObject = useCallback((currentSearch: string): Having => {
|
||||
const getHavingObject = useCallback((currentSearch: string): HavingForm => {
|
||||
const textArr = currentSearch.split(' ');
|
||||
const [columnName = '', op = '', ...value] = textArr;
|
||||
|
||||
@ -94,48 +96,92 @@ export function HavingFilter({
|
||||
[columnName, aggregatorOptions],
|
||||
);
|
||||
|
||||
const isValidHavingValue = (search: string): boolean => {
|
||||
const values = getHavingObject(search).value.join(' ');
|
||||
if (values) {
|
||||
const numRegexp = /^[^a-zA-Z]*$/;
|
||||
const isValidHavingValue = useCallback(
|
||||
(search: string): boolean => {
|
||||
const values = getHavingObject(search).value.join(' ');
|
||||
|
||||
return numRegexp.test(values);
|
||||
}
|
||||
if (values) {
|
||||
const numRegexp = /^[-\d.,\s]+$/;
|
||||
|
||||
return true;
|
||||
};
|
||||
return numRegexp.test(values);
|
||||
}
|
||||
|
||||
const handleSearch = (search: string): void => {
|
||||
const trimmedSearch = search.replace(/\s\s+/g, ' ').trimStart();
|
||||
return true;
|
||||
},
|
||||
[getHavingObject],
|
||||
);
|
||||
|
||||
const currentSearch = isMulti
|
||||
? trimmedSearch
|
||||
: trimmedSearch.split(' ').slice(0, 3).join(' ');
|
||||
const isValidSearch = isValidHavingValue(currentSearch);
|
||||
const handleSearch = useCallback(
|
||||
(search: string): void => {
|
||||
const trimmedSearch = search.replace(/\s\s+/g, ' ').trimStart();
|
||||
|
||||
if (isValidSearch) {
|
||||
setSearchText(currentSearch);
|
||||
}
|
||||
};
|
||||
const currentSearch = isMulti
|
||||
? trimmedSearch
|
||||
: trimmedSearch.split(' ').slice(0, 3).join(' ');
|
||||
|
||||
const resetChanges = (): void => {
|
||||
handleSearch('');
|
||||
const isValidSearch = isValidHavingValue(currentSearch);
|
||||
|
||||
if (isValidSearch) {
|
||||
setSearchText(currentSearch);
|
||||
}
|
||||
},
|
||||
[isMulti, isValidHavingValue],
|
||||
);
|
||||
|
||||
const resetChanges = useCallback((): void => {
|
||||
setSearchText('');
|
||||
setCurrentFormValue(initialHavingValues);
|
||||
setOptions(aggregatorOptions);
|
||||
};
|
||||
}, [aggregatorOptions]);
|
||||
|
||||
const handleChange = (values: string[]): void => {
|
||||
const having: Having[] = values.map(transformFromStringToHaving);
|
||||
const handleChange = useCallback(
|
||||
(values: string[]): void => {
|
||||
const having: Having[] = values.map(transformFromStringToHaving);
|
||||
|
||||
const isSelectable: boolean =
|
||||
currentFormValue.value.length > 0 &&
|
||||
currentFormValue.value.every((value) => !!value);
|
||||
const isSelectable =
|
||||
currentFormValue.value.length > 0 &&
|
||||
currentFormValue.value.every((value) => !!value);
|
||||
|
||||
if (isSelectable) {
|
||||
onChange(having);
|
||||
resetChanges();
|
||||
}
|
||||
},
|
||||
[currentFormValue, resetChanges, onChange],
|
||||
);
|
||||
|
||||
const handleUpdateTag = useCallback(
|
||||
(value: string) => {
|
||||
const filteredValues = localValues.filter(
|
||||
(currentValue) => currentValue !== value,
|
||||
);
|
||||
const having: Having[] = filteredValues.map(transformFromStringToHaving);
|
||||
|
||||
if (isSelectable) {
|
||||
onChange(having);
|
||||
resetChanges();
|
||||
}
|
||||
};
|
||||
setSearchText(value);
|
||||
},
|
||||
[localValues, onChange],
|
||||
);
|
||||
|
||||
const tagRender = useCallback(
|
||||
({ label, value, closable, disabled, onClose }: HavingTagRenderProps) => {
|
||||
const handleClose = (): void => {
|
||||
onClose();
|
||||
setSearchText('');
|
||||
};
|
||||
return (
|
||||
<HavingFilterTag
|
||||
label={label}
|
||||
value={value}
|
||||
closable={closable}
|
||||
disabled={disabled}
|
||||
onClose={handleClose}
|
||||
onUpdate={handleUpdateTag}
|
||||
/>
|
||||
);
|
||||
},
|
||||
[handleUpdateTag],
|
||||
);
|
||||
|
||||
const handleSelect = (currentValue: string): void => {
|
||||
const { columnName, op, value } = getHavingObject(currentValue);
|
||||
@ -144,7 +190,7 @@ export function HavingFilter({
|
||||
|
||||
const isClearSearch = isCompletedValue && columnName && op;
|
||||
|
||||
handleSearch(isClearSearch ? '' : currentValue);
|
||||
setSearchText(isClearSearch ? '' : currentValue);
|
||||
};
|
||||
|
||||
const parseSearchText = useCallback(
|
||||
@ -172,9 +218,11 @@ export function HavingFilter({
|
||||
|
||||
return (
|
||||
<Select
|
||||
autoClearSearchValue={false}
|
||||
mode="multiple"
|
||||
onSearch={handleSearch}
|
||||
searchValue={searchText}
|
||||
tagRender={tagRender}
|
||||
value={localValues}
|
||||
data-testid="havingSelect"
|
||||
disabled={!query.aggregateAttribute.key}
|
||||
@ -182,7 +230,6 @@ export function HavingFilter({
|
||||
notFoundContent={currentFormValue.value.length === 0 ? undefined : null}
|
||||
placeholder="Count(operation) > 5"
|
||||
onDeselect={handleDeselect}
|
||||
onBlur={resetChanges}
|
||||
onChange={handleChange}
|
||||
onSelect={handleSelect}
|
||||
>
|
||||
|
@ -8,13 +8,13 @@ import {
|
||||
import { transformFromStringToHaving } from 'lib/query/transformQueryBuilderData';
|
||||
import React from 'react';
|
||||
// ** Types
|
||||
import { IBuilderQueryForm } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
// ** Components
|
||||
import { HavingFilter } from '../HavingFilter';
|
||||
|
||||
const valueWithAttributeAndOperator: IBuilderQueryForm = {
|
||||
const valueWithAttributeAndOperator: IBuilderQuery = {
|
||||
...initialQueryBuilderFormValues,
|
||||
dataSource: DataSource.LOGS,
|
||||
aggregateOperator: 'SUM',
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { InputNumber } from 'antd';
|
||||
import React from 'react';
|
||||
import { IBuilderQueryForm } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
import { selectStyle } from '../QueryBuilderSearch/config';
|
||||
|
||||
@ -24,6 +24,7 @@ function LimitFilter({ onChange, query }: LimitFilterProps): JSX.Element {
|
||||
<InputNumber
|
||||
min={1}
|
||||
type="number"
|
||||
defaultValue={query.limit ?? 1}
|
||||
disabled={!query.aggregateAttribute.key}
|
||||
style={selectStyle}
|
||||
onChange={onChange}
|
||||
@ -34,7 +35,7 @@ function LimitFilter({ onChange, query }: LimitFilterProps): JSX.Element {
|
||||
|
||||
interface LimitFilterProps {
|
||||
onChange: (values: number | null) => void;
|
||||
query: IBuilderQueryForm;
|
||||
query: IBuilderQuery;
|
||||
}
|
||||
|
||||
export default LimitFilter;
|
||||
|
@ -27,6 +27,7 @@ export const OperatorsSelect = memo(function OperatorsSelect({
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
style={selectStyle}
|
||||
showSearch
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...props}
|
||||
/>
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { IBuilderQueryForm } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
export type OrderByFilterProps = {
|
||||
query: IBuilderQueryForm;
|
||||
query: IBuilderQuery;
|
||||
onChange: (values: BaseAutocompleteData[]) => void;
|
||||
};
|
||||
|
||||
|
@ -26,7 +26,6 @@ export function OrderByFilter({
|
||||
async () =>
|
||||
getAggregateKeys({
|
||||
aggregateAttribute: query.aggregateAttribute.key,
|
||||
tagType: query.aggregateAttribute.type,
|
||||
dataSource: query.dataSource,
|
||||
aggregateOperator: query.aggregateOperator,
|
||||
searchText,
|
||||
|
@ -1 +1 @@
|
||||
export const selectStyle = { width: '100%' };
|
||||
export const selectStyle = { width: '100%', minWidth: '10rem' };
|
||||
|
@ -2,7 +2,7 @@ import { Select, Spin, Tag, Tooltip } from 'antd';
|
||||
import { useAutoComplete } from 'hooks/queryBuilder/useAutoComplete';
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import {
|
||||
IBuilderQueryForm,
|
||||
IBuilderQuery,
|
||||
TagFilter,
|
||||
} from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
@ -119,7 +119,7 @@ function QueryBuilderSearch({
|
||||
}
|
||||
|
||||
interface QueryBuilderSearchProps {
|
||||
query: IBuilderQueryForm;
|
||||
query: IBuilderQuery;
|
||||
onChange: (value: TagFilter) => void;
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { SelectProps } from 'antd';
|
||||
import { IBuilderQueryForm } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { ReduceOperators } from 'types/common/queryBuilder';
|
||||
|
||||
export type ReduceToFilterProps = Omit<SelectProps, 'onChange' | 'value'> & {
|
||||
query: IBuilderQueryForm;
|
||||
onChange: (value: string) => void;
|
||||
query: IBuilderQuery;
|
||||
onChange: (value: ReduceOperators) => void;
|
||||
};
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { Select } from 'antd';
|
||||
import { REDUCE_TO_VALUES } from 'constants/queryBuilder';
|
||||
import React, { memo } from 'react';
|
||||
// ** Types
|
||||
import { EReduceOperator } from 'types/common/queryBuilder';
|
||||
import { ReduceOperators } from 'types/common/queryBuilder';
|
||||
import { SelectOption } from 'types/common/select';
|
||||
|
||||
import { ReduceToFilterProps } from './ReduceToFilter.interfaces';
|
||||
@ -10,17 +11,24 @@ export const ReduceToFilter = memo(function ReduceToFilter({
|
||||
query,
|
||||
onChange,
|
||||
}: ReduceToFilterProps): JSX.Element {
|
||||
const options: SelectOption<string, string>[] = Object.values(
|
||||
EReduceOperator,
|
||||
).map((str) => ({ label: str, value: str }));
|
||||
const currentValue =
|
||||
REDUCE_TO_VALUES.find((option) => option.value === query.reduceTo) ||
|
||||
REDUCE_TO_VALUES[0];
|
||||
|
||||
const handleChange = (
|
||||
newValue: SelectOption<ReduceOperators, string>,
|
||||
): void => {
|
||||
onChange(newValue.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<Select
|
||||
placeholder="Reduce to"
|
||||
style={{ width: '100%' }}
|
||||
options={options}
|
||||
value={query.reduceTo}
|
||||
onChange={onChange}
|
||||
options={REDUCE_TO_VALUES}
|
||||
value={currentValue}
|
||||
labelInValue
|
||||
onChange={handleChange}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { isExistsNotExistsOperator } from 'container/QueryBuilder/filters/QueryBuilderSearch/utils';
|
||||
import { Option } from 'container/QueryBuilder/type';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { IBuilderQueryForm } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { checkStringEndsWithSpace } from 'utils/checkStringEndsWithSpace';
|
||||
|
||||
import { useFetchKeysAndValues } from './useFetchKeysAndValues';
|
||||
@ -23,7 +23,7 @@ interface IAutoComplete {
|
||||
isFetching: boolean;
|
||||
}
|
||||
|
||||
export const useAutoComplete = (query: IBuilderQueryForm): IAutoComplete => {
|
||||
export const useAutoComplete = (query: IBuilderQuery): IAutoComplete => {
|
||||
const [searchValue, setSearchValue] = useState<string>('');
|
||||
|
||||
const handleSearch = (value: string): void => setSearchValue(value);
|
||||
@ -48,6 +48,7 @@ export const useAutoComplete = (query: IBuilderQueryForm): IAutoComplete => {
|
||||
isValidTag,
|
||||
isFreeText,
|
||||
handleSearch,
|
||||
query,
|
||||
);
|
||||
|
||||
const handleSelect = useCallback(
|
||||
|
@ -6,7 +6,7 @@ import {
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { useDebounce } from 'react-use';
|
||||
import { IBuilderQueryForm } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { separateSearchValue } from 'utils/separateSearchValue';
|
||||
|
||||
type UseFetchKeysAndValuesReturnValues = {
|
||||
@ -24,7 +24,7 @@ type UseFetchKeysAndValuesReturnValues = {
|
||||
|
||||
export const useFetchKeysAndValues = (
|
||||
searchValue: string,
|
||||
query: IBuilderQueryForm,
|
||||
query: IBuilderQuery,
|
||||
): UseFetchKeysAndValuesReturnValues => {
|
||||
const [keys, setKeys] = useState<AttributeKeyOptions[]>([]);
|
||||
const [results, setResults] = useState<string[]>([]);
|
||||
@ -53,7 +53,7 @@ export const useFetchKeysAndValues = (
|
||||
*/
|
||||
const handleFetchOption = async (
|
||||
value: string,
|
||||
query: IBuilderQueryForm,
|
||||
query: IBuilderQuery,
|
||||
): Promise<void> => {
|
||||
if (value) {
|
||||
// separate the search value into the attribute key and the operator
|
||||
|
@ -11,16 +11,16 @@ const operatorTypeMapper: Record<string, OperatorType> = {
|
||||
[OPERATORS.NIN]: 'MULTIPLY_VALUE',
|
||||
[OPERATORS.EXISTS]: 'NON_VALUE',
|
||||
[OPERATORS.NOT_EXISTS]: 'NON_VALUE',
|
||||
[OPERATORS.LTE]: 'SINGLE_VALUE',
|
||||
[OPERATORS.LT]: 'SINGLE_VALUE',
|
||||
[OPERATORS.GTE]: 'SINGLE_VALUE',
|
||||
[OPERATORS.GT]: 'SINGLE_VALUE',
|
||||
[OPERATORS['<=']]: 'SINGLE_VALUE',
|
||||
[OPERATORS['<']]: 'SINGLE_VALUE',
|
||||
[OPERATORS['>=']]: 'SINGLE_VALUE',
|
||||
[OPERATORS['>']]: 'SINGLE_VALUE',
|
||||
[OPERATORS.LIKE]: 'SINGLE_VALUE',
|
||||
[OPERATORS.NLIKE]: 'SINGLE_VALUE',
|
||||
[OPERATORS.CONTAINS]: 'SINGLE_VALUE',
|
||||
[OPERATORS.NOT_CONTAINS]: 'SINGLE_VALUE',
|
||||
[OPERATORS.EQUALS]: 'SINGLE_VALUE',
|
||||
[OPERATORS.NOT_EQUALS]: 'SINGLE_VALUE',
|
||||
[OPERATORS['=']]: 'SINGLE_VALUE',
|
||||
[OPERATORS['!=']]: 'SINGLE_VALUE',
|
||||
};
|
||||
|
||||
export const useOperatorType = (operator: string): OperatorType =>
|
||||
|
164
frontend/src/hooks/queryBuilder/useQueryOperations.ts
Normal file
164
frontend/src/hooks/queryBuilder/useQueryOperations.ts
Normal file
@ -0,0 +1,164 @@
|
||||
import {
|
||||
initialAggregateAttribute,
|
||||
initialQueryBuilderFormValues,
|
||||
mapOfFilters,
|
||||
mapOfOperators,
|
||||
PANEL_TYPES,
|
||||
} from 'constants/queryBuilder';
|
||||
import { ITEMS } from 'container/NewDashboard/ComponentsSlider/menuItems';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { findDataTypeOfOperator } from 'lib/query/findDataTypeOfOperator';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import {
|
||||
HandleChangeQueryData,
|
||||
UseQueryOperations,
|
||||
} from 'types/common/operations.types';
|
||||
import { DataSource, StringOperators } from 'types/common/queryBuilder';
|
||||
|
||||
export const useQueryOperations: UseQueryOperations = ({
|
||||
query,
|
||||
index,
|
||||
panelType,
|
||||
}) => {
|
||||
const { handleSetQueryData, removeEntityByIndex } = useQueryBuilder();
|
||||
const [operators, setOperators] = useState<string[]>([]);
|
||||
const [listOfAdditionalFilters, setListOfAdditionalFilters] = useState<
|
||||
string[]
|
||||
>([]);
|
||||
|
||||
const { dataSource, aggregateOperator } = query;
|
||||
|
||||
const handleChangeOperator = useCallback(
|
||||
(value: string): void => {
|
||||
const aggregateDataType: BaseAutocompleteData['dataType'] =
|
||||
query.aggregateAttribute.dataType;
|
||||
|
||||
const typeOfValue = findDataTypeOfOperator(value);
|
||||
const shouldResetAggregateAttribute =
|
||||
(aggregateDataType === 'string' || aggregateDataType === 'bool') &&
|
||||
typeOfValue === 'number';
|
||||
|
||||
const newQuery: IBuilderQuery = {
|
||||
...query,
|
||||
aggregateOperator: value,
|
||||
having: [],
|
||||
orderBy: [],
|
||||
limit: null,
|
||||
tagFilters: { items: [], op: 'AND' },
|
||||
...(shouldResetAggregateAttribute
|
||||
? { aggregateAttribute: initialAggregateAttribute }
|
||||
: {}),
|
||||
};
|
||||
|
||||
handleSetQueryData(index, newQuery);
|
||||
},
|
||||
[index, query, handleSetQueryData],
|
||||
);
|
||||
|
||||
const getNewOperators = useCallback(
|
||||
(dataSource: DataSource, currentPanelType: ITEMS): string[] => {
|
||||
let operatorsByDataSource = mapOfOperators[dataSource];
|
||||
|
||||
if (
|
||||
dataSource !== DataSource.METRICS &&
|
||||
currentPanelType !== PANEL_TYPES.LIST
|
||||
) {
|
||||
operatorsByDataSource = operatorsByDataSource.filter(
|
||||
(operator) => operator !== StringOperators.NOOP,
|
||||
);
|
||||
}
|
||||
|
||||
return operatorsByDataSource;
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const getNewListOfAdditionalFilters = useCallback(
|
||||
(dataSource: DataSource): string[] =>
|
||||
mapOfFilters[dataSource].map((item) => item.text),
|
||||
[],
|
||||
);
|
||||
|
||||
const handleChangeAggregatorAttribute = useCallback(
|
||||
(value: BaseAutocompleteData): void => {
|
||||
const newQuery: IBuilderQuery = {
|
||||
...query,
|
||||
aggregateAttribute: value,
|
||||
having: [],
|
||||
};
|
||||
|
||||
handleSetQueryData(index, newQuery);
|
||||
},
|
||||
[index, query, handleSetQueryData],
|
||||
);
|
||||
|
||||
const handleChangeDataSource = useCallback(
|
||||
(nextSource: DataSource): void => {
|
||||
const newOperators = getNewOperators(nextSource, panelType);
|
||||
|
||||
const entries = Object.entries(initialQueryBuilderFormValues).filter(
|
||||
([key]) => key !== 'queryName' && key !== 'expression',
|
||||
);
|
||||
|
||||
const initCopyResult = Object.fromEntries(entries);
|
||||
|
||||
const newQuery: IBuilderQuery = {
|
||||
...query,
|
||||
...initCopyResult,
|
||||
dataSource: nextSource,
|
||||
aggregateOperator: newOperators[0],
|
||||
};
|
||||
|
||||
setOperators(newOperators);
|
||||
handleSetQueryData(index, newQuery);
|
||||
},
|
||||
[index, query, panelType, handleSetQueryData, getNewOperators],
|
||||
);
|
||||
|
||||
const handleDeleteQuery = useCallback(() => {
|
||||
removeEntityByIndex('queryData', index);
|
||||
}, [removeEntityByIndex, index]);
|
||||
|
||||
const handleChangeQueryData: HandleChangeQueryData = useCallback(
|
||||
(key, value) => {
|
||||
const newQuery: IBuilderQuery = {
|
||||
...query,
|
||||
[key]: value,
|
||||
};
|
||||
|
||||
handleSetQueryData(index, newQuery);
|
||||
},
|
||||
[query, index, handleSetQueryData],
|
||||
);
|
||||
|
||||
const isMetricsDataSource = useMemo(
|
||||
() => query.dataSource === DataSource.METRICS,
|
||||
[query.dataSource],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (operators.length === 0) {
|
||||
const initialOperators = getNewOperators(dataSource, panelType);
|
||||
setOperators(initialOperators);
|
||||
}
|
||||
}, [operators, dataSource, panelType, getNewOperators]);
|
||||
|
||||
useEffect(() => {
|
||||
const additionalFilters = getNewListOfAdditionalFilters(dataSource);
|
||||
|
||||
setListOfAdditionalFilters(additionalFilters);
|
||||
}, [dataSource, aggregateOperator, getNewListOfAdditionalFilters]);
|
||||
|
||||
return {
|
||||
isMetricsDataSource,
|
||||
operators,
|
||||
listOfAdditionalFilters,
|
||||
handleChangeOperator,
|
||||
handleChangeAggregatorAttribute,
|
||||
handleChangeDataSource,
|
||||
handleDeleteQuery,
|
||||
handleChangeQueryData,
|
||||
};
|
||||
};
|
@ -1,5 +1,9 @@
|
||||
import { isExistsNotExistsOperator } from 'container/QueryBuilder/filters/QueryBuilderSearch/utils';
|
||||
import { useCallback, useState } from 'react';
|
||||
import {
|
||||
isExistsNotExistsOperator,
|
||||
isInNotInOperator,
|
||||
} from 'container/QueryBuilder/filters/QueryBuilderSearch/utils';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
type IUseTag = {
|
||||
handleAddTag: (value: string) => void;
|
||||
@ -21,6 +25,7 @@ export const useTag = (
|
||||
isValidTag: boolean,
|
||||
isFreeText: boolean,
|
||||
handleSearch: (value: string) => void,
|
||||
query: IBuilderQuery,
|
||||
): IUseTag => {
|
||||
const [tags, setTags] = useState<string[]>([]);
|
||||
|
||||
@ -55,5 +60,16 @@ export const useTag = (
|
||||
setTags((prevTags) => prevTags.filter((v) => v !== value));
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setTags(
|
||||
(query?.tagFilters?.items || []).map((obj) =>
|
||||
isInNotInOperator(obj.op)
|
||||
? `${obj.key} ${obj.op} ${obj.value.join(',')}`
|
||||
: `${obj.key} ${obj.op} ${obj.value.join(' ')}`,
|
||||
),
|
||||
);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
return { handleAddTag, handleClearTag, tags, updateTag };
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { QueryData } from 'types/api/widgets/getQuery';
|
||||
import { SeriesItem } from 'types/api/widgets/getQuery';
|
||||
|
||||
const getLabelName = (
|
||||
metric: QueryData['metric'],
|
||||
metric: SeriesItem['labels'],
|
||||
query: string,
|
||||
legends: string,
|
||||
): string => {
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { GlobalTime } from 'types/actions/globalTime';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
|
||||
@ -6,7 +7,7 @@ const GetMaxMinTime = ({
|
||||
minTime,
|
||||
maxTime,
|
||||
}: GetMaxMinProps): GlobalTime => {
|
||||
if (graphType === 'VALUE') {
|
||||
if (graphType === PANEL_TYPES.VALUE) {
|
||||
return {
|
||||
maxTime,
|
||||
minTime: maxTime,
|
||||
|
39
frontend/src/lib/newQueryBuilder/convertNewDataToOld.ts
Normal file
39
frontend/src/lib/newQueryBuilder/convertNewDataToOld.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import {
|
||||
MetricRangePayloadProps,
|
||||
MetricRangePayloadV3,
|
||||
} from 'types/api/metrics/getQueryRange';
|
||||
import { QueryData } from 'types/api/widgets/getQuery';
|
||||
|
||||
export const convertNewDataToOld = (
|
||||
newData: MetricRangePayloadV3,
|
||||
): MetricRangePayloadProps => {
|
||||
const { result, resultType } = newData.data;
|
||||
const oldResult: MetricRangePayloadProps['data']['result'] = [];
|
||||
|
||||
result.forEach((item) => {
|
||||
if (item.series) {
|
||||
item.series.forEach((serie) => {
|
||||
const values: QueryData['values'] = serie.values.reduce<
|
||||
QueryData['values']
|
||||
>((acc, currentInfo) => {
|
||||
const renderValues: [number, string] = [
|
||||
currentInfo.timestamp,
|
||||
currentInfo.value,
|
||||
];
|
||||
|
||||
return [...acc, renderValues];
|
||||
}, []);
|
||||
const result: QueryData = {
|
||||
metric: serie.labels,
|
||||
values,
|
||||
queryName: item.queryName,
|
||||
};
|
||||
|
||||
oldResult.push(result);
|
||||
});
|
||||
}
|
||||
});
|
||||
const oldResultType = resultType;
|
||||
|
||||
return { data: { result: oldResult, resultType: oldResultType } };
|
||||
};
|
@ -0,0 +1,53 @@
|
||||
import {
|
||||
IBuilderFormula,
|
||||
IBuilderQuery,
|
||||
} from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { QueryBuilderData } from 'types/common/queryBuilder';
|
||||
|
||||
type MapQueryDataToApiResult = {
|
||||
data: Record<string, IBuilderQuery | IBuilderFormula>;
|
||||
newLegendMap: Record<string, string>;
|
||||
};
|
||||
type MapQuery = Record<string, IBuilderQuery>;
|
||||
type MapFormula = Record<string, IBuilderFormula>;
|
||||
|
||||
export const mapQueryDataToApi = (
|
||||
data: QueryBuilderData,
|
||||
): MapQueryDataToApiResult => {
|
||||
const newLegendMap: Record<string, string> = {};
|
||||
|
||||
const preparedQueryData: MapQuery = data.queryData.reduce<MapQuery>(
|
||||
(acc, query) => {
|
||||
const newResult: MapQuery = {
|
||||
...acc,
|
||||
[query.queryName]: {
|
||||
...query,
|
||||
},
|
||||
};
|
||||
|
||||
newLegendMap[query.queryName] = query.legend;
|
||||
|
||||
return newResult;
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
const preparedFormulaData: MapFormula = data.queryFormulas.reduce<MapFormula>(
|
||||
(acc, formula) => {
|
||||
const newResult: MapFormula = {
|
||||
...acc,
|
||||
[formula.queryName]: {
|
||||
...formula,
|
||||
},
|
||||
};
|
||||
|
||||
return newResult;
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
return {
|
||||
data: { ...preparedQueryData, ...preparedFormulaData },
|
||||
newLegendMap,
|
||||
};
|
||||
};
|
@ -4,19 +4,31 @@ import { Having } from 'types/api/queryBuilder/queryBuilderData';
|
||||
export const transformHavingToStringValue = (having: Having[]): string[] => {
|
||||
const result: string[] = having.map((item) => {
|
||||
const operator = Object.entries(OPERATORS).find(([key]) => key === item.op);
|
||||
const value = Array.isArray(item.value) ? item.value.join(', ') : item.value;
|
||||
|
||||
return `${item.columnName} ${operator ? operator[1] : ''} ${item.value.join(
|
||||
' ',
|
||||
)}`;
|
||||
return `${item.columnName} ${operator ? operator[1] : ''} ${value}`;
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const transformFromStringToHaving = (havingStr: string): Having => {
|
||||
const [columnName, op, ...value] = havingStr.split(' ');
|
||||
const [columnName, op, ...values] = havingStr.split(' ');
|
||||
|
||||
const operator = Object.entries(OPERATORS).find(([, value]) => value === op);
|
||||
|
||||
return { columnName, op: operator ? operator[0] : '', value };
|
||||
const currentValue = values.reduce<number[]>((acc, strNum) => {
|
||||
const num = parseFloat(strNum);
|
||||
if (Number.isNaN(num)) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
return [...acc, num];
|
||||
}, []);
|
||||
|
||||
return {
|
||||
columnName,
|
||||
op: operator ? operator[0] : '',
|
||||
value: currentValue.length > 1 ? currentValue : currentValue[0],
|
||||
};
|
||||
};
|
||||
|
@ -19,7 +19,7 @@ import React, {
|
||||
// TODO: Rename Types on the Reusable type for any source
|
||||
import {
|
||||
IBuilderFormula,
|
||||
IBuilderQueryForm,
|
||||
IBuilderQuery,
|
||||
} from 'types/api/queryBuilder/queryBuilderData';
|
||||
import {
|
||||
DataSource,
|
||||
@ -77,8 +77,7 @@ export function QueryBuilderProvider({
|
||||
const removeEntityByIndex = useCallback(
|
||||
(type: keyof QueryBuilderData, index: number) => {
|
||||
setQueryBuilderData((prevState) => {
|
||||
const currentArray: (IBuilderQueryForm | IBuilderFormula)[] =
|
||||
prevState[type];
|
||||
const currentArray: (IBuilderQuery | IBuilderFormula)[] = prevState[type];
|
||||
return {
|
||||
...prevState,
|
||||
[type]: currentArray.filter((item, i) => index !== i),
|
||||
@ -89,20 +88,20 @@ export function QueryBuilderProvider({
|
||||
);
|
||||
|
||||
const createNewQuery = useCallback(
|
||||
(queries: IBuilderQueryForm[]): IBuilderQueryForm => {
|
||||
(queries: IBuilderQuery[]): IBuilderQuery => {
|
||||
const existNames = queries.map((item) => item.queryName);
|
||||
|
||||
const newQuery: IBuilderQueryForm = {
|
||||
const newQuery: IBuilderQuery = {
|
||||
...initialQueryBuilderFormValues,
|
||||
queryName: createNewBuilderItemName({ existNames, sourceNames: alphabet }),
|
||||
expression: createNewBuilderItemName({
|
||||
existNames,
|
||||
sourceNames: alphabet,
|
||||
}),
|
||||
...(initialDataSource
|
||||
? {
|
||||
dataSource: initialDataSource,
|
||||
aggregateOperator: mapOfOperators[initialDataSource][0],
|
||||
expression: createNewBuilderItemName({
|
||||
existNames,
|
||||
sourceNames: alphabet,
|
||||
}),
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
@ -113,11 +112,14 @@ export function QueryBuilderProvider({
|
||||
);
|
||||
|
||||
const createNewFormula = useCallback((formulas: IBuilderFormula[]) => {
|
||||
const existNames = formulas.map((item) => item.label);
|
||||
const existNames = formulas.map((item) => item.queryName);
|
||||
|
||||
const newFormula: IBuilderFormula = {
|
||||
...initialFormulaBuilderFormValues,
|
||||
label: createNewBuilderItemName({ existNames, sourceNames: formulasNames }),
|
||||
queryName: createNewBuilderItemName({
|
||||
existNames,
|
||||
sourceNames: formulasNames,
|
||||
}),
|
||||
};
|
||||
|
||||
return newFormula;
|
||||
@ -153,11 +155,8 @@ export function QueryBuilderProvider({
|
||||
);
|
||||
|
||||
const updateQueryBuilderData = useCallback(
|
||||
(
|
||||
queries: IBuilderQueryForm[],
|
||||
index: number,
|
||||
newQueryData: IBuilderQueryForm,
|
||||
) => queries.map((item, idx) => (index === idx ? newQueryData : item)),
|
||||
(queries: IBuilderQuery[], index: number, newQueryData: IBuilderQuery) =>
|
||||
queries.map((item, idx) => (index === idx ? newQueryData : item)),
|
||||
[],
|
||||
);
|
||||
|
||||
@ -168,7 +167,7 @@ export function QueryBuilderProvider({
|
||||
);
|
||||
|
||||
const handleSetQueryData = useCallback(
|
||||
(index: number, newQueryData: IBuilderQueryForm): void => {
|
||||
(index: number, newQueryData: IBuilderQuery): void => {
|
||||
setQueryBuilderData((prevState) => {
|
||||
const updatedQueryBuilderData = updateQueryBuilderData(
|
||||
prevState.queryData,
|
||||
|
@ -2,8 +2,11 @@ import getDashboard from 'api/dashboard/get';
|
||||
import {
|
||||
ClickHouseQueryTemplate,
|
||||
PromQLQueryTemplate,
|
||||
QueryBuilderQueryTemplate,
|
||||
} from 'constants/dashboard';
|
||||
import {
|
||||
initialQueryBuilderFormValues,
|
||||
PANEL_TYPES,
|
||||
} from 'constants/queryBuilder';
|
||||
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
||||
import GetQueryName from 'lib/query/GetQueryName';
|
||||
import { Dispatch } from 'redux';
|
||||
@ -42,7 +45,7 @@ export const GetDashboard = ({
|
||||
isStacked: false,
|
||||
nullZeroValues: 'zero',
|
||||
opacity: '0',
|
||||
panelTypes: graphType || 'TIME_SERIES',
|
||||
panelTypes: graphType || PANEL_TYPES.TIME_SERIES,
|
||||
timePreferance: 'GLOBAL_TIME',
|
||||
title: '',
|
||||
queryType: 0,
|
||||
@ -69,14 +72,9 @@ export const GetDashboard = ({
|
||||
...ClickHouseQueryTemplate,
|
||||
},
|
||||
],
|
||||
metricsBuilder: {
|
||||
formulas: [],
|
||||
queryBuilder: [
|
||||
{
|
||||
name: GetQueryName([]) as string,
|
||||
...QueryBuilderQueryTemplate,
|
||||
},
|
||||
],
|
||||
builder: {
|
||||
queryFormulas: [],
|
||||
queryData: [initialQueryBuilderFormValues],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -6,7 +6,6 @@ import { getMetricsQueryRange } from 'api/metrics/getQueryRange';
|
||||
import { AxiosError } from 'axios';
|
||||
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
|
||||
import { ITEMS } from 'container/NewDashboard/ComponentsSlider/menuItems';
|
||||
import { WIDGET_QUERY_BUILDER_FORMULA_KEY_NAME } from 'container/NewWidget/LeftContainer/QuerySection/constants';
|
||||
import { EQueryTypeToQueryKeyMapping } from 'container/NewWidget/LeftContainer/QuerySection/types';
|
||||
import { timePreferenceType } from 'container/NewWidget/RightContainer/timeItems';
|
||||
import { Time } from 'container/TopNav/DateTimeSelection/config';
|
||||
@ -14,15 +13,17 @@ import GetMaxMinTime from 'lib/getMaxMinTime';
|
||||
import GetMinMax from 'lib/getMinMax';
|
||||
import GetStartAndEndTime from 'lib/getStartAndEndTime';
|
||||
import getStep from 'lib/getStep';
|
||||
import { mapQueryDataToApi } from 'lib/newQueryBuilder/queryBuilderMappers/mapQueryDataToApi';
|
||||
import { isEmpty } from 'lodash-es';
|
||||
import { Dispatch } from 'redux';
|
||||
import store from 'store';
|
||||
import AppActions from 'types/actions';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { IDashboardVariable, Query } from 'types/api/dashboard/getAll';
|
||||
import { Query } from 'types/api/dashboard/getAll';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import { EDataSource, EPanelType, EQueryType } from 'types/common/dashboard';
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { convertNewDataToOld } from 'lib/newQueryBuilder/convertNewDataToOld';
|
||||
|
||||
export async function GetMetricQueryRange({
|
||||
query,
|
||||
@ -41,51 +42,25 @@ export async function GetMetricQueryRange({
|
||||
const queryKey: Record<EQueryTypeToQueryKeyMapping, string> =
|
||||
EQueryTypeToQueryKeyMapping[EQueryType[query.queryType]];
|
||||
const queryData = query[queryKey];
|
||||
const legendMap: Record<string, string> = {};
|
||||
let legendMap: Record<string, string> = {};
|
||||
|
||||
const QueryPayload = {
|
||||
dataSource: EDataSource.METRICS,
|
||||
compositeMetricQuery: {
|
||||
queryType,
|
||||
panelType: EPanelType[graphType],
|
||||
compositeQuery: {
|
||||
queryType: queryKey,
|
||||
panelType: graphType,
|
||||
},
|
||||
};
|
||||
|
||||
switch (queryType as EQueryType) {
|
||||
case EQueryType.QUERY_BUILDER: {
|
||||
const builderQueries = {};
|
||||
queryData.queryBuilder.map((query) => {
|
||||
const generatedQueryPayload = {
|
||||
queryName: query.name,
|
||||
aggregateOperator: query.aggregateOperator,
|
||||
metricName: query.metricName,
|
||||
tagFilters: query.tagFilters,
|
||||
};
|
||||
|
||||
if (graphType === 'TIME_SERIES') {
|
||||
generatedQueryPayload.groupBy = query.groupBy;
|
||||
}
|
||||
|
||||
// Value
|
||||
else {
|
||||
generatedQueryPayload.reduceTo = query.reduceTo;
|
||||
}
|
||||
|
||||
generatedQueryPayload.expression = query.name;
|
||||
generatedQueryPayload.disabled = query.disabled;
|
||||
builderQueries[query.name] = generatedQueryPayload;
|
||||
legendMap[query.name] = query.legend || '';
|
||||
const { queryData, queryFormulas } = query.builder;
|
||||
const builderQueries = mapQueryDataToApi({
|
||||
queryData,
|
||||
queryFormulas,
|
||||
});
|
||||
legendMap = builderQueries.newLegendMap;
|
||||
|
||||
queryData[WIDGET_QUERY_BUILDER_FORMULA_KEY_NAME].map((formula) => {
|
||||
const generatedFormulaPayload = {};
|
||||
legendMap[formula.name] = formula.legend || formula.name;
|
||||
generatedFormulaPayload.queryName = formula.name;
|
||||
generatedFormulaPayload.expression = formula.expression;
|
||||
generatedFormulaPayload.disabled = formula.disabled;
|
||||
generatedFormulaPayload.legend = formula.legend;
|
||||
builderQueries[formula.name] = generatedFormulaPayload;
|
||||
});
|
||||
QueryPayload.compositeMetricQuery.builderQueries = builderQueries;
|
||||
QueryPayload.compositeQuery.builderQueries = builderQueries.data;
|
||||
break;
|
||||
}
|
||||
case EQueryType.CLICKHOUSE: {
|
||||
@ -98,7 +73,7 @@ export async function GetMetricQueryRange({
|
||||
};
|
||||
legendMap[query.name] = query.legend;
|
||||
});
|
||||
QueryPayload.compositeMetricQuery.chQueries = chQueries;
|
||||
QueryPayload.compositeQuery.chQueries = chQueries;
|
||||
break;
|
||||
}
|
||||
case EQueryType.PROM: {
|
||||
@ -111,7 +86,7 @@ export async function GetMetricQueryRange({
|
||||
};
|
||||
legendMap[query.name] = query.legend;
|
||||
});
|
||||
QueryPayload.compositeMetricQuery.promQueries = promQueries;
|
||||
QueryPayload.compositeQuery.promQueries = promQueries;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@ -148,7 +123,12 @@ export async function GetMetricQueryRange({
|
||||
`API responded with ${response.statusCode} - ${response.error}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (response.payload?.data?.result) {
|
||||
const v2Range = convertNewDataToOld(response.payload);
|
||||
|
||||
response.payload = v2Range;
|
||||
|
||||
response.payload.data.result = response.payload.data.result.map(
|
||||
(queryData) => {
|
||||
const newQueryData = queryData;
|
||||
@ -164,6 +144,7 @@ export async function GetMetricQueryRange({
|
||||
newQueryData.metric[queryData.queryName] = queryData.queryName;
|
||||
}
|
||||
}
|
||||
|
||||
return newQueryData;
|
||||
},
|
||||
);
|
||||
@ -182,6 +163,7 @@ export const GetQueryResults = (
|
||||
errorMessage: '',
|
||||
widgetId: props.widgetId,
|
||||
errorBoolean: false,
|
||||
isLoadingQueryResult: true,
|
||||
},
|
||||
});
|
||||
const response = await GetMetricQueryRange(props);
|
||||
@ -194,6 +176,7 @@ export const GetQueryResults = (
|
||||
payload: {
|
||||
errorMessage: isError || '',
|
||||
widgetId: props.widgetId,
|
||||
isLoadingQueryResult: false,
|
||||
},
|
||||
});
|
||||
return;
|
||||
@ -217,6 +200,7 @@ export const GetQueryResults = (
|
||||
errorMessage: (error as AxiosError).toString(),
|
||||
widgetId: props.widgetId,
|
||||
errorBoolean: true,
|
||||
isLoadingQueryResult: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ const InitialValue: InitialValueTypes = {
|
||||
isEditMode: false,
|
||||
isQueryFired: false,
|
||||
isAddWidget: false,
|
||||
isLoadingQueryResult: false,
|
||||
};
|
||||
|
||||
const dashboard = (
|
||||
@ -170,7 +171,12 @@ const dashboard = (
|
||||
}
|
||||
|
||||
case QUERY_ERROR: {
|
||||
const { widgetId, errorMessage, errorBoolean = true } = action.payload;
|
||||
const {
|
||||
widgetId,
|
||||
errorMessage,
|
||||
errorBoolean = true,
|
||||
isLoadingQueryResult = false,
|
||||
} = action.payload;
|
||||
const [selectedDashboard] = state.dashboards;
|
||||
const { data } = selectedDashboard;
|
||||
|
||||
@ -210,6 +216,7 @@ const dashboard = (
|
||||
},
|
||||
],
|
||||
isQueryFired: true,
|
||||
isLoadingQueryResult,
|
||||
};
|
||||
}
|
||||
|
||||
@ -255,6 +262,7 @@ const dashboard = (
|
||||
},
|
||||
],
|
||||
isQueryFired: true,
|
||||
isLoadingQueryResult: false,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -152,6 +152,7 @@ interface QueryError {
|
||||
errorMessage: string;
|
||||
widgetId: string;
|
||||
errorBoolean?: boolean;
|
||||
isLoadingQueryResult?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ import {
|
||||
EQueryType,
|
||||
EReduceOperator,
|
||||
} from 'types/common/dashboard';
|
||||
import { QueryBuilderData } from 'types/common/queryBuilder';
|
||||
|
||||
import { QueryData } from '../widgets/getQuery';
|
||||
|
||||
@ -92,10 +93,7 @@ export interface PromQLWidgets extends IBaseWidget {
|
||||
export interface Query {
|
||||
queryType: EQueryType;
|
||||
promQL: IPromQLQuery[];
|
||||
metricsBuilder: {
|
||||
formulas: IMetricsBuilderFormula[];
|
||||
queryBuilder: IMetricsBuilderQuery[];
|
||||
};
|
||||
builder: QueryBuilderData;
|
||||
clickHouse: IClickHouseQuery[];
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,16 @@
|
||||
import { QueryData } from '../widgets/getQuery';
|
||||
import { QueryData, QueryDataV3 } from '../widgets/getQuery';
|
||||
|
||||
export type MetricsRangeProps = never;
|
||||
export interface MetricRangePayloadProps {
|
||||
data: {
|
||||
result: QueryData[];
|
||||
resultType: string;
|
||||
variables: Record<string, unknown>;
|
||||
};
|
||||
}
|
||||
|
||||
export interface MetricRangePayloadV3 {
|
||||
data: {
|
||||
result: QueryDataV3[];
|
||||
resultType: string;
|
||||
};
|
||||
}
|
||||
|
@ -1,11 +1,8 @@
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
import { BaseAutocompleteData } from './queryAutocompleteResponse';
|
||||
|
||||
export interface IGetAttributeKeysPayload {
|
||||
aggregateOperator: string;
|
||||
dataSource: DataSource;
|
||||
searchText: string;
|
||||
aggregateAttribute: string;
|
||||
tagType: BaseAutocompleteData['type'];
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
import { DataSource, ReduceOperators } from 'types/common/queryBuilder';
|
||||
|
||||
import { BaseAutocompleteData } from './queryAutocompleteResponse';
|
||||
|
||||
@ -6,7 +6,8 @@ import { BaseAutocompleteData } from './queryAutocompleteResponse';
|
||||
export interface IBuilderFormula {
|
||||
expression: string;
|
||||
disabled: boolean;
|
||||
label: string;
|
||||
queryName: string;
|
||||
dataSource?: DataSource;
|
||||
legend: string;
|
||||
}
|
||||
|
||||
@ -27,6 +28,10 @@ export interface TagFilter {
|
||||
export type Having = {
|
||||
columnName: string;
|
||||
op: string;
|
||||
value: number | number[];
|
||||
};
|
||||
|
||||
export type HavingForm = Omit<Having, 'value'> & {
|
||||
value: string[];
|
||||
};
|
||||
|
||||
@ -35,7 +40,7 @@ export type IBuilderQuery = {
|
||||
queryName: string;
|
||||
dataSource: DataSource;
|
||||
aggregateOperator: string;
|
||||
aggregateAttribute: string;
|
||||
aggregateAttribute: BaseAutocompleteData;
|
||||
tagFilters: TagFilter;
|
||||
groupBy: BaseAutocompleteData[];
|
||||
expression: string;
|
||||
@ -44,10 +49,6 @@ export type IBuilderQuery = {
|
||||
limit: number | null;
|
||||
stepInterval: number;
|
||||
orderBy: BaseAutocompleteData[];
|
||||
reduceTo: string;
|
||||
};
|
||||
|
||||
export type IBuilderQueryForm = Omit<IBuilderQuery, 'aggregateAttribute'> & {
|
||||
aggregateAttribute: BaseAutocompleteData;
|
||||
reduceTo: ReduceOperators;
|
||||
legend: string;
|
||||
};
|
||||
|
@ -4,8 +4,7 @@ export interface PayloadProps {
|
||||
}
|
||||
|
||||
export interface QueryData {
|
||||
metric?: {
|
||||
__name__: string;
|
||||
metric: {
|
||||
[key: string]: string;
|
||||
};
|
||||
queryName: string;
|
||||
@ -13,6 +12,20 @@ export interface QueryData {
|
||||
values: [number, string][];
|
||||
}
|
||||
|
||||
export interface SeriesItem {
|
||||
labels: {
|
||||
[key: string]: string;
|
||||
};
|
||||
values: { timestamp: number; value: string }[];
|
||||
}
|
||||
|
||||
export interface QueryDataV3 {
|
||||
list: null;
|
||||
queryName: string;
|
||||
legend?: string;
|
||||
series: SeriesItem[];
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
query: string;
|
||||
step: string;
|
||||
|
@ -1,9 +1,3 @@
|
||||
export enum EDataSource {
|
||||
METRICS = 1,
|
||||
TRACES,
|
||||
LOGS,
|
||||
}
|
||||
|
||||
export enum EQueryType {
|
||||
QUERY_BUILDER = 1,
|
||||
CLICKHOUSE,
|
||||
@ -42,8 +36,8 @@ export enum EAggregateOperator {
|
||||
}
|
||||
|
||||
export enum EPanelType {
|
||||
TIME_SERIES = 1,
|
||||
VALUE,
|
||||
GRAPH = 'graph',
|
||||
VALUE = 'value',
|
||||
}
|
||||
|
||||
export enum EReduceOperator {
|
||||
|
30
frontend/src/types/common/operations.types.ts
Normal file
30
frontend/src/types/common/operations.types.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { QueryProps } from 'container/QueryBuilder/components/Query/Query.interfaces';
|
||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
type UseQueryOperationsParams = Pick<
|
||||
QueryProps,
|
||||
'index' | 'panelType' | 'query'
|
||||
>;
|
||||
|
||||
export type HandleChangeQueryData = <
|
||||
Key extends keyof IBuilderQuery,
|
||||
Value extends IBuilderQuery[Key]
|
||||
>(
|
||||
key: Key,
|
||||
value: Value,
|
||||
) => void;
|
||||
|
||||
export type UseQueryOperations = (
|
||||
params: UseQueryOperationsParams,
|
||||
) => {
|
||||
isMetricsDataSource: boolean;
|
||||
operators: string[];
|
||||
listOfAdditionalFilters: string[];
|
||||
handleChangeOperator: (value: string) => void;
|
||||
handleChangeAggregatorAttribute: (value: BaseAutocompleteData) => void;
|
||||
handleChangeDataSource: (newSource: DataSource) => void;
|
||||
handleDeleteQuery: () => void;
|
||||
handleChangeQueryData: HandleChangeQueryData;
|
||||
};
|
@ -1,6 +1,6 @@
|
||||
import {
|
||||
IBuilderFormula,
|
||||
IBuilderQueryForm,
|
||||
IBuilderQuery,
|
||||
} from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
export enum DataSource {
|
||||
@ -103,6 +103,10 @@ export enum TracesAggregatorOperator {
|
||||
P95 = 'p95',
|
||||
P99 = 'p99',
|
||||
RATE = 'rate',
|
||||
RATE_SUM = 'rate_sum',
|
||||
RATE_AVG = 'rate_avg',
|
||||
RATE_MIN = 'rate_min',
|
||||
RATE_MAX = 'rate_max',
|
||||
}
|
||||
|
||||
export enum LogsAggregatorOperator {
|
||||
@ -123,18 +127,23 @@ export enum LogsAggregatorOperator {
|
||||
P95 = 'p95',
|
||||
P99 = 'p99',
|
||||
RATE = 'rate',
|
||||
RATE_SUM = 'rate_sum',
|
||||
RATE_AVG = 'rate_avg',
|
||||
RATE_MIN = 'rate_min',
|
||||
RATE_MAX = 'rate_max',
|
||||
}
|
||||
|
||||
export enum EReduceOperator {
|
||||
LATEST_OF_VALUES_IN_TIMEFRAME = 'Latest of values in timeframe',
|
||||
'SUM_OF_VALUES_IN_TIMEFRAME' = 'Sum of values in timeframe',
|
||||
'AVERAGE_OF_VALUES_IN_TIMEFRAME' = 'Average of values in timeframe',
|
||||
'MAX_OF_VALUES_IN_TIMEFRAME' = 'Max of values in timeframe',
|
||||
'MIN_OF_VALUES_IN_TIMEFRAME' = 'Min of values in timeframe',
|
||||
}
|
||||
export type PanelTypeKeys =
|
||||
| 'TIME_SERIES'
|
||||
| 'VALUE'
|
||||
| 'TABLE'
|
||||
| 'LIST'
|
||||
| 'EMPTY_WIDGET';
|
||||
|
||||
export type ReduceOperators = 'last' | 'sum' | 'avg' | 'max' | 'min';
|
||||
|
||||
export type QueryBuilderData = {
|
||||
queryData: IBuilderQueryForm[];
|
||||
queryData: IBuilderQuery[];
|
||||
queryFormulas: IBuilderFormula[];
|
||||
};
|
||||
|
||||
@ -143,7 +152,7 @@ export type QueryBuilderContextType = {
|
||||
queryBuilderData: QueryBuilderData;
|
||||
initialDataSource: DataSource | null;
|
||||
resetQueryBuilderData: () => void;
|
||||
handleSetQueryData: (index: number, queryData: IBuilderQueryForm) => void;
|
||||
handleSetQueryData: (index: number, queryData: IBuilderQuery) => void;
|
||||
handleSetFormulaData: (index: number, formulaData: IBuilderFormula) => void;
|
||||
initQueryBuilderData: (queryBuilderData: QueryBuilderData) => void;
|
||||
setupInitialDataSource: (newInitialDataSource: DataSource | null) => void;
|
||||
@ -151,3 +160,8 @@ export type QueryBuilderContextType = {
|
||||
addNewQuery: () => void;
|
||||
addNewFormula: () => void;
|
||||
};
|
||||
|
||||
export type QueryAdditionalFilter = {
|
||||
field: keyof IBuilderQuery;
|
||||
text: string;
|
||||
};
|
||||
|
@ -8,4 +8,5 @@ export default interface DashboardReducer {
|
||||
isEditMode: boolean;
|
||||
isQueryFired: boolean;
|
||||
isAddWidget: boolean;
|
||||
isLoadingQueryResult: boolean;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user