From 8679f2c37a068cf83935a478acac23ce3d22d0a6 Mon Sep 17 00:00:00 2001 From: Yevhen Shevchenko <90138953+yeshev@users.noreply.github.com> Date: Wed, 10 May 2023 19:40:27 +0300 Subject: [PATCH] feat: add query builder to the alerts (#2657) * 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 * feat: add query builder to the alerts * fix: alert response integration with query builder * fix: validation of query builder rules * fix: rules query builder * fix: filter value with similar keys * fix: null values for options * fix: query builder disabled when exist formula * fix: removing filter key with underscore * feat: add builder data to metric application (#2665) * feat: add builder data to metric application * fix: query types to single variant * fix: formula legend formatting * fix: argumant name * fix: date for graph --------- Co-authored-by: Palash Gupta * fix: pipeline --------- Co-authored-by: Palash Gupta --- frontend/src/constants/alerts.ts | 9 + frontend/src/constants/queryBuilder.ts | 2 +- frontend/src/constants/regExp.ts | 5 + .../src/container/CreateAlertRule/defaults.ts | 96 ++---- .../FormAlertRules/ChartPreview/index.tsx | 11 +- .../container/FormAlertRules/QuerySection.tsx | 275 ++---------------- .../src/container/FormAlertRules/index.tsx | 133 +++------ .../src/container/FormAlertRules/styles.ts | 1 - .../src/container/FormAlertRules/utils.ts | 101 +------ .../src/container/GridGraphLayout/utils.ts | 4 +- .../MetricsPageQueries/DBCallQueries.ts | 63 ++-- .../MetricsPageQueries/ExternalQueries.ts | 135 ++++++--- .../MetricsPageQueriesFactory.ts | 80 ++--- .../MetricsPageQueries/OverviewQueries.ts | 96 ++++-- .../MetricsApplication/Tabs/DBCall.tsx | 24 +- .../MetricsApplication/Tabs/External.tsx | 39 ++- .../MetricsApplication/Tabs/Overview.tsx | 21 +- .../container/MetricsApplication/Tabs/util.ts | 6 +- .../QueryBuilder/clickHouse/index.tsx | 2 +- .../QueryBuilder/queryBuilder/query.tsx | 2 - .../QueryBuilder/queryBuilder/utils.ts | 15 +- .../LeftContainer/QuerySection/constants.ts | 17 +- .../LeftContainer/QuerySection/index.tsx | 30 +- .../LeftContainer/QuerySection/types.ts | 14 - .../QuerySection/utils/getQueryKey.ts | 10 - .../container/QueryBuilder/QueryBuilder.tsx | 25 +- .../FilterLabel/FilterLabel.styled.ts | 2 +- .../ListMarker/ListMarker.styled.ts | 1 - .../components/Query/Query.interfaces.ts | 2 - .../QueryBuilder/components/Query/Query.tsx | 13 +- .../AggregatorFilter/AggregatorFilter.tsx | 27 +- .../GroupByFilter/GroupByFilter.interfaces.ts | 8 - .../filters/GroupByFilter/GroupByFilter.tsx | 70 +++-- .../filters/HavingFilter/HavingFilter.tsx | 5 +- .../filters/QueryBuilderSearch/index.tsx | 3 + .../hooks/queryBuilder/useQueryOperations.ts | 54 ++-- .../src/hooks/useResourceAttribute/utils.ts | 6 +- .../newQueryBuilder/convertNewDataToOld.ts | 2 +- .../newQueryBuilder/getFilterObjectValue.ts | 31 ++ .../getOperatorsBySourceAndPanelType.ts | 24 ++ .../mapQueryDataFromApi.ts | 20 ++ .../queryBuilderMappers/mapQueryDataToApi.ts | 18 +- frontend/src/providers/QueryBuilder.tsx | 43 ++- .../store/actions/dashboard/getDashboard.ts | 4 +- .../actions/dashboard/getQueryResults.ts | 14 +- frontend/src/types/api/alerts/alertTypes.ts | 1 - .../src/types/api/alerts/compositeQuery.ts | 5 +- frontend/src/types/api/alerts/def.ts | 2 +- frontend/src/types/api/alerts/queryType.ts | 17 -- frontend/src/types/api/dashboard/getAll.ts | 4 +- frontend/src/types/api/dashboard/shared.ts | 5 - .../queryBuilder/queryAutocompleteResponse.ts | 4 +- .../api/queryBuilder/queryBuilderData.ts | 5 +- frontend/src/types/common/dashboard.ts | 6 +- frontend/src/types/common/operations.types.ts | 5 +- frontend/src/types/common/queryBuilder.ts | 11 +- .../types/common/queryBuilderMappers.types.ts | 14 + frontend/src/types/common/select.ts | 8 + 58 files changed, 726 insertions(+), 924 deletions(-) create mode 100644 frontend/src/constants/alerts.ts create mode 100644 frontend/src/constants/regExp.ts delete mode 100644 frontend/src/container/NewWidget/LeftContainer/QuerySection/utils/getQueryKey.ts create mode 100644 frontend/src/lib/newQueryBuilder/getFilterObjectValue.ts create mode 100644 frontend/src/lib/newQueryBuilder/getOperatorsBySourceAndPanelType.ts create mode 100644 frontend/src/lib/newQueryBuilder/queryBuilderMappers/mapQueryDataFromApi.ts delete mode 100644 frontend/src/types/api/alerts/queryType.ts delete mode 100644 frontend/src/types/api/dashboard/shared.ts create mode 100644 frontend/src/types/common/queryBuilderMappers.types.ts diff --git a/frontend/src/constants/alerts.ts b/frontend/src/constants/alerts.ts new file mode 100644 index 0000000000..3565ded3d7 --- /dev/null +++ b/frontend/src/constants/alerts.ts @@ -0,0 +1,9 @@ +import { AlertTypes } from 'types/api/alerts/alertTypes'; +import { DataSource } from 'types/common/queryBuilder'; + +export const ALERTS_DATA_SOURCE_MAP: Record = { + [AlertTypes.METRICS_BASED_ALERT]: DataSource.METRICS, + [AlertTypes.LOGS_BASED_ALERT]: DataSource.LOGS, + [AlertTypes.TRACES_BASED_ALERT]: DataSource.TRACES, + [AlertTypes.EXCEPTIONS_BASED_ALERT]: DataSource.TRACES, +}; diff --git a/frontend/src/constants/queryBuilder.ts b/frontend/src/constants/queryBuilder.ts index 067df532ef..7c7fc8114f 100644 --- a/frontend/src/constants/queryBuilder.ts +++ b/frontend/src/constants/queryBuilder.ts @@ -86,7 +86,7 @@ export const initialAggregateAttribute: IBuilderQuery['aggregateAttribute'] = { export const initialQueryBuilderFormValues: IBuilderQuery = { dataSource: DataSource.METRICS, queryName: createNewBuilderItemName({ existNames: [], sourceNames: alphabet }), - aggregateOperator: Object.values(MetricAggregateOperator)[0], + aggregateOperator: MetricAggregateOperator.NOOP, aggregateAttribute: initialAggregateAttribute, tagFilters: { items: [], op: 'AND' }, expression: createNewBuilderItemName({ diff --git a/frontend/src/constants/regExp.ts b/frontend/src/constants/regExp.ts new file mode 100644 index 0000000000..3ea1e3179c --- /dev/null +++ b/frontend/src/constants/regExp.ts @@ -0,0 +1,5 @@ +export const FORMULA_REGEXP = /F\d+/; + +export const HAVING_FILTER_REGEXP = /^[-\d.,\s]+$/; + +export const TYPE_ADDON_REGEXP = /_(.+)/; diff --git a/frontend/src/container/CreateAlertRule/defaults.ts b/frontend/src/container/CreateAlertRule/defaults.ts index 5ce07a03be..2b85030035 100644 --- a/frontend/src/container/CreateAlertRule/defaults.ts +++ b/frontend/src/container/CreateAlertRule/defaults.ts @@ -1,3 +1,7 @@ +import { + initialQueryBuilderFormValues, + PANEL_TYPES, +} from 'constants/queryBuilder'; import { AlertTypes } from 'types/api/alerts/alertTypes'; import { AlertDef, @@ -5,6 +9,12 @@ import { defaultEvalWindow, defaultMatchType, } from 'types/api/alerts/def'; +import { EQueryType } from 'types/common/dashboard'; +import { + DataSource, + LogsAggregatorOperator, + TracesAggregatorOperator, +} from 'types/common/queryBuilder'; const defaultAlertDescription = 'This alert is fired when the defined metric (current value: {{$value}}) crosses the threshold ({{$threshold}})'; @@ -19,28 +29,16 @@ const defaultAnnotations = { export const alertDefaults: AlertDef = { alertType: AlertTypes.METRICS_BASED_ALERT, condition: { - compositeMetricQuery: { + compositeQuery: { builderQueries: { A: { - queryName: 'A', - name: 'A', - formulaOnly: false, - metricName: '', - tagFilters: { - op: 'AND', - items: [], - }, - groupBy: [], - aggregateOperator: 1, - expression: 'A', - disabled: false, - toggleDisable: false, - toggleDelete: false, + ...initialQueryBuilderFormValues, }, }, promQueries: {}, chQueries: {}, - queryType: 1, + queryType: EQueryType.QUERY_BUILDER, + panelType: PANEL_TYPES.TIME_SERIES, }, op: defaultCompareOp, matchType: defaultMatchType, @@ -55,23 +53,12 @@ export const alertDefaults: AlertDef = { export const logAlertDefaults: AlertDef = { alertType: AlertTypes.LOGS_BASED_ALERT, condition: { - compositeMetricQuery: { + compositeQuery: { builderQueries: { A: { - queryName: 'A', - name: 'A', - formulaOnly: false, - metricName: '', - tagFilters: { - op: 'AND', - items: [], - }, - groupBy: [], - aggregateOperator: 1, - expression: 'A', - disabled: false, - toggleDisable: false, - toggleDelete: false, + ...initialQueryBuilderFormValues, + aggregateOperator: LogsAggregatorOperator.COUNT, + dataSource: DataSource.LOGS, }, }, promQueries: {}, @@ -84,7 +71,8 @@ export const logAlertDefaults: AlertDef = { disabled: false, }, }, - queryType: 2, + queryType: EQueryType.CLICKHOUSE, + panelType: PANEL_TYPES.TIME_SERIES, }, op: defaultCompareOp, matchType: '4', @@ -100,23 +88,12 @@ export const logAlertDefaults: AlertDef = { export const traceAlertDefaults: AlertDef = { alertType: AlertTypes.TRACES_BASED_ALERT, condition: { - compositeMetricQuery: { + compositeQuery: { builderQueries: { A: { - queryName: 'A', - name: 'A', - formulaOnly: false, - metricName: '', - tagFilters: { - op: 'AND', - items: [], - }, - groupBy: [], - aggregateOperator: 1, - expression: 'A', - disabled: false, - toggleDisable: false, - toggleDelete: false, + ...initialQueryBuilderFormValues, + aggregateOperator: TracesAggregatorOperator.COUNT, + dataSource: DataSource.TRACES, }, }, promQueries: {}, @@ -129,7 +106,8 @@ export const traceAlertDefaults: AlertDef = { disabled: false, }, }, - queryType: 2, + queryType: EQueryType.CLICKHOUSE, + panelType: PANEL_TYPES.TIME_SERIES, }, op: defaultCompareOp, matchType: '4', @@ -145,23 +123,12 @@ export const traceAlertDefaults: AlertDef = { export const exceptionAlertDefaults: AlertDef = { alertType: AlertTypes.EXCEPTIONS_BASED_ALERT, condition: { - compositeMetricQuery: { + compositeQuery: { builderQueries: { A: { - queryName: 'A', - name: 'A', - formulaOnly: false, - metricName: '', - tagFilters: { - op: 'AND', - items: [], - }, - groupBy: [], - aggregateOperator: 1, - expression: 'A', - disabled: false, - toggleDisable: false, - toggleDelete: false, + ...initialQueryBuilderFormValues, + aggregateOperator: TracesAggregatorOperator.COUNT, + dataSource: DataSource.TRACES, }, }, promQueries: {}, @@ -174,7 +141,8 @@ export const exceptionAlertDefaults: AlertDef = { disabled: false, }, }, - queryType: 2, + queryType: EQueryType.CLICKHOUSE, + panelType: PANEL_TYPES.TIME_SERIES, }, op: defaultCompareOp, matchType: '4', diff --git a/frontend/src/container/FormAlertRules/ChartPreview/index.tsx b/frontend/src/container/FormAlertRules/ChartPreview/index.tsx index 47d9e3ef10..040a53348f 100644 --- a/frontend/src/container/FormAlertRules/ChartPreview/index.tsx +++ b/frontend/src/container/FormAlertRules/ChartPreview/index.tsx @@ -60,10 +60,11 @@ function ChartPreview({ switch (query?.queryType) { case EQueryType.PROM: - return query.promQL?.length > 0 && query.promQL[0].query !== ''; + return query.promql?.length > 0 && query.promql[0].query !== ''; case EQueryType.CLICKHOUSE: return ( - query.clickHouse?.length > 0 && query.clickHouse[0].rawQuery?.length > 0 + query.clickhouse_sql?.length > 0 && + query.clickhouse_sql[0].rawQuery?.length > 0 ); case EQueryType.QUERY_BUILDER: return ( @@ -84,13 +85,13 @@ function ChartPreview({ queryFn: () => GetMetricQueryRange({ query: query || { - queryType: 1, - promQL: [], + queryType: EQueryType.QUERY_BUILDER, + promql: [], builder: { queryFormulas: [], queryData: [], }, - clickHouse: [], + clickhouse_sql: [], }, globalSelectedInterval: selectedInterval, graphType, diff --git a/frontend/src/container/FormAlertRules/QuerySection.tsx b/frontend/src/container/FormAlertRules/QuerySection.tsx index 0f370773c9..0855c52bc0 100644 --- a/frontend/src/container/FormAlertRules/QuerySection.tsx +++ b/frontend/src/container/FormAlertRules/QuerySection.tsx @@ -1,36 +1,20 @@ -import { PlusOutlined } from '@ant-design/icons'; import { Button, Tabs } from 'antd'; +import { ALERTS_DATA_SOURCE_MAP } from 'constants/alerts'; 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 { - IQueryBuilderFormulaHandleChange, - IQueryBuilderQueryHandleChange, -} from 'container/NewWidget/LeftContainer/QuerySection/QueryBuilder/queryBuilder/types'; -import { useNotifications } from 'hooks/useNotifications'; -import React, { useCallback } from 'react'; +import { QueryBuilder } from 'container/QueryBuilder'; +import React from 'react'; import { useTranslation } from 'react-i18next'; import { AlertTypes } from 'types/api/alerts/alertTypes'; -import { - IChQueries, - IFormulaQueries, - IMetricQueries, - IPromQueries, -} from 'types/api/alerts/compositeQuery'; -import { EAggregateOperator, EQueryType } from 'types/common/dashboard'; +import { IChQueries, IPromQueries } from 'types/api/alerts/compositeQuery'; +import { EQueryType } from 'types/common/dashboard'; import ChQuerySection from './ChQuerySection'; import PromqlSection from './PromqlSection'; -import { FormContainer, QueryButton, StepHeading } from './styles'; -import { toIMetricsBuilderQuery } from './utils'; +import { FormContainer, StepHeading } from './styles'; function QuerySection({ queryCategory, setQueryCategory, - metricQueries, - setMetricQueries, - formulaQueries, - setFormulaQueries, promQueries, setPromQueries, chQueries, @@ -41,9 +25,9 @@ function QuerySection({ // init namespace for translations const { t } = useTranslation('alerts'); - const handleQueryCategoryChange = (s: string): void => { + const handleQueryCategoryChange = (queryType: string): void => { if ( - parseInt(s, 10) === EQueryType.PROM && + queryType === EQueryType.PROM && (!promQueries || Object.keys(promQueries).length === 0) ) { setPromQueries({ @@ -58,7 +42,7 @@ function QuerySection({ } if ( - parseInt(s, 10) === EQueryType.CLICKHOUSE && + queryType === EQueryType.CLICKHOUSE && (!chQueries || Object.keys(chQueries).length === 0) ) { setChQueries({ @@ -71,148 +55,9 @@ function QuerySection({ }, }); } - setQueryCategory(parseInt(s, 10)); + setQueryCategory(queryType as EQueryType); }; - const getNextQueryLabel = useCallback((): string => { - let maxAscii = 0; - - Object.keys(metricQueries).forEach((key) => { - const n = key.charCodeAt(0); - if (n > maxAscii) { - maxAscii = n - 64; - } - }); - - return String.fromCharCode(64 + maxAscii + 1); - }, [metricQueries]); - - const handleFormulaChange = ({ - formulaIndex, - expression, - legend, - toggleDisable, - toggleDelete, - }: IQueryBuilderFormulaHandleChange): void => { - const allFormulas = formulaQueries; - const current = allFormulas[formulaIndex]; - if (expression !== undefined) { - current.expression = expression; - } - - if (legend !== undefined) { - current.legend = legend; - } - - if (toggleDisable) { - current.disabled = !current.disabled; - } - - if (toggleDelete) { - delete allFormulas[formulaIndex]; - } else { - allFormulas[formulaIndex] = current; - } - - setFormulaQueries({ - ...allFormulas, - }); - }; - - const handleMetricQueryChange = ({ - queryIndex, - aggregateFunction, - metricName, - tagFilters, - groupBy, - legend, - toggleDisable, - toggleDelete, - }: IQueryBuilderQueryHandleChange): void => { - const allQueries = metricQueries; - const current = metricQueries[queryIndex]; - if (aggregateFunction) { - current.aggregateOperator = aggregateFunction; - } - if (metricName) { - current.metricName = metricName; - } - - if (tagFilters && current.tagFilters) { - current.tagFilters.items = tagFilters; - } - - if (legend) { - current.legend = legend; - } - - if (groupBy) { - current.groupBy = groupBy; - } - - if (toggleDisable) { - current.disabled = !current.disabled; - } - - if (toggleDelete) { - delete allQueries[queryIndex]; - } else { - allQueries[queryIndex] = current; - } - - setMetricQueries({ - ...allQueries, - }); - }; - const { notifications } = useNotifications(); - - const addMetricQuery = useCallback(() => { - if (Object.keys(metricQueries).length > 5) { - notifications.error({ - message: t('metric_query_max_limit'), - }); - return; - } - - const queryLabel = getNextQueryLabel(); - - const queries = metricQueries; - queries[queryLabel] = { - name: queryLabel, - queryName: queryLabel, - metricName: '', - formulaOnly: false, - aggregateOperator: EAggregateOperator.NOOP, - legend: '', - tagFilters: { - op: 'AND', - items: [], - }, - groupBy: [], - disabled: false, - expression: queryLabel, - }; - setMetricQueries({ ...queries }); - }, [t, getNextQueryLabel, metricQueries, setMetricQueries, notifications]); - - const addFormula = useCallback(() => { - // defaulting to F1 as only one formula is supported - // in alert definition - const queryLabel = 'F1'; - - const formulas = formulaQueries; - formulas[queryLabel] = { - queryName: queryLabel, - name: queryLabel, - formulaOnly: true, - expression: 'A', - disabled: false, - legend: '', - }; - - setFormulaQueries({ ...formulas }); - }, [formulaQueries, setFormulaQueries]); - const renderPromqlUI = (): JSX.Element => ( ); @@ -221,61 +66,14 @@ function QuerySection({ ); - const renderFormulaButton = (): JSX.Element => ( - }> - {t('button_formula')} - - ); - - const renderQueryButton = (): JSX.Element => ( - }> - {t('button_query')} - - ); - const renderMetricUI = (): JSX.Element => ( -
- {metricQueries && - Object.keys(metricQueries).map((key: string) => { - // todo(amol): need to handle this in fetch - const current = metricQueries[key]; - current.name = key; - - return ( - - ); - })} - - {queryCategory !== EQueryType.PROM && renderQueryButton()} -
- {formulaQueries && - Object.keys(formulaQueries).map((key: string) => { - // todo(amol): need to handle this in fetch - const current = formulaQueries[key]; - current.name = key; - - return ( - - ); - })} - {queryCategory === EQueryType.QUERY_BUILDER && - (!formulaQueries || Object.keys(formulaQueries).length === 0) && - metricQueries && - Object.keys(metricQueries).length > 0 && - renderFormulaButton()} -
-
+ ); const handleRunQuery = (): void => { @@ -285,19 +83,18 @@ function QuerySection({ const tabs = [ { label: t('tab_qb'), - key: EQueryType.QUERY_BUILDER.toString(), - disabled: true, + key: EQueryType.QUERY_BUILDER, }, { label: t('tab_chquery'), - key: EQueryType.CLICKHOUSE.toString(), + key: EQueryType.CLICKHOUSE, }, ]; const items = [ - { label: t('tab_qb'), key: EQueryType.QUERY_BUILDER.toString() }, - { label: t('tab_chquery'), key: EQueryType.CLICKHOUSE.toString() }, - { label: t('tab_promql'), key: EQueryType.PROM.toString() }, + { label: t('tab_qb'), key: EQueryType.QUERY_BUILDER }, + { label: t('tab_chquery'), key: EQueryType.CLICKHOUSE }, + { label: t('tab_promql'), key: EQueryType.PROM }, ]; const renderTabs = (typ: AlertTypes): JSX.Element | null => { @@ -309,16 +106,14 @@ function QuerySection({ - {queryCategory === EQueryType.CLICKHOUSE && ( - - )} + } items={tabs} @@ -330,16 +125,14 @@ function QuerySection({ - {queryCategory === EQueryType.CLICKHOUSE && ( - - )} + } items={items} @@ -373,10 +166,6 @@ function QuerySection({ interface QuerySectionProps { queryCategory: EQueryType; setQueryCategory: (n: EQueryType) => void; - metricQueries: IMetricQueries; - setMetricQueries: (b: IMetricQueries) => void; - formulaQueries: IFormulaQueries; - setFormulaQueries: (b: IFormulaQueries) => void; promQueries: IPromQueries; setPromQueries: (p: IPromQueries) => void; chQueries: IChQueries; diff --git a/frontend/src/container/FormAlertRules/index.tsx b/frontend/src/container/FormAlertRules/index.tsx index 9a3ea93466..983760be1b 100644 --- a/frontend/src/container/FormAlertRules/index.tsx +++ b/frontend/src/container/FormAlertRules/index.tsx @@ -5,18 +5,16 @@ import testAlertApi from 'api/alerts/testAlert'; import ROUTES from 'constants/routes'; import QueryTypeTag from 'container/NewWidget/LeftContainer/QueryTypeTag'; import PlotTag from 'container/NewWidget/LeftContainer/WidgetGraph/PlotTag'; +import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; import { useNotifications } from 'hooks/useNotifications'; import history from 'lib/history'; +import { mapQueryDataFromApi } from 'lib/newQueryBuilder/queryBuilderMappers/mapQueryDataFromApi'; +import { mapQueryDataToApi } from 'lib/newQueryBuilder/queryBuilderMappers/mapQueryDataToApi'; import React, { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useQueryClient } from 'react-query'; import { AlertTypes } from 'types/api/alerts/alertTypes'; -import { - IChQueries, - IFormulaQueries, - IMetricQueries, - IPromQueries, -} from 'types/api/alerts/compositeQuery'; +import { IChQueries, IPromQueries } from 'types/api/alerts/compositeQuery'; import { AlertDef, defaultEvalWindow, @@ -36,15 +34,8 @@ import { PanelContainer, StyledLeftContainer, } from './styles'; -import useDebounce from './useDebounce'; import UserGuide from './UserGuide'; -import { - prepareBuilderQueries, - prepareStagedQuery, - toChartInterval, - toFormulaQueries, - toMetricQueries, -} from './utils'; +import { prepareStagedQuery, toChartInterval } from './utils'; function FormAlertRules({ alertType, @@ -55,33 +46,21 @@ function FormAlertRules({ // init namespace for translations const { t } = useTranslation('alerts'); + const { queryBuilderData, initQueryBuilderData } = useQueryBuilder(); + // use query client const ruleCache = useQueryClient(); const [loading, setLoading] = useState(false); - // queryRunId helps to override of query caching for clickhouse query - // tab. A random string will be assigned for each execution - const [runQueryId, setRunQueryId] = useState(); - // alertDef holds the form values to be posted const [alertDef, setAlertDef] = useState(initialValue); // initQuery contains initial query when component was mounted - const initQuery = initialValue?.condition?.compositeMetricQuery; + const initQuery = initialValue.condition.compositeQuery; const [queryCategory, setQueryCategory] = useState( - initQuery?.queryType, - ); - - // local state to handle metric queries - const [metricQueries, setMetricQueries] = useState( - toMetricQueries(initQuery?.builderQueries), - ); - - // local state to handle formula queries - const [formulaQueries, setFormulaQueries] = useState( - toFormulaQueries(initQuery?.builderQueries), + initQuery.queryType, ); // local state to handle promql queries @@ -106,43 +85,31 @@ function FormAlertRules({ // run query button is provided. const [manualStagedQuery, setManualStagedQuery] = useState(); - // delay to reduce load on backend api with auto-run query. only for clickhouse - // queries we have manual run, hence both debounce and debounceStagedQuery are not required - const debounceDelay = queryCategory !== EQueryType.CLICKHOUSE ? 1000 : 0; - - // debounce query to delay backend api call and chart update. - // used in query builder and promql tabs to enable auto-refresh - // of chart on user edit - const debouncedStagedQuery = useDebounce(stagedQuery, debounceDelay); - // this use effect initiates staged query and // other queries based on server data. // useful when fetching of initial values (from api) // is delayed useEffect(() => { - const initQuery = initialValue?.condition?.compositeMetricQuery; - const typ = initQuery?.queryType; + const initQuery = initialValue?.condition?.compositeQuery; + const type = initQuery.queryType; - // extract metric query from builderQueries - const mq = toMetricQueries(initQuery?.builderQueries); - - // extract formula query from builderQueries - const fq = toFormulaQueries(initQuery?.builderQueries); + const builderData = mapQueryDataFromApi( + initialValue?.condition?.compositeQuery?.builderQueries || {}, + ); // prepare staged query const sq = prepareStagedQuery( - typ, - mq, - fq, + type, + builderData.queryData, + builderData.queryFormulas, initQuery?.promQueries, initQuery?.chQueries, ); const pq = initQuery?.promQueries; const chq = initQuery?.chQueries; - setQueryCategory(typ); - setMetricQueries(mq); - setFormulaQueries(fq); + setQueryCategory(type); + initQueryBuilderData(builderData); setPromQueries(pq); setStagedQuery(sq); @@ -151,7 +118,7 @@ function FormAlertRules({ setChQueries(chq); setAlertDef(initialValue); - }, [initialValue]); + }, [initialValue, initQueryBuilderData]); // this useEffect updates staging query when // any of its sub-parameters changes @@ -159,16 +126,15 @@ function FormAlertRules({ // prepare staged query const sq: StagedQuery = prepareStagedQuery( queryCategory, - metricQueries, - formulaQueries, + queryBuilderData.queryData, + queryBuilderData.queryFormulas, promQueries, chQueries, ); setStagedQuery(sq); - }, [queryCategory, chQueries, metricQueries, formulaQueries, promQueries]); + }, [queryCategory, chQueries, queryBuilderData, promQueries]); const onRunQuery = (): void => { - setRunQueryId(Math.random().toString(36).substring(2, 15)); setManualStagedQuery(stagedQuery); }; @@ -190,6 +156,15 @@ function FormAlertRules({ evalWindow: defaultEvalWindow, }); } + + const sq: StagedQuery = prepareStagedQuery( + val, + queryBuilderData.queryData, + queryBuilderData.queryFormulas, + promQueries, + chQueries, + ); + setManualStagedQuery(sq); }; const { notifications } = useNotifications(); @@ -244,10 +219,9 @@ function FormAlertRules({ }, [t, chQueries, queryCategory, notifications]); const validateQBParams = useCallback((): boolean => { - let retval = true; if (queryCategory !== EQueryType.QUERY_BUILDER) return true; - if (!metricQueries || Object.keys(metricQueries).length === 0) { + if (!queryBuilderData.queryData || queryBuilderData.queryData.length === 0) { notifications.error({ message: 'Error', description: t('condition_required'), @@ -263,27 +237,8 @@ function FormAlertRules({ return false; } - Object.keys(metricQueries).forEach((key) => { - if (metricQueries[key].metricName === '') { - notifications.error({ - message: 'Error', - description: t('metricname_missing', { where: metricQueries[key].name }), - }); - retval = false; - } - }); - - Object.keys(formulaQueries).forEach((key) => { - if (formulaQueries[key].expression === '') { - notifications.error({ - message: 'Error', - description: t('expression_missing', formulaQueries[key].name), - }); - retval = false; - } - }); - return retval; - }, [t, alertDef, queryCategory, metricQueries, formulaQueries, notifications]); + return true; + }, [t, alertDef, queryCategory, queryBuilderData, notifications]); const isFormValid = useCallback((): boolean => { if (!alertDef.alert || alertDef.alert === '') { @@ -321,11 +276,12 @@ function FormAlertRules({ queryCategory === EQueryType.PROM ? 'promql_rule' : 'threshold_rule', condition: { ...alertDef.condition, - compositeMetricQuery: { - builderQueries: prepareBuilderQueries(metricQueries, formulaQueries), + compositeQuery: { + builderQueries: mapQueryDataToApi(queryBuilderData).data, promQueries, chQueries, queryType: queryCategory, + panelType: initQuery.panelType, }, }, }; @@ -335,11 +291,11 @@ function FormAlertRules({ const memoizedPreparePostData = useCallback(preparePostData, [ queryCategory, alertDef, - metricQueries, - formulaQueries, + queryBuilderData, promQueries, chQueries, alertType, + initQuery, ]); const saveRule = useCallback(async () => { @@ -458,7 +414,7 @@ function FormAlertRules({ headline={} name="" threshold={alertDef.condition?.target} - query={debouncedStagedQuery} + query={manualStagedQuery} selectedInterval={toChartInterval(alertDef.evalWindow)} /> ); @@ -468,7 +424,7 @@ function FormAlertRules({ headline={} name="Chart Preview" threshold={alertDef.condition?.target} - query={debouncedStagedQuery} + query={manualStagedQuery} /> ); @@ -478,7 +434,6 @@ function FormAlertRules({ name="Chart Preview" threshold={alertDef.condition?.target} query={manualStagedQuery} - userQueryKey={runQueryId} selectedInterval={toChartInterval(alertDef.evalWindow)} /> ); @@ -498,10 +453,6 @@ function FormAlertRules({ { - const f: IFormulaQueries = {}; - if (!b) return f; - Object.keys(b).forEach((key) => { - if (key === 'F1') { - f[key] = b[key] as IFormulaQuery; - } - }); - - return f; -}; - -export const toMetricQueries = (b: IBuilderQueries): IMetricQueries => { - const m: IMetricQueries = {}; - if (!b) return m; - Object.keys(b).forEach((key) => { - if (key !== 'F1') { - m[key] = b[key] as IMetricQuery; - } - }); - - return m; -}; - -export const toIMetricsBuilderQuery = ( - q: IMetricQuery, -): IMetricsBuilderQuery => ({ - name: q.name, - metricName: q.metricName, - tagFilters: q.tagFilters, - groupBy: q.groupBy, - aggregateOperator: q.aggregateOperator, - disabled: q.disabled, - legend: q.legend, -}); - -export const prepareBuilderQueries = ( - m: IMetricQueries, - f: IFormulaQueries, -): IBuilderQueries => { - if (!m) return {}; - const b: IBuilderQueries = { - ...m, - }; - - Object.keys(f).forEach((key) => { - b[key] = { - ...f[key], - aggregateOperator: undefined, - metricName: '', - }; - }); - return b; -}; - export const prepareStagedQuery = ( t: EQueryType, - m: IMetricQueries, - f: IFormulaQueries, + m: IBuilderQuery[], + f: IBuilderFormula[], p: IPromQueries, c: IChQueries, ): IStagedQuery => { - const qbList: IMetricQuery[] = []; - const formulaList: IFormulaQuery[] = []; const promList: IPromQuery[] = []; const chQueryList: IChQuery[] = []; - // convert map[string]IMetricQuery to IMetricQuery[] - if (m) { - Object.keys(m).forEach((key) => { - qbList.push(m[key]); - }); - } - - // convert map[string]IFormulaQuery to IFormulaQuery[] - if (f) { - Object.keys(f).forEach((key) => { - formulaList.push(f[key]); - }); - } - // convert map[string]IPromQuery to IPromQuery[] if (p) { Object.keys(p).forEach((key) => { @@ -115,13 +37,12 @@ export const prepareStagedQuery = ( return { queryType: t, - promQL: promList, - // TODO: change it later to actual builder - metricsBuilder: { - formulas: formulaList, - queryBuilder: qbList, + promql: promList, + builder: { + queryFormulas: f, + queryData: m, }, - clickHouse: chQueryList, + clickhouse_sql: chQueryList, }; }; diff --git a/frontend/src/container/GridGraphLayout/utils.ts b/frontend/src/container/GridGraphLayout/utils.ts index 5f411aa26b..04944451fa 100644 --- a/frontend/src/container/GridGraphLayout/utils.ts +++ b/frontend/src/container/GridGraphLayout/utils.ts @@ -42,13 +42,13 @@ export const UpdateDashboard = async ( panelTypes: graphType, query: { queryType: EQueryType.QUERY_BUILDER, - promQL: [ + promql: [ { name: GetQueryName([]) || '', ...PromQLQueryTemplate, }, ], - clickHouse: [ + clickhouse_sql: [ { name: GetQueryName([]) || '', ...ClickHouseQueryTemplate, diff --git a/frontend/src/container/MetricsApplication/MetricsPageQueries/DBCallQueries.ts b/frontend/src/container/MetricsApplication/MetricsPageQueries/DBCallQueries.ts index 0b0f9ad4c8..e85b4d4ace 100644 --- a/frontend/src/container/MetricsApplication/MetricsPageQueries/DBCallQueries.ts +++ b/frontend/src/container/MetricsApplication/MetricsPageQueries/DBCallQueries.ts @@ -1,8 +1,6 @@ -import { - IMetricsBuilderFormula, - IMetricsBuilderQuery, - IQueryBuilderTagFilterItems, -} from 'types/api/dashboard/getAll'; +import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; +import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData'; +import { QueryBuilderData } from 'types/common/queryBuilder'; import { getQueryBuilderQueries, @@ -13,16 +11,25 @@ export const databaseCallsRPS = ({ servicename, legend, tagFilterItems, -}: DatabaseCallsRPSProps): { - formulas: IMetricsBuilderFormula[]; - queryBuilder: IMetricsBuilderQuery[]; -} => { - const metricName = 'signoz_db_latency_count'; - const groupBy = ['db_system']; - const itemsA = [ +}: DatabaseCallsRPSProps): QueryBuilderData => { + const metricName: BaseAutocompleteData = { + dataType: 'float64', + isColumn: true, + key: 'signoz_db_latency_count', + type: null, + }; + const groupBy: BaseAutocompleteData[] = [ + { dataType: 'string', isColumn: false, key: 'db_system', type: 'tag' }, + ]; + const itemsA: TagFilterItem[] = [ { id: '', - key: 'service_name', + key: { + dataType: 'string', + isColumn: false, + key: 'service_name', + type: 'resource', + }, op: 'IN', value: [`${servicename}`], }, @@ -40,20 +47,32 @@ export const databaseCallsRPS = ({ export const databaseCallsAvgDuration = ({ servicename, tagFilterItems, -}: DatabaseCallProps): { - formulas: IMetricsBuilderFormula[]; - queryBuilder: IMetricsBuilderQuery[]; -} => { - const metricNameA = 'signoz_db_latency_sum'; - const metricNameB = 'signoz_db_latency_count'; +}: DatabaseCallProps): QueryBuilderData => { + const metricNameA: BaseAutocompleteData = { + dataType: 'float64', + isColumn: true, + key: 'signoz_db_latency_sum', + type: null, + }; + const metricNameB: BaseAutocompleteData = { + dataType: 'float64', + isColumn: true, + key: 'signoz_db_latency_count', + type: null, + }; const expression = 'A/B'; const legendFormula = 'Average Duration'; const legend = ''; const disabled = true; - const additionalItemsA = [ + const additionalItemsA: TagFilterItem[] = [ { id: '', - key: 'service_name', + key: { + dataType: 'string', + isColumn: false, + key: 'service_name', + type: 'resource', + }, op: 'IN', value: [`${servicename}`], }, @@ -79,5 +98,5 @@ interface DatabaseCallsRPSProps extends DatabaseCallProps { interface DatabaseCallProps { servicename: string | undefined; - tagFilterItems: IQueryBuilderTagFilterItems[] | []; + tagFilterItems: TagFilterItem[]; } diff --git a/frontend/src/container/MetricsApplication/MetricsPageQueries/ExternalQueries.ts b/frontend/src/container/MetricsApplication/MetricsPageQueries/ExternalQueries.ts index c202fefe65..69af5042f2 100644 --- a/frontend/src/container/MetricsApplication/MetricsPageQueries/ExternalQueries.ts +++ b/frontend/src/container/MetricsApplication/MetricsPageQueries/ExternalQueries.ts @@ -1,45 +1,67 @@ -import { - IMetricsBuilderFormula, - IMetricsBuilderQuery, - IQueryBuilderTagFilterItems, -} from 'types/api/dashboard/getAll'; +import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; +import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData'; +import { QueryBuilderData } from 'types/common/queryBuilder'; import { getQueryBuilderQueries, getQueryBuilderQuerieswithFormula, } from './MetricsPageQueriesFactory'; -const groupBy = ['address']; +const groupBy: BaseAutocompleteData[] = [ + { dataType: 'string', isColumn: false, key: 'address', type: 'tag' }, +]; export const externalCallErrorPercent = ({ servicename, legend, tagFilterItems, -}: ExternalCallDurationByAddressProps): { - formulas: IMetricsBuilderFormula[]; - queryBuilder: IMetricsBuilderQuery[]; -} => { - const metricNameA = 'signoz_external_call_latency_count'; - const metricNameB = 'signoz_external_call_latency_count'; - const additionalItemsA = [ +}: ExternalCallDurationByAddressProps): QueryBuilderData => { + const metricNameA: BaseAutocompleteData = { + dataType: 'float64', + isColumn: true, + key: 'signoz_external_call_latency_count', + type: null, + }; + const metricNameB: BaseAutocompleteData = { + dataType: 'float64', + isColumn: true, + key: 'signoz_external_call_latency_count', + type: null, + }; + const additionalItemsA: TagFilterItem[] = [ { id: '', - key: 'service_name', + key: { + dataType: 'string', + isColumn: false, + key: 'service_name', + type: 'resource', + }, op: 'IN', value: [`${servicename}`], }, { id: '', - key: 'status_code', + key: { + dataType: 'int64', + isColumn: false, + key: 'status_code', + type: 'tag', + }, op: 'IN', value: ['STATUS_CODE_ERROR'], }, ...tagFilterItems, ]; - const additionalItemsB = [ + const additionalItemsB: TagFilterItem[] = [ { id: '', - key: 'service_name', + key: { + dataType: 'string', + isColumn: false, + key: 'service_name', + type: 'resource', + }, op: 'IN', value: [`${servicename}`], }, @@ -64,20 +86,32 @@ export const externalCallErrorPercent = ({ export const externalCallDuration = ({ servicename, tagFilterItems, -}: ExternalCallProps): { - formulas: IMetricsBuilderFormula[]; - queryBuilder: IMetricsBuilderQuery[]; -} => { - const metricNameA = 'signoz_external_call_latency_sum'; - const metricNameB = 'signoz_external_call_latency_count'; +}: ExternalCallProps): QueryBuilderData => { + const metricNameA: BaseAutocompleteData = { + dataType: 'float64', + isColumn: true, + key: 'signoz_external_call_latency_sum', + type: null, + }; + const metricNameB: BaseAutocompleteData = { + dataType: 'float64', + isColumn: true, + key: 'signoz_external_call_latency_count', + type: null, + }; const expression = 'A/B'; const legendFormula = 'Average Duration'; const legend = ''; const disabled = true; - const additionalItemsA = [ + const additionalItemsA: TagFilterItem[] = [ { id: '', - key: 'service_name', + key: { + dataType: 'string', + isColumn: false, + key: 'service_name', + type: 'resource', + }, op: 'IN', value: [`${servicename}`], }, @@ -101,15 +135,22 @@ export const externalCallRpsByAddress = ({ servicename, legend, tagFilterItems, -}: ExternalCallDurationByAddressProps): { - formulas: IMetricsBuilderFormula[]; - queryBuilder: IMetricsBuilderQuery[]; -} => { - const metricName = 'signoz_external_call_latency_count'; - const itemsA = [ +}: ExternalCallDurationByAddressProps): QueryBuilderData => { + const metricName: BaseAutocompleteData = { + dataType: 'float64', + isColumn: true, + key: 'signoz_external_call_latency_count', + type: null, + }; + const itemsA: TagFilterItem[] = [ { id: '', - key: 'service_name', + key: { + dataType: 'string', + isColumn: false, + key: 'service_name', + type: 'resource', + }, op: 'IN', value: [`${servicename}`], }, @@ -127,19 +168,31 @@ export const externalCallDurationByAddress = ({ servicename, legend, tagFilterItems, -}: ExternalCallDurationByAddressProps): { - formulas: IMetricsBuilderFormula[]; - queryBuilder: IMetricsBuilderQuery[]; -} => { - const metricNameA = 'signoz_external_call_latency_sum'; - const metricNameB = 'signoz_external_call_latency_count'; +}: ExternalCallDurationByAddressProps): QueryBuilderData => { + const metricNameA: BaseAutocompleteData = { + dataType: 'float64', + isColumn: true, + key: 'signoz_external_call_latency_sum', + type: null, + }; + const metricNameB: BaseAutocompleteData = { + dataType: 'float64', + isColumn: true, + key: 'signoz_external_call_latency_count', + type: null, + }; const expression = 'A/B'; const legendFormula = legend; const disabled = true; - const additionalItemsA = [ + const additionalItemsA: TagFilterItem[] = [ { id: '', - key: 'service_name', + key: { + dataType: 'string', + isColumn: false, + key: 'service_name', + type: 'resource', + }, op: 'IN', value: [`${servicename}`], }, @@ -166,5 +219,5 @@ interface ExternalCallDurationByAddressProps extends ExternalCallProps { export interface ExternalCallProps { servicename: string | undefined; - tagFilterItems: IQueryBuilderTagFilterItems[]; + tagFilterItems: TagFilterItem[]; } diff --git a/frontend/src/container/MetricsApplication/MetricsPageQueries/MetricsPageQueriesFactory.ts b/frontend/src/container/MetricsApplication/MetricsPageQueries/MetricsPageQueriesFactory.ts index 4654ebf20f..9acc614f47 100644 --- a/frontend/src/container/MetricsApplication/MetricsPageQueries/MetricsPageQueriesFactory.ts +++ b/frontend/src/container/MetricsApplication/MetricsPageQueries/MetricsPageQueriesFactory.ts @@ -1,28 +1,30 @@ import { - IMetricsBuilderFormula, - IMetricsBuilderQuery, - IQueryBuilderTagFilterItems, -} from 'types/api/dashboard/getAll'; + initialFormulaBuilderFormValues, + initialQueryBuilderFormValues, +} from 'constants/queryBuilder'; +import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; +import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData'; +import { + MetricAggregateOperator, + QueryBuilderData, +} from 'types/common/queryBuilder'; export const getQueryBuilderQueries = ({ metricName, - groupBy, + groupBy = [], legend, itemsA, -}: BuilderQueriesProps): { - formulas: IMetricsBuilderFormula[]; - queryBuilder: IMetricsBuilderQuery[]; -} => ({ - formulas: [], - queryBuilder: [ +}: BuilderQueriesProps): QueryBuilderData => ({ + queryFormulas: [], + queryData: [ { - aggregateOperator: 18, + ...initialQueryBuilderFormValues, + aggregateOperator: MetricAggregateOperator.SUM_RATE, disabled: false, groupBy, + aggregateAttribute: metricName, legend, - metricName, - name: 'A', - reduceTo: 1, + reduceTo: 'sum', tagFilters: { items: itemsA, op: 'AND', @@ -37,44 +39,42 @@ export const getQueryBuilderQuerieswithFormula = ({ additionalItemsA, additionalItemsB, legend, - groupBy, + groupBy = [], disabled, expression, legendFormula, -}: BuilderQuerieswithFormulaProps): { - formulas: IMetricsBuilderFormula[]; - queryBuilder: IMetricsBuilderQuery[]; -} => ({ - formulas: [ +}: BuilderQuerieswithFormulaProps): QueryBuilderData => ({ + queryFormulas: [ { - disabled: false, + ...initialFormulaBuilderFormValues, expression, - name: 'F1', legend: legendFormula, }, ], - queryBuilder: [ + queryData: [ { - aggregateOperator: 18, + ...initialQueryBuilderFormValues, + aggregateOperator: MetricAggregateOperator.SUM_RATE, disabled, groupBy, legend, - metricName: metricNameA, - name: 'A', - reduceTo: 1, + aggregateAttribute: metricNameA, + reduceTo: 'sum', tagFilters: { items: additionalItemsA, op: 'AND', }, }, { - aggregateOperator: 18, + ...initialQueryBuilderFormValues, + aggregateOperator: MetricAggregateOperator.SUM_RATE, disabled, groupBy, legend, - metricName: metricNameB, - name: 'B', - reduceTo: 1, + aggregateAttribute: metricNameB, + queryName: 'B', + expression: 'B', + reduceTo: 'sum', tagFilters: { items: additionalItemsB, op: 'AND', @@ -84,20 +84,20 @@ export const getQueryBuilderQuerieswithFormula = ({ }); interface BuilderQueriesProps { - metricName: string; - groupBy?: string[]; + metricName: BaseAutocompleteData; + groupBy?: BaseAutocompleteData[]; legend: string; - itemsA: IQueryBuilderTagFilterItems[]; + itemsA: TagFilterItem[]; } interface BuilderQuerieswithFormulaProps { - metricNameA: string; - metricNameB: string; + metricNameA: BaseAutocompleteData; + metricNameB: BaseAutocompleteData; legend: string; disabled: boolean; - groupBy?: string[]; + groupBy?: BaseAutocompleteData[]; expression: string; legendFormula: string; - additionalItemsA: IQueryBuilderTagFilterItems[]; - additionalItemsB: IQueryBuilderTagFilterItems[]; + additionalItemsA: TagFilterItem[]; + additionalItemsB: TagFilterItem[]; } diff --git a/frontend/src/container/MetricsApplication/MetricsPageQueries/OverviewQueries.ts b/frontend/src/container/MetricsApplication/MetricsPageQueries/OverviewQueries.ts index 8c5f95f943..97a72c4fd2 100644 --- a/frontend/src/container/MetricsApplication/MetricsPageQueries/OverviewQueries.ts +++ b/frontend/src/container/MetricsApplication/MetricsPageQueries/OverviewQueries.ts @@ -1,8 +1,6 @@ -import { - IMetricsBuilderFormula, - IMetricsBuilderQuery, - IQueryBuilderTagFilterItems, -} from 'types/api/dashboard/getAll'; +import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; +import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData'; +import { QueryBuilderData } from 'types/common/queryBuilder'; import { getQueryBuilderQueries, @@ -13,19 +11,35 @@ export const operationPerSec = ({ servicename, tagFilterItems, topLevelOperations, -}: OperationPerSecProps): IOverviewQueries => { - const metricName = 'signoz_latency_count'; +}: OperationPerSecProps): QueryBuilderData => { + const metricName: BaseAutocompleteData = { + dataType: 'float64', + isColumn: true, + key: 'signoz_latency_count', + type: null, + }; const legend = 'Operations'; - const itemsA = [ + + const itemsA: TagFilterItem[] = [ { id: '', - key: 'service_name', + key: { + dataType: 'string', + isColumn: false, + key: 'service_name', + type: 'resource', + }, op: 'IN', value: [`${servicename}`], }, { id: '', - key: 'operation', + key: { + dataType: 'string', + isColumn: false, + key: 'operation', + type: 'tag', + }, op: 'IN', value: topLevelOperations, }, @@ -43,41 +57,76 @@ export const errorPercentage = ({ servicename, tagFilterItems, topLevelOperations, -}: OperationPerSecProps): IOverviewQueries => { - const metricNameA = 'signoz_calls_total'; - const metricNameB = 'signoz_calls_total'; - const additionalItemsA = [ +}: OperationPerSecProps): QueryBuilderData => { + const metricNameA: BaseAutocompleteData = { + dataType: 'float64', + isColumn: true, + key: 'signoz_calls_total', + type: null, + }; + const metricNameB: BaseAutocompleteData = { + dataType: 'float64', + isColumn: true, + key: 'signoz_calls_total', + type: null, + }; + const additionalItemsA: TagFilterItem[] = [ { id: '', - key: 'service_name', + key: { + dataType: 'string', + isColumn: false, + key: 'service_name', + type: 'resource', + }, op: 'IN', value: [`${servicename}`], }, { id: '', - key: 'operation', + key: { + dataType: 'string', + isColumn: false, + key: 'operation', + type: 'tag', + }, op: 'IN', value: topLevelOperations, }, { id: '', - key: 'status_code', + key: { + dataType: 'int64', + isColumn: false, + key: 'status_code', + type: 'tag', + }, op: 'IN', value: ['STATUS_CODE_ERROR'], }, ...tagFilterItems, ]; - const additionalItemsB = [ + const additionalItemsB: TagFilterItem[] = [ { id: '', - key: 'service_name', + key: { + dataType: 'string', + isColumn: false, + key: 'service_name', + type: 'resource', + }, op: 'IN', value: [`${servicename}`], }, { id: '', - key: 'operation', + key: { + dataType: 'string', + isColumn: false, + key: 'operation', + type: 'tag', + }, op: 'IN', value: topLevelOperations, }, @@ -102,11 +151,6 @@ export const errorPercentage = ({ export interface OperationPerSecProps { servicename: string | undefined; - tagFilterItems: IQueryBuilderTagFilterItems[]; + tagFilterItems: TagFilterItem[]; topLevelOperations: string[]; } - -interface IOverviewQueries { - formulas: IMetricsBuilderFormula[]; - queryBuilder: IMetricsBuilderQuery[]; -} diff --git a/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx b/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx index 42cbb11265..8ae0033d4b 100644 --- a/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx @@ -1,5 +1,3 @@ -/* eslint-disable */ -// @ts-nocheck import { Col } from 'antd'; import FullView from 'container/GridGraphLayout/Graph/FullView/index.metricsBuilder'; import { @@ -14,6 +12,8 @@ import { import React, { useMemo, useState } from 'react'; import { useParams } from 'react-router-dom'; import { Widgets } from 'types/api/dashboard/getAll'; +import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData'; +import { EQueryType } from 'types/common/dashboard'; import { Card, GraphContainer, GraphTitle, Row } from '../styles'; import { Button } from './styles'; @@ -29,7 +29,7 @@ function DBCall({ getWidgetQueryBuilder }: DBCallProps): JSX.Element { const [selectedTimeStamp, setSelectedTimeStamp] = useState(0); const { queries } = useResourceAttribute(); - const tagFilterItems = useMemo( + const tagFilterItems: TagFilterItem[] = useMemo( () => handleNonInQueryRange(resourceAttributesToTagFilterItems(queries)) || [], [queries], @@ -48,29 +48,27 @@ function DBCall({ getWidgetQueryBuilder }: DBCallProps): JSX.Element { const databaseCallsRPSWidget = useMemo( () => getWidgetQueryBuilder({ - queryType: 1, - promQL: [], - // TODO: change it later to actual builder - metricsBuilder: databaseCallsRPS({ + queryType: EQueryType.QUERY_BUILDER, + promql: [], + builder: databaseCallsRPS({ servicename, legend, tagFilterItems, }), - clickHouse: [], + clickhouse_sql: [], }), [getWidgetQueryBuilder, servicename, tagFilterItems], ); const databaseCallsAverageDurationWidget = useMemo( () => getWidgetQueryBuilder({ - queryType: 1, - promQL: [], - // TODO: change it later to actual builder - metricsBuilder: databaseCallsAvgDuration({ + queryType: EQueryType.QUERY_BUILDER, + promql: [], + builder: databaseCallsAvgDuration({ servicename, tagFilterItems, }), - clickHouse: [], + clickhouse_sql: [], }), [getWidgetQueryBuilder, servicename, tagFilterItems], ); diff --git a/frontend/src/container/MetricsApplication/Tabs/External.tsx b/frontend/src/container/MetricsApplication/Tabs/External.tsx index a429aa2983..3a234143ef 100644 --- a/frontend/src/container/MetricsApplication/Tabs/External.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/External.tsx @@ -1,5 +1,3 @@ -/* eslint-disable */ -// @ts-nocheck import { Col } from 'antd'; import FullView from 'container/GridGraphLayout/Graph/FullView/index.metricsBuilder'; import { @@ -16,6 +14,7 @@ import { import React, { useMemo, useState } from 'react'; import { useParams } from 'react-router-dom'; import { Widgets } from 'types/api/dashboard/getAll'; +import { EQueryType } from 'types/common/dashboard'; import { Card, GraphContainer, GraphTitle, Row } from '../styles'; import { legend } from './constant'; @@ -41,15 +40,14 @@ function External({ getWidgetQueryBuilder }: ExternalProps): JSX.Element { const externalCallErrorWidget = useMemo( () => getWidgetQueryBuilder({ - queryType: 1, - promQL: [], - // TODO: change it later to actual builder - metricsBuilder: externalCallErrorPercent({ + queryType: EQueryType.QUERY_BUILDER, + promql: [], + builder: externalCallErrorPercent({ servicename, legend: legend.address, tagFilterItems, }), - clickHouse: [], + clickhouse_sql: [], }), [getWidgetQueryBuilder, servicename, tagFilterItems], ); @@ -62,14 +60,13 @@ function External({ getWidgetQueryBuilder }: ExternalProps): JSX.Element { const externalCallDurationWidget = useMemo( () => getWidgetQueryBuilder({ - queryType: 1, - promQL: [], - // TODO: change it later to actual builder - metricsBuilder: externalCallDuration({ + queryType: EQueryType.QUERY_BUILDER, + promql: [], + builder: externalCallDuration({ servicename, tagFilterItems, }), - clickHouse: [], + clickhouse_sql: [], }), [getWidgetQueryBuilder, servicename, tagFilterItems], ); @@ -77,15 +74,14 @@ function External({ getWidgetQueryBuilder }: ExternalProps): JSX.Element { const externalCallRPSWidget = useMemo( () => getWidgetQueryBuilder({ - queryType: 1, - promQL: [], - // TODO: change it later to actual builder - metricsBuilder: externalCallRpsByAddress({ + queryType: EQueryType.QUERY_BUILDER, + promql: [], + builder: externalCallRpsByAddress({ servicename, legend: legend.address, tagFilterItems, }), - clickHouse: [], + clickhouse_sql: [], }), [getWidgetQueryBuilder, servicename, tagFilterItems], ); @@ -93,15 +89,14 @@ function External({ getWidgetQueryBuilder }: ExternalProps): JSX.Element { const externalCallDurationAddressWidget = useMemo( () => getWidgetQueryBuilder({ - queryType: 1, - promQL: [], - // TODO: change it later to actual builder - metricsBuilder: externalCallDurationByAddress({ + queryType: EQueryType.QUERY_BUILDER, + promql: [], + builder: externalCallDurationByAddress({ servicename, legend: legend.address, tagFilterItems, }), - clickHouse: [], + clickhouse_sql: [], }), [getWidgetQueryBuilder, servicename, tagFilterItems], ); diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx index 710c9c960e..eedabe262b 100644 --- a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx @@ -1,5 +1,3 @@ -/* eslint-disable */ -// @ts-nocheck import { ActiveElement, Chart, ChartData, ChartEvent } from 'chart.js'; import Graph from 'components/Graph'; import { QueryParams } from 'constants/query'; @@ -21,6 +19,7 @@ import { useLocation, useParams } from 'react-router-dom'; import { UpdateTimeInterval } from 'store/actions'; import { AppState } from 'store/reducers'; import { Widgets } from 'types/api/dashboard/getAll'; +import { EQueryType } from 'types/common/dashboard'; import MetricReducer from 'types/reducer/metrics'; import { @@ -84,15 +83,14 @@ function Application({ getWidgetQueryBuilder }: DashboardProps): JSX.Element { const operationPerSecWidget = useMemo( () => getWidgetQueryBuilder({ - queryType: 1, - promQL: [], - // TODO: change it later to actual builder - metricsBuilder: operationPerSec({ + queryType: EQueryType.QUERY_BUILDER, + promql: [], + builder: operationPerSec({ servicename, tagFilterItems, topLevelOperations, }), - clickHouse: [], + clickhouse_sql: [], }), [getWidgetQueryBuilder, servicename, topLevelOperations, tagFilterItems], ); @@ -100,15 +98,14 @@ function Application({ getWidgetQueryBuilder }: DashboardProps): JSX.Element { const errorPercentageWidget = useMemo( () => getWidgetQueryBuilder({ - queryType: 1, - promQL: [], - // TODO: change it later to actual builder - metricsBuilder: errorPercentage({ + queryType: EQueryType.QUERY_BUILDER, + promql: [], + builder: errorPercentage({ servicename, tagFilterItems, topLevelOperations, }), - clickHouse: [], + clickhouse_sql: [], }), [servicename, topLevelOperations, tagFilterItems, getWidgetQueryBuilder], ); diff --git a/frontend/src/container/MetricsApplication/Tabs/util.ts b/frontend/src/container/MetricsApplication/Tabs/util.ts index 185df530e1..313ac6e082 100644 --- a/frontend/src/container/MetricsApplication/Tabs/util.ts +++ b/frontend/src/container/MetricsApplication/Tabs/util.ts @@ -4,7 +4,7 @@ import ROUTES from 'constants/routes'; import { routeConfig } from 'container/SideNav/config'; import { getQueryString } from 'container/SideNav/helper'; import history from 'lib/history'; -import { IQueryBuilderTagFilterItems } from 'types/api/dashboard/getAll'; +import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData'; import { Tags } from 'types/reducer/trace'; export const dbSystemTags: Tags[] = [ @@ -90,9 +90,7 @@ export function onGraphClickHandler( }; } -export const handleNonInQueryRange = ( - tags: IQueryBuilderTagFilterItems[], -): IQueryBuilderTagFilterItems[] => +export const handleNonInQueryRange = (tags: TagFilterItem[]): TagFilterItem[] => tags.map((tag) => { if (tag.op === 'Not IN') { return { diff --git a/frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/clickHouse/index.tsx b/frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/clickHouse/index.tsx index 95b0fa8bfd..175aae717e 100644 --- a/frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/clickHouse/index.tsx +++ b/frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/clickHouse/index.tsx @@ -13,7 +13,7 @@ import { IClickHouseQueryHandleChange } from './types'; interface IClickHouseQueryContainerProps { queryData: Query; updateQueryData: (args: IHandleUpdatedQuery) => void; - clickHouseQueries: Query['clickHouse']; + clickHouseQueries: Query['clickhouse_sql']; } function ClickHouseQueryContainer({ queryData, diff --git a/frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/queryBuilder/query.tsx b/frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/queryBuilder/query.tsx index c5500bbe8d..e17cef48b1 100644 --- a/frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/queryBuilder/query.tsx +++ b/frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/queryBuilder/query.tsx @@ -69,8 +69,6 @@ function MetricsBuilder({ }); }, [metricName]); - // TODO: rewrite to Form component from antd - return ( { - const queries = query[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME].queryBuilder; - const formulas = - query[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME][ - WIDGET_QUERY_BUILDER_FORMULA_KEY_NAME - ]; + const queries = query[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME].queryData; + const formulas = query[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME].queryFormulas; return queries.length + formulas.length < QUERY_AND_FORMULA_LIMIT; }; diff --git a/frontend/src/container/NewWidget/LeftContainer/QuerySection/constants.ts b/frontend/src/container/NewWidget/LeftContainer/QuerySection/constants.ts index a16ed33a74..efa8eed00e 100644 --- a/frontend/src/container/NewWidget/LeftContainer/QuerySection/constants.ts +++ b/frontend/src/container/NewWidget/LeftContainer/QuerySection/constants.ts @@ -1,21 +1,10 @@ -/* eslint-disable */ -// @ts-ignore -// @ts-nocheck - import { EQueryType } from 'types/common/dashboard'; -import { EQueryTypeToQueryKeyMapping } from './types'; +export const WIDGET_PROMQL_QUERY_KEY_NAME = EQueryType.PROM; -export const WIDGET_PROMQL_QUERY_KEY_NAME: EQueryTypeToQueryKeyMapping.PROM = - EQueryTypeToQueryKeyMapping[EQueryType[EQueryType.PROM]]; +export const WIDGET_CLICKHOUSE_QUERY_KEY_NAME = EQueryType.CLICKHOUSE; -export const WIDGET_CLICKHOUSE_QUERY_KEY_NAME: EQueryTypeToQueryKeyMapping.CLICKHOUSE = EQueryTypeToQueryKeyMapping[ - EQueryType[EQueryType.CLICKHOUSE] -] as string; - -export const WIDGET_QUERY_BUILDER_QUERY_KEY_NAME: EQueryTypeToQueryKeyMapping.QUERY_BUILDER = EQueryTypeToQueryKeyMapping[ - EQueryType[EQueryType.QUERY_BUILDER] -] as string; +export const WIDGET_QUERY_BUILDER_QUERY_KEY_NAME = EQueryType.QUERY_BUILDER; type TFormulas = 'formulas'; export const WIDGET_QUERY_BUILDER_FORMULA_KEY_NAME: TFormulas = 'formulas'; diff --git a/frontend/src/container/NewWidget/LeftContainer/QuerySection/index.tsx b/frontend/src/container/NewWidget/LeftContainer/QuerySection/index.tsx index e72f7614f6..5d08eea848 100644 --- a/frontend/src/container/NewWidget/LeftContainer/QuerySection/index.tsx +++ b/frontend/src/container/NewWidget/LeftContainer/QuerySection/index.tsx @@ -30,7 +30,6 @@ import ClickHouseQueryContainer from './QueryBuilder/clickHouse'; import PromQLQueryContainer from './QueryBuilder/promQL'; import TabHeader from './TabHeader'; import { IHandleUpdatedQuery } from './types'; -import { getQueryKey } from './utils/getQueryKey'; import { showUnstagedStashConfirmBox } from './utils/userSettings'; function QuerySection({ @@ -76,15 +75,10 @@ function QuerySection({ queryA: Query, queryB: Query, queryCategory: EQueryType, - ): boolean => { - const keyOfConcern = getQueryKey(queryCategory); - return !isEqual(queryA[keyOfConcern], queryB[keyOfConcern]); - }; + ): boolean => !isEqual(queryA[queryCategory], queryB[queryCategory]); useEffect(() => { - handleUnstagedChanges( - queryDiff(query, localQueryChanges, parseInt(`${queryCategory}`, 10)), - ); + handleUnstagedChanges(queryDiff(query, localQueryChanges, queryCategory)); }, [handleUnstagedChanges, localQueryChanges, query, queryCategory]); const regenRctKeys = (): void => { @@ -111,11 +105,7 @@ function QuerySection({ const handleQueryCategoryChange = (qCategory: string): void => { // If true, then it means that the user has made some changes and haven't staged them - const unstagedChanges = queryDiff( - query, - localQueryChanges, - parseInt(`${queryCategory}`, 10), - ); + const unstagedChanges = queryDiff(query, localQueryChanges, queryCategory); if (unstagedChanges && showUnstagedStashConfirmBox()) { // eslint-disable-next-line no-alert @@ -125,10 +115,10 @@ function QuerySection({ return; } - setQueryCategory(parseInt(`${qCategory}`, 10)); + setQueryCategory(qCategory as EQueryType); const newLocalQuery = { ...cloneDeep(query), - queryType: parseInt(`${qCategory}`, 10), + queryType: qCategory as EQueryType, }; setLocalQueryChanges(newLocalQuery); regenRctKeys(); @@ -147,7 +137,7 @@ function QuerySection({ const items = [ { - key: EQueryType.QUERY_BUILDER.toString(), + key: EQueryType.QUERY_BUILDER, label: 'Query Builder', tab: ( , }, { - key: EQueryType.CLICKHOUSE.toString(), + key: EQueryType.CLICKHOUSE, label: 'ClickHouse Query', tab: ( diff --git a/frontend/src/container/NewWidget/LeftContainer/QuerySection/types.ts b/frontend/src/container/NewWidget/LeftContainer/QuerySection/types.ts index 35eb34d8cf..ca39d0db09 100644 --- a/frontend/src/container/NewWidget/LeftContainer/QuerySection/types.ts +++ b/frontend/src/container/NewWidget/LeftContainer/QuerySection/types.ts @@ -1,19 +1,5 @@ import { Query } from 'types/api/dashboard/getAll'; -export type TQueryCategories = 'query_builder' | 'clickhouse_query' | 'promql'; - -export enum EQueryCategories { - query_builder = 0, - clickhouse_query, - promql, -} - -export enum EQueryTypeToQueryKeyMapping { - QUERY_BUILDER = 'builder', - CLICKHOUSE = 'clickHouse', - PROM = 'promQL', -} - export interface IHandleUpdatedQuery { updatedQuery: Query; } diff --git a/frontend/src/container/NewWidget/LeftContainer/QuerySection/utils/getQueryKey.ts b/frontend/src/container/NewWidget/LeftContainer/QuerySection/utils/getQueryKey.ts deleted file mode 100644 index 88b6eeb27a..0000000000 --- a/frontend/src/container/NewWidget/LeftContainer/QuerySection/utils/getQueryKey.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { EQueryType } from 'types/common/dashboard'; - -import { EQueryTypeToQueryKeyMapping } from '../types'; - -export const getQueryKey = ( - queryCategory: EQueryType, -): EQueryTypeToQueryKeyMapping => - EQueryTypeToQueryKeyMapping[ - EQueryType[queryCategory] as keyof typeof EQueryTypeToQueryKeyMapping - ]; diff --git a/frontend/src/container/QueryBuilder/QueryBuilder.tsx b/frontend/src/container/QueryBuilder/QueryBuilder.tsx index 712ac23ec5..e5e1fc35bc 100644 --- a/frontend/src/container/QueryBuilder/QueryBuilder.tsx +++ b/frontend/src/container/QueryBuilder/QueryBuilder.tsx @@ -19,26 +19,27 @@ export const QueryBuilder = memo(function QueryBuilder({ const { queryBuilderData, setupInitialDataSource, - resetQueryBuilderData, + resetQueryBuilderInfo, addNewQuery, addNewFormula, + handleSetPanelType, } = useQueryBuilder(); useEffect(() => { if (config && config.queryVariant === 'static') { setupInitialDataSource(config.initialDataSource); } - - return (): void => { - setupInitialDataSource(null); - }; }, [config, setupInitialDataSource]); + useEffect(() => { + handleSetPanelType(panelType); + }, [handleSetPanelType, panelType]); + useEffect( () => (): void => { - resetQueryBuilderData(); + resetQueryBuilderInfo(); }, - [resetQueryBuilderData], + [resetQueryBuilderInfo], ); const isDisabledQueryButton = useMemo( @@ -51,6 +52,13 @@ export const QueryBuilder = memo(function QueryBuilder({ [queryBuilderData], ); + const isAvailableToDisableQuery = useMemo( + () => + queryBuilderData.queryData.length > 1 || + queryBuilderData.queryFormulas.length > 0, + [queryBuilderData], + ); + return ( @@ -59,10 +67,9 @@ export const QueryBuilder = memo(function QueryBuilder({ 1} + isAvailableToDisable={isAvailableToDisableQuery} queryVariant={config?.queryVariant || 'dropdown'} query={query} - panelType={panelType} /> ))} diff --git a/frontend/src/container/QueryBuilder/components/FilterLabel/FilterLabel.styled.ts b/frontend/src/container/QueryBuilder/components/FilterLabel/FilterLabel.styled.ts index 85936ce595..4e24a2f778 100644 --- a/frontend/src/container/QueryBuilder/components/FilterLabel/FilterLabel.styled.ts +++ b/frontend/src/container/QueryBuilder/components/FilterLabel/FilterLabel.styled.ts @@ -3,7 +3,7 @@ import styled from 'styled-components'; export const StyledLabel = styled.div` padding: 0 0.6875rem; min-height: 2rem; - width: 100%; + min-width: 5.625rem; display: inline-flex; white-space: nowrap; align-items: center; diff --git a/frontend/src/container/QueryBuilder/components/ListMarker/ListMarker.styled.ts b/frontend/src/container/QueryBuilder/components/ListMarker/ListMarker.styled.ts index 5c1e5cc396..76a70c25c7 100644 --- a/frontend/src/container/QueryBuilder/components/ListMarker/ListMarker.styled.ts +++ b/frontend/src/container/QueryBuilder/components/ListMarker/ListMarker.styled.ts @@ -7,7 +7,6 @@ export const StyledButton = styled(Button)<{ $isAvailableToDisable: boolean }>` padding: ${(props): string => props.$isAvailableToDisable ? '0.43rem' : '0.43rem 0.68rem'}; border-radius: 0.375rem; - margin-right: 0.5rem; pointer-events: ${(props): string => props.$isAvailableToDisable ? 'default' : 'none'}; `; diff --git a/frontend/src/container/QueryBuilder/components/Query/Query.interfaces.ts b/frontend/src/container/QueryBuilder/components/Query/Query.interfaces.ts index c67ae42465..414e61678b 100644 --- a/frontend/src/container/QueryBuilder/components/Query/Query.interfaces.ts +++ b/frontend/src/container/QueryBuilder/components/Query/Query.interfaces.ts @@ -1,4 +1,3 @@ -import { ITEMS } from 'container/NewDashboard/ComponentsSlider/menuItems'; import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData'; export type QueryProps = { @@ -6,5 +5,4 @@ export type QueryProps = { isAvailableToDisable: boolean; query: IBuilderQuery; queryVariant: 'static' | 'dropdown'; - panelType: ITEMS; }; diff --git a/frontend/src/container/QueryBuilder/components/Query/Query.tsx b/frontend/src/container/QueryBuilder/components/Query/Query.tsx index 7af2fc8db4..ee2f587026 100644 --- a/frontend/src/container/QueryBuilder/components/Query/Query.tsx +++ b/frontend/src/container/QueryBuilder/components/Query/Query.tsx @@ -20,6 +20,7 @@ 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/queryBuilder/useQueryBuilder'; import { useQueryOperations } from 'hooks/queryBuilder/useQueryOperations'; // ** Hooks import React, { ChangeEvent, memo, ReactNode, useCallback } from 'react'; @@ -34,8 +35,8 @@ export const Query = memo(function Query({ isAvailableToDisable, queryVariant, query, - panelType, }: QueryProps): JSX.Element { + const { panelType } = useQueryBuilder(); const { operators, isMetricsDataSource, @@ -45,7 +46,7 @@ export const Query = memo(function Query({ handleChangeQueryData, handleChangeOperator, handleDeleteQuery, - } = useQueryOperations({ index, query, panelType }); + } = useQueryOperations({ index, query }); const handleChangeAggregateEvery = useCallback( (value: IBuilderQuery['stepInterval']) => { @@ -211,7 +212,7 @@ export const Query = memo(function Query({ return ( - + + + {queryVariant === 'dropdown' ? ( ) : ( )} - + {isMetricsDataSource && ( diff --git a/frontend/src/container/QueryBuilder/filters/AggregatorFilter/AggregatorFilter.tsx b/frontend/src/container/QueryBuilder/filters/AggregatorFilter/AggregatorFilter.tsx index cd87d44816..1dff437914 100644 --- a/frontend/src/container/QueryBuilder/filters/AggregatorFilter/AggregatorFilter.tsx +++ b/frontend/src/container/QueryBuilder/filters/AggregatorFilter/AggregatorFilter.tsx @@ -3,11 +3,12 @@ import { AutoComplete, Spin } from 'antd'; // ** Api import { getAggregateAttribute } from 'api/queryBuilder/getAggregateAttribute'; import { initialAggregateAttribute } from 'constants/queryBuilder'; +import { getFilterObjectValue } from 'lib/newQueryBuilder/getFilterObjectValue'; 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 { ExtendedSelectOption } from 'types/common/select'; import { transformToUpperCase } from 'utils/transformToUpperCase'; import { selectStyle } from '../QueryBuilderSearch/config'; @@ -36,23 +37,35 @@ export const AggregatorFilter = memo(function AggregatorFilter({ { enabled: !!query.aggregateOperator && !!query.dataSource }, ); - const handleSearchAttribute = (searchText: string): void => - setSearchText(searchText); + const handleSearchAttribute = (searchText: string): void => { + const { key } = getFilterObjectValue(searchText); + setSearchText(key); + }; - const optionsData: SelectOption[] = + const optionsData: ExtendedSelectOption[] = data?.payload?.attributeKeys?.map((item) => ({ label: transformStringWithPrefix({ str: item.key, prefix: item.type || '', condition: !item.isColumn, }), - value: item.key, + value: transformStringWithPrefix({ + str: item.key, + prefix: item.type || '', + condition: !item.isColumn, + }), + key: transformStringWithPrefix({ + str: item.key, + prefix: item.type || '', + condition: !item.isColumn, + }), })) || []; const handleChangeAttribute = (value: string): void => { + const { key, isColumn } = getFilterObjectValue(value); const currentAttributeObj = data?.payload?.attributeKeys?.find( - (item) => item.key === value, - ) || { ...initialAggregateAttribute, key: value }; + (item) => item.key === key && isColumn === item.isColumn, + ) || { ...initialAggregateAttribute, key }; setSearchText(''); onChange(currentAttributeObj); diff --git a/frontend/src/container/QueryBuilder/filters/GroupByFilter/GroupByFilter.interfaces.ts b/frontend/src/container/QueryBuilder/filters/GroupByFilter/GroupByFilter.interfaces.ts index 2624c2d7a1..cea028d3ee 100644 --- a/frontend/src/container/QueryBuilder/filters/GroupByFilter/GroupByFilter.interfaces.ts +++ b/frontend/src/container/QueryBuilder/filters/GroupByFilter/GroupByFilter.interfaces.ts @@ -6,11 +6,3 @@ export type GroupByFilterProps = { onChange: (values: BaseAutocompleteData[]) => void; disabled: boolean; }; - -export type GroupByFilterValue = { - disabled: boolean | undefined; - key: string; - label: string; - title: string | undefined; - value: string; -}; diff --git a/frontend/src/container/QueryBuilder/filters/GroupByFilter/GroupByFilter.tsx b/frontend/src/container/QueryBuilder/filters/GroupByFilter/GroupByFilter.tsx index 6b39a83b18..e7d84aadc0 100644 --- a/frontend/src/container/QueryBuilder/filters/GroupByFilter/GroupByFilter.tsx +++ b/frontend/src/container/QueryBuilder/filters/GroupByFilter/GroupByFilter.tsx @@ -2,19 +2,17 @@ import { Select, Spin } from 'antd'; import { getAggregateKeys } from 'api/queryBuilder/getAttributeKeys'; // ** Constants import { QueryBuilderKeys } from 'constants/queryBuilder'; +import { getFilterObjectValue } from 'lib/newQueryBuilder/getFilterObjectValue'; // ** Components // ** Helpers import { transformStringWithPrefix } from 'lib/query/transformStringWithPrefix'; -import React, { memo, useState } from 'react'; +import React, { memo, useMemo, useState } from 'react'; import { useQuery } from 'react-query'; import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; -import { SelectOption } from 'types/common/select'; +import { ExtendedSelectOption } from 'types/common/select'; import { selectStyle } from '../QueryBuilderSearch/config'; -import { - GroupByFilterProps, - GroupByFilterValue, -} from './GroupByFilter.interfaces'; +import { GroupByFilterProps } from './GroupByFilter.interfaces'; export const GroupByFilter = memo(function GroupByFilter({ query, @@ -48,28 +46,44 @@ export const GroupByFilter = memo(function GroupByFilter({ setIsFocused(true); }; - const optionsData: SelectOption[] = - data?.payload?.attributeKeys?.map((item) => ({ - label: transformStringWithPrefix({ - str: item.key, - prefix: item.type || '', - condition: !item.isColumn, - }), - value: item.key, - })) || []; + const optionsData: ExtendedSelectOption[] = useMemo(() => { + if (data && data.payload && data.payload.attributeKeys) { + return data.payload.attributeKeys.map((item) => ({ + label: transformStringWithPrefix({ + str: item.key, + prefix: item.type || '', + condition: !item.isColumn, + }), + value: transformStringWithPrefix({ + str: item.key, + prefix: item.type || '', + condition: !item.isColumn, + }), + key: transformStringWithPrefix({ + str: item.key, + prefix: item.type || '', + condition: !item.isColumn, + }), + })); + } - const handleChange = (values: GroupByFilterValue[]): void => { + return []; + }, [data]); + + const handleChange = (values: ExtendedSelectOption[]): void => { const groupByValues: BaseAutocompleteData[] = values.map((item) => { const responseKeys = data?.payload?.attributeKeys || []; + const { key, isColumn } = getFilterObjectValue(item.value); + const existGroupResponse = responseKeys.find( - (group) => group.key === item.value, + (group) => group.key === key && group.isColumn === isColumn, ); if (existGroupResponse) { return existGroupResponse; } const existGroupQuery = query.groupBy.find( - (group) => group.key === item.value, + (group) => group.key === key && group.isColumn === isColumn, ); if (existGroupQuery) { @@ -78,7 +92,7 @@ export const GroupByFilter = memo(function GroupByFilter({ return { isColumn: null, - key: item.value, + key, dataType: null, type: null, }; @@ -88,16 +102,22 @@ export const GroupByFilter = memo(function GroupByFilter({ onChange(groupByValues); }; - const values: GroupByFilterValue[] = query.groupBy.map((item) => ({ + const values: ExtendedSelectOption[] = query.groupBy.map((item) => ({ label: transformStringWithPrefix({ str: item.key, prefix: item.type || '', condition: !item.isColumn, }), - key: item.key, - value: item.key, - disabled: undefined, - title: undefined, + key: transformStringWithPrefix({ + str: item.key, + prefix: item.type || '', + condition: !item.isColumn, + }), + value: transformStringWithPrefix({ + str: item.key, + prefix: item.type || '', + condition: !item.isColumn, + }), })); return ( @@ -110,8 +130,8 @@ export const GroupByFilter = memo(function GroupByFilter({ showArrow={false} onBlur={onBlur} onFocus={onFocus} - filterOption={false} options={optionsData} + filterOption={false} labelInValue value={values} notFoundContent={isFetching ? : null} diff --git a/frontend/src/container/QueryBuilder/filters/HavingFilter/HavingFilter.tsx b/frontend/src/container/QueryBuilder/filters/HavingFilter/HavingFilter.tsx index d39877a776..ebc1d7b826 100644 --- a/frontend/src/container/QueryBuilder/filters/HavingFilter/HavingFilter.tsx +++ b/frontend/src/container/QueryBuilder/filters/HavingFilter/HavingFilter.tsx @@ -1,6 +1,7 @@ import { Select } from 'antd'; // ** Constants import { HAVING_OPERATORS, initialHavingValues } from 'constants/queryBuilder'; +import { HAVING_FILTER_REGEXP } from 'constants/regExp'; import { HavingFilterTag } from 'container/QueryBuilder/components'; import { HavingTagRenderProps } from 'container/QueryBuilder/components/HavingFilterTag/HavingFilterTag.interfaces'; // ** Hooks @@ -101,9 +102,7 @@ export function HavingFilter({ const values = getHavingObject(search).value.join(' '); if (values) { - const numRegexp = /^[-\d.,\s]+$/; - - return numRegexp.test(values); + return HAVING_FILTER_REGEXP.test(values); } return true; diff --git a/frontend/src/container/QueryBuilder/filters/QueryBuilderSearch/index.tsx b/frontend/src/container/QueryBuilder/filters/QueryBuilderSearch/index.tsx index c4354ad041..cc3b908752 100644 --- a/frontend/src/container/QueryBuilder/filters/QueryBuilderSearch/index.tsx +++ b/frontend/src/container/QueryBuilder/filters/QueryBuilderSearch/index.tsx @@ -75,10 +75,13 @@ function QueryBuilderSearch({ useEffect(() => { const initialTagFilters: TagFilter = { items: [], op: 'AND' }; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore initialTagFilters.items = tags.map((tag) => { const [tagKey, tagOperator, ...tagValue] = tag.split(' '); return { id: uuid().slice(0, 8), + // TODO: key should be fixed by Chintan Sudani key: tagKey, op: tagOperator, value: tagValue.map((i) => i.replace(',', '')), diff --git a/frontend/src/hooks/queryBuilder/useQueryOperations.ts b/frontend/src/hooks/queryBuilder/useQueryOperations.ts index 251c04c31e..bf3c3daa46 100644 --- a/frontend/src/hooks/queryBuilder/useQueryOperations.ts +++ b/frontend/src/hooks/queryBuilder/useQueryOperations.ts @@ -2,11 +2,9 @@ import { initialAggregateAttribute, initialQueryBuilderFormValues, mapOfFilters, - mapOfOperators, - PANEL_TYPES, } from 'constants/queryBuilder'; -import { ITEMS } from 'container/NewDashboard/ComponentsSlider/menuItems'; import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; +import { getOperatorsBySourceAndPanelType } from 'lib/newQueryBuilder/getOperatorsBySourceAndPanelType'; import { findDataTypeOfOperator } from 'lib/query/findDataTypeOfOperator'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; @@ -15,14 +13,14 @@ import { HandleChangeQueryData, UseQueryOperations, } from 'types/common/operations.types'; -import { DataSource, StringOperators } from 'types/common/queryBuilder'; +import { DataSource } from 'types/common/queryBuilder'; -export const useQueryOperations: UseQueryOperations = ({ - query, - index, - panelType, -}) => { - const { handleSetQueryData, removeEntityByIndex } = useQueryBuilder(); +export const useQueryOperations: UseQueryOperations = ({ query, index }) => { + const { + handleSetQueryData, + removeEntityByIndex, + panelType, + } = useQueryBuilder(); const [operators, setOperators] = useState([]); const [listOfAdditionalFilters, setListOfAdditionalFilters] = useState< string[] @@ -57,24 +55,6 @@ export const useQueryOperations: UseQueryOperations = ({ [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), @@ -96,7 +76,10 @@ export const useQueryOperations: UseQueryOperations = ({ const handleChangeDataSource = useCallback( (nextSource: DataSource): void => { - const newOperators = getNewOperators(nextSource, panelType); + const newOperators = getOperatorsBySourceAndPanelType({ + dataSource: nextSource, + panelType, + }); const entries = Object.entries(initialQueryBuilderFormValues).filter( ([key]) => key !== 'queryName' && key !== 'expression', @@ -114,7 +97,7 @@ export const useQueryOperations: UseQueryOperations = ({ setOperators(newOperators); handleSetQueryData(index, newQuery); }, - [index, query, panelType, handleSetQueryData, getNewOperators], + [index, query, panelType, handleSetQueryData], ); const handleDeleteQuery = useCallback(() => { @@ -139,11 +122,12 @@ export const useQueryOperations: UseQueryOperations = ({ ); useEffect(() => { - if (operators.length === 0) { - const initialOperators = getNewOperators(dataSource, panelType); - setOperators(initialOperators); - } - }, [operators, dataSource, panelType, getNewOperators]); + const initialOperators = getOperatorsBySourceAndPanelType({ + dataSource, + panelType, + }); + setOperators(initialOperators); + }, [dataSource, panelType]); useEffect(() => { const additionalFilters = getNewListOfAdditionalFilters(dataSource); diff --git a/frontend/src/hooks/useResourceAttribute/utils.ts b/frontend/src/hooks/useResourceAttribute/utils.ts index c76cf534e3..2be45582c0 100644 --- a/frontend/src/hooks/useResourceAttribute/utils.ts +++ b/frontend/src/hooks/useResourceAttribute/utils.ts @@ -11,7 +11,7 @@ import { } from 'hooks/useResourceAttribute/types'; import { decode } from 'js-base64'; import history from 'lib/history'; -import { IQueryBuilderTagFilterItems } from 'types/api/dashboard/getAll'; +import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData'; import { OperatorValues, Tags } from 'types/reducer/trace'; import { v4 as uuid } from 'uuid'; @@ -63,10 +63,10 @@ export const convertRawQueriesToTraceSelectedTags = ( /* Convert resource attributes to tagFilter items for queryBuilder */ export const resourceAttributesToTagFilterItems = ( queries: IResourceAttribute[], -): IQueryBuilderTagFilterItems[] => +): TagFilterItem[] => queries.map((res) => ({ id: `${res.id}`, - key: `${res.tagKey}`, + key: { key: res.tagKey, isColumn: false, type: null, dataType: null }, op: `${res.operator}`, value: `${res.tagValue}`.split(','), })); diff --git a/frontend/src/lib/newQueryBuilder/convertNewDataToOld.ts b/frontend/src/lib/newQueryBuilder/convertNewDataToOld.ts index 475d4de2ae..dcb103d6cb 100644 --- a/frontend/src/lib/newQueryBuilder/convertNewDataToOld.ts +++ b/frontend/src/lib/newQueryBuilder/convertNewDataToOld.ts @@ -17,7 +17,7 @@ export const convertNewDataToOld = ( QueryData['values'] >((acc, currentInfo) => { const renderValues: [number, string] = [ - currentInfo.timestamp, + currentInfo.timestamp / 1000, currentInfo.value, ]; diff --git a/frontend/src/lib/newQueryBuilder/getFilterObjectValue.ts b/frontend/src/lib/newQueryBuilder/getFilterObjectValue.ts new file mode 100644 index 0000000000..9926837cd8 --- /dev/null +++ b/frontend/src/lib/newQueryBuilder/getFilterObjectValue.ts @@ -0,0 +1,31 @@ +import { TYPE_ADDON_REGEXP } from 'constants/regExp'; +import { + AutocompleteType, + BaseAutocompleteData, +} from 'types/api/queryBuilder/queryAutocompleteResponse'; + +const isTypeExist = (str: string): boolean => { + const types: AutocompleteType[] = ['tag', 'resource']; + let isExist = false; + types.forEach((type) => { + if (str.includes(type)) { + isExist = true; + } + }); + + return isExist; +}; + +export const getFilterObjectValue = ( + value: string, +): Omit => { + const isExist = isTypeExist(value); + + if (!isExist) { + return { isColumn: true, key: value }; + } + + const splittedValue = value.split(TYPE_ADDON_REGEXP); + + return { isColumn: false, key: splittedValue[1] }; +}; diff --git a/frontend/src/lib/newQueryBuilder/getOperatorsBySourceAndPanelType.ts b/frontend/src/lib/newQueryBuilder/getOperatorsBySourceAndPanelType.ts new file mode 100644 index 0000000000..12c267ef92 --- /dev/null +++ b/frontend/src/lib/newQueryBuilder/getOperatorsBySourceAndPanelType.ts @@ -0,0 +1,24 @@ +import { mapOfOperators, PANEL_TYPES } from 'constants/queryBuilder'; +import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider'; +import { DataSource, StringOperators } from 'types/common/queryBuilder'; + +type GetQueryOperatorsParams = { + dataSource: DataSource; + panelType: GRAPH_TYPES; +}; + +// Modify this function if need special conditions for filtering of the operators +export const getOperatorsBySourceAndPanelType = ({ + dataSource, + panelType, +}: GetQueryOperatorsParams): string[] => { + let operatorsByDataSource = mapOfOperators[dataSource]; + + if (dataSource !== DataSource.METRICS && panelType !== PANEL_TYPES.LIST) { + operatorsByDataSource = operatorsByDataSource.filter( + (operator) => operator !== StringOperators.NOOP, + ); + } + + return operatorsByDataSource; +}; diff --git a/frontend/src/lib/newQueryBuilder/queryBuilderMappers/mapQueryDataFromApi.ts b/frontend/src/lib/newQueryBuilder/queryBuilderMappers/mapQueryDataFromApi.ts new file mode 100644 index 0000000000..b1e9521fd9 --- /dev/null +++ b/frontend/src/lib/newQueryBuilder/queryBuilderMappers/mapQueryDataFromApi.ts @@ -0,0 +1,20 @@ +import { initialQueryBuilderFormValues } from 'constants/queryBuilder'; +import { isQuery, QueryBuilderData } from 'types/common/queryBuilder'; +import { QueryDataResourse } from 'types/common/queryBuilderMappers.types'; + +export const mapQueryDataFromApi = ( + data: QueryDataResourse, +): QueryBuilderData => { + const queryData: QueryBuilderData['queryData'] = []; + const queryFormulas: QueryBuilderData['queryFormulas'] = []; + + Object.entries(data).forEach(([, value]) => { + if (isQuery(value)) { + queryData.push({ ...initialQueryBuilderFormValues, ...value }); + } else { + queryFormulas.push(value); + } + }); + + return { queryData, queryFormulas }; +}; diff --git a/frontend/src/lib/newQueryBuilder/queryBuilderMappers/mapQueryDataToApi.ts b/frontend/src/lib/newQueryBuilder/queryBuilderMappers/mapQueryDataToApi.ts index f6b4c80c49..17707df911 100644 --- a/frontend/src/lib/newQueryBuilder/queryBuilderMappers/mapQueryDataToApi.ts +++ b/frontend/src/lib/newQueryBuilder/queryBuilderMappers/mapQueryDataToApi.ts @@ -1,15 +1,9 @@ -import { - IBuilderFormula, - IBuilderQuery, -} from 'types/api/queryBuilder/queryBuilderData'; import { QueryBuilderData } from 'types/common/queryBuilder'; - -type MapQueryDataToApiResult = { - data: Record; - newLegendMap: Record; -}; -type MapQuery = Record; -type MapFormula = Record; +import { + MapFormula, + MapQuery, + MapQueryDataToApiResult, +} from 'types/common/queryBuilderMappers.types'; export const mapQueryDataToApi = ( data: QueryBuilderData, @@ -41,6 +35,8 @@ export const mapQueryDataToApi = ( }, }; + newLegendMap[formula.queryName] = formula.legend; + return newResult; }, {}, diff --git a/frontend/src/providers/QueryBuilder.tsx b/frontend/src/providers/QueryBuilder.tsx index a216bc91dd..8e8d429447 100644 --- a/frontend/src/providers/QueryBuilder.tsx +++ b/frontend/src/providers/QueryBuilder.tsx @@ -3,11 +3,13 @@ import { formulasNames, initialFormulaBuilderFormValues, initialQueryBuilderFormValues, - mapOfOperators, MAX_FORMULAS, MAX_QUERIES, + PANEL_TYPES, } from 'constants/queryBuilder'; +import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider'; import { createNewBuilderItemName } from 'lib/newQueryBuilder/createNewBuilderItemName'; +import { getOperatorsBySourceAndPanelType } from 'lib/newQueryBuilder/getOperatorsBySourceAndPanelType'; import React, { createContext, PropsWithChildren, @@ -16,7 +18,6 @@ import React, { useState, } from 'react'; // ** Types -// TODO: Rename Types on the Reusable type for any source import { IBuilderFormula, IBuilderQuery, @@ -30,9 +31,12 @@ import { export const QueryBuilderContext = createContext({ queryBuilderData: { queryData: [], queryFormulas: [] }, initialDataSource: null, + panelType: PANEL_TYPES.TIME_SERIES, resetQueryBuilderData: () => {}, + resetQueryBuilderInfo: () => {}, handleSetQueryData: () => {}, handleSetFormulaData: () => {}, + handleSetPanelType: () => {}, initQueryBuilderData: () => {}, setupInitialDataSource: () => {}, removeEntityByIndex: () => {}, @@ -48,25 +52,28 @@ const initialQueryBuilderData: QueryBuilderData = { export function QueryBuilderProvider({ children, }: PropsWithChildren): JSX.Element { - // TODO: this is temporary. It will be used when we have fixed dataSource and need create new query with this data source - // eslint-disable-next-line @typescript-eslint/no-unused-vars const [initialDataSource, setInitialDataSource] = useState( null, ); - // TODO: when initialDataSource will be setuped, on create button initial dataSource will from initialDataSource + const [panelType, setPanelType] = useState( + PANEL_TYPES.TIME_SERIES, + ); + const [queryBuilderData, setQueryBuilderData] = useState({ queryData: [], queryFormulas: [], }); - // ** Method for resetting query builder data - const resetQueryBuilderData = useCallback((): void => { + const resetQueryBuilderInfo = useCallback((): void => { + setInitialDataSource(null); + setPanelType(PANEL_TYPES.TIME_SERIES); + }, []); + + const resetQueryBuilderData = useCallback(() => { setQueryBuilderData(initialQueryBuilderData); }, []); - // ** Method for setuping query builder data - // ** Before setuping transform data from backend to frontend format const initQueryBuilderData = useCallback( (queryBuilderData: QueryBuilderData): void => { setQueryBuilderData(queryBuilderData); @@ -101,14 +108,17 @@ export function QueryBuilderProvider({ ...(initialDataSource ? { dataSource: initialDataSource, - aggregateOperator: mapOfOperators[initialDataSource][0], + aggregateOperator: getOperatorsBySourceAndPanelType({ + dataSource: initialDataSource, + panelType, + })[0], } : {}), }; return newQuery; }, - [initialDataSource], + [initialDataSource, panelType], ); const createNewFormula = useCallback((formulas: IBuilderFormula[]) => { @@ -184,7 +194,6 @@ export function QueryBuilderProvider({ [updateQueryBuilderData], ); const handleSetFormulaData = useCallback( - // eslint-disable-next-line @typescript-eslint/no-unused-vars (index: number, formulaData: IBuilderFormula): void => { setQueryBuilderData((prevState) => { const updatedFormulasBuilderData = updateFormulaBuilderData( @@ -202,13 +211,20 @@ export function QueryBuilderProvider({ [updateFormulaBuilderData], ); + const handleSetPanelType = useCallback((newPanelType: GRAPH_TYPES) => { + setPanelType(newPanelType); + }, []); + const contextValues: QueryBuilderContextType = useMemo( () => ({ queryBuilderData, initialDataSource, + panelType, resetQueryBuilderData, + resetQueryBuilderInfo, handleSetQueryData, handleSetFormulaData, + handleSetPanelType, initQueryBuilderData, setupInitialDataSource, removeEntityByIndex, @@ -218,9 +234,12 @@ export function QueryBuilderProvider({ [ queryBuilderData, initialDataSource, + panelType, resetQueryBuilderData, + resetQueryBuilderInfo, handleSetQueryData, handleSetFormulaData, + handleSetPanelType, initQueryBuilderData, setupInitialDataSource, removeEntityByIndex, diff --git a/frontend/src/store/actions/dashboard/getDashboard.ts b/frontend/src/store/actions/dashboard/getDashboard.ts index f1e1c14df1..9005b1beb0 100644 --- a/frontend/src/store/actions/dashboard/getDashboard.ts +++ b/frontend/src/store/actions/dashboard/getDashboard.ts @@ -60,13 +60,13 @@ export const GetDashboard = ({ }, query: { queryType: EQueryType.QUERY_BUILDER, - promQL: [ + promql: [ { name: GetQueryName([]) as string, ...PromQLQueryTemplate, }, ], - clickHouse: [ + clickhouse_sql: [ { name: GetQueryName([]) as string, ...ClickHouseQueryTemplate, diff --git a/frontend/src/store/actions/dashboard/getQueryResults.ts b/frontend/src/store/actions/dashboard/getQueryResults.ts index 54299227a8..176b416553 100644 --- a/frontend/src/store/actions/dashboard/getQueryResults.ts +++ b/frontend/src/store/actions/dashboard/getQueryResults.ts @@ -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 { EQueryTypeToQueryKeyMapping } from 'container/NewWidget/LeftContainer/QuerySection/types'; import { timePreferenceType } from 'container/NewWidget/RightContainer/timeItems'; import { Time } from 'container/TopNav/DateTimeSelection/config'; import GetMaxMinTime from 'lib/getMaxMinTime'; @@ -38,24 +37,21 @@ export async function GetMetricQueryRange({ globalSelectedInterval: Time; variables?: Record; }): Promise | ErrorResponse> { - const { queryType } = query; - const queryKey: Record = - EQueryTypeToQueryKeyMapping[EQueryType[query.queryType]]; - const queryData = query[queryKey]; + const queryData = query[query.queryType]; let legendMap: Record = {}; const QueryPayload = { compositeQuery: { - queryType: queryKey, + queryType: query.queryType, panelType: graphType, }, }; - switch (queryType as EQueryType) { + switch (query.queryType) { case EQueryType.QUERY_BUILDER: { - const { queryData, queryFormulas } = query.builder; + const { queryData: data, queryFormulas } = query.builder; const builderQueries = mapQueryDataToApi({ - queryData, + queryData: data, queryFormulas, }); legendMap = builderQueries.newLegendMap; diff --git a/frontend/src/types/api/alerts/alertTypes.ts b/frontend/src/types/api/alerts/alertTypes.ts index b8ec9f38ef..47e43b377f 100644 --- a/frontend/src/types/api/alerts/alertTypes.ts +++ b/frontend/src/types/api/alerts/alertTypes.ts @@ -1,6 +1,5 @@ // this list must exactly match with the backend export enum AlertTypes { - NONE = 'NONE', METRICS_BASED_ALERT = 'METRIC_BASED_ALERT', LOGS_BASED_ALERT = 'LOGS_BASED_ALERT', TRACES_BASED_ALERT = 'TRACES_BASED_ALERT', diff --git a/frontend/src/types/api/alerts/compositeQuery.ts b/frontend/src/types/api/alerts/compositeQuery.ts index 864e4aa163..4e096d06a4 100644 --- a/frontend/src/types/api/alerts/compositeQuery.ts +++ b/frontend/src/types/api/alerts/compositeQuery.ts @@ -1,3 +1,4 @@ +import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider'; import { IClickHouseQuery, IMetricsBuilderFormula, @@ -6,12 +7,14 @@ import { IQueryBuilderTagFilters, } from 'types/api/dashboard/getAll'; import { EAggregateOperator, EQueryType } from 'types/common/dashboard'; +import { QueryDataResourse } from 'types/common/queryBuilderMappers.types'; export interface ICompositeMetricQuery { - builderQueries: IBuilderQueries; + builderQueries: QueryDataResourse; promQueries: IPromQueries; chQueries: IChQueries; queryType: EQueryType; + panelType: GRAPH_TYPES; } export interface IChQueries { diff --git a/frontend/src/types/api/alerts/def.ts b/frontend/src/types/api/alerts/def.ts index 65b3e64af4..8d5440e7d9 100644 --- a/frontend/src/types/api/alerts/def.ts +++ b/frontend/src/types/api/alerts/def.ts @@ -24,7 +24,7 @@ export interface AlertDef { } export interface RuleCondition { - compositeMetricQuery: ICompositeMetricQuery; + compositeQuery: ICompositeMetricQuery; op?: string | undefined; target?: number | undefined; matchType?: string | undefined; diff --git a/frontend/src/types/api/alerts/queryType.ts b/frontend/src/types/api/alerts/queryType.ts deleted file mode 100644 index 277d6f0703..0000000000 --- a/frontend/src/types/api/alerts/queryType.ts +++ /dev/null @@ -1,17 +0,0 @@ -export type QueryType = 1 | 2 | 3; - -export const QUERY_BUILDER: QueryType = 1; -export const PROMQL: QueryType = 3; - -export const resolveQueryCategoryName = (s: number): string => { - switch (s) { - case 1: - return 'Query Builder'; - case 2: - return 'Clickhouse Query'; - case 3: - return 'PromQL'; - default: - return ''; - } -}; diff --git a/frontend/src/types/api/dashboard/getAll.ts b/frontend/src/types/api/dashboard/getAll.ts index 72421b2c52..a38093d937 100644 --- a/frontend/src/types/api/dashboard/getAll.ts +++ b/frontend/src/types/api/dashboard/getAll.ts @@ -92,9 +92,9 @@ export interface PromQLWidgets extends IBaseWidget { } export interface Query { queryType: EQueryType; - promQL: IPromQLQuery[]; + promql: IPromQLQuery[]; builder: QueryBuilderData; - clickHouse: IClickHouseQuery[]; + clickhouse_sql: IClickHouseQuery[]; } export interface IMetricsBuilderFormula { diff --git a/frontend/src/types/api/dashboard/shared.ts b/frontend/src/types/api/dashboard/shared.ts deleted file mode 100644 index 31778e79bc..0000000000 --- a/frontend/src/types/api/dashboard/shared.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum EQueryTypeToQueryKeyMapping { - QUERY_BUILDER = 'metricsBuilder', - CLICKHOUSE = 'clickHouse', - PROM = 'promQL', -} diff --git a/frontend/src/types/api/queryBuilder/queryAutocompleteResponse.ts b/frontend/src/types/api/queryBuilder/queryAutocompleteResponse.ts index 72b2c3a6f5..fc34b030ff 100644 --- a/frontend/src/types/api/queryBuilder/queryAutocompleteResponse.ts +++ b/frontend/src/types/api/queryBuilder/queryAutocompleteResponse.ts @@ -2,11 +2,13 @@ export type LocalDataType = 'number' | 'string' | 'bool'; export type DataType = 'int64' | 'float64' | 'string' | 'bool'; +export type AutocompleteType = 'tag' | 'resource'; + export interface BaseAutocompleteData { dataType: DataType | null; isColumn: boolean | null; key: string; - type: 'tag' | 'resource' | null; + type: AutocompleteType | null; } export interface IQueryAutocompleteResponse { diff --git a/frontend/src/types/api/queryBuilder/queryBuilderData.ts b/frontend/src/types/api/queryBuilder/queryBuilderData.ts index 130bdd7399..d0fb929a13 100644 --- a/frontend/src/types/api/queryBuilder/queryBuilderData.ts +++ b/frontend/src/types/api/queryBuilder/queryBuilderData.ts @@ -13,14 +13,13 @@ export interface IBuilderFormula { export interface TagFilterItem { id: string; - key: string; - // TODO: type it in the future + key?: BaseAutocompleteData; op: string; value: string[]; } export interface TagFilter { - items: TagFilterItem[] | []; + items: TagFilterItem[]; // TODO: type it in the future op: string; } diff --git a/frontend/src/types/common/dashboard.ts b/frontend/src/types/common/dashboard.ts index 39312f2ab7..e348c744b2 100644 --- a/frontend/src/types/common/dashboard.ts +++ b/frontend/src/types/common/dashboard.ts @@ -1,7 +1,7 @@ export enum EQueryType { - QUERY_BUILDER = 1, - CLICKHOUSE, - PROM, + QUERY_BUILDER = 'builder', + CLICKHOUSE = 'clickhouse_sql', + PROM = 'promql', } export enum EAggregateOperator { diff --git a/frontend/src/types/common/operations.types.ts b/frontend/src/types/common/operations.types.ts index daa8344f4f..b3807df083 100644 --- a/frontend/src/types/common/operations.types.ts +++ b/frontend/src/types/common/operations.types.ts @@ -3,10 +3,7 @@ import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteRe import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData'; import { DataSource } from 'types/common/queryBuilder'; -type UseQueryOperationsParams = Pick< - QueryProps, - 'index' | 'panelType' | 'query' ->; +type UseQueryOperationsParams = Pick; export type HandleChangeQueryData = < Key extends keyof IBuilderQuery, diff --git a/frontend/src/types/common/queryBuilder.ts b/frontend/src/types/common/queryBuilder.ts index dbf2154876..127a78c746 100644 --- a/frontend/src/types/common/queryBuilder.ts +++ b/frontend/src/types/common/queryBuilder.ts @@ -1,3 +1,4 @@ +import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider'; import { IBuilderFormula, IBuilderQuery, @@ -45,7 +46,6 @@ export enum NumberOperators { HIST_QUANTILE_99 = 'hist_quantile_99', } -// TODO: add boolean operators from backend export enum BoolOperators { NOOP = 'noop', COUNT = 'count', @@ -147,13 +147,20 @@ export type QueryBuilderData = { queryFormulas: IBuilderFormula[]; }; -// ** TODO: temporary types for context, fix it during development +export const isQuery = ( + query: IBuilderFormula | IBuilderQuery, +): query is IBuilderQuery => + 'dataSource' in query && 'aggregateOperator' in query; + export type QueryBuilderContextType = { queryBuilderData: QueryBuilderData; initialDataSource: DataSource | null; + panelType: GRAPH_TYPES; resetQueryBuilderData: () => void; + resetQueryBuilderInfo: () => void; handleSetQueryData: (index: number, queryData: IBuilderQuery) => void; handleSetFormulaData: (index: number, formulaData: IBuilderFormula) => void; + handleSetPanelType: (newPanelType: GRAPH_TYPES) => void; initQueryBuilderData: (queryBuilderData: QueryBuilderData) => void; setupInitialDataSource: (newInitialDataSource: DataSource | null) => void; removeEntityByIndex: (type: keyof QueryBuilderData, index: number) => void; diff --git a/frontend/src/types/common/queryBuilderMappers.types.ts b/frontend/src/types/common/queryBuilderMappers.types.ts new file mode 100644 index 0000000000..9203b6dc9d --- /dev/null +++ b/frontend/src/types/common/queryBuilderMappers.types.ts @@ -0,0 +1,14 @@ +import { + IBuilderFormula, + IBuilderQuery, +} from 'types/api/queryBuilder/queryBuilderData'; + +export type MapQuery = Record; +export type MapFormula = Record; + +export type QueryDataResourse = Record; + +export type MapQueryDataToApiResult = { + data: QueryDataResourse; + newLegendMap: Record; +}; diff --git a/frontend/src/types/common/select.ts b/frontend/src/types/common/select.ts index 0bc6b8c47b..6743e5bf2a 100644 --- a/frontend/src/types/common/select.ts +++ b/frontend/src/types/common/select.ts @@ -2,3 +2,11 @@ export type SelectOption = { value: Value; label: Label; }; + +export type ExtendedSelectOption = { + disabled?: boolean; + key: string; + label: string; + title?: string; + value: string; +};