feat: Metrics Builder Enhancements and Code Cleanup (#1325)

* feat: improved ts typings
* chore: remove uncommented code
This commit is contained in:
Pranshu Chittora 2022-06-28 17:32:02 +05:30 committed by GitHub
parent 6dbc11991b
commit ba7427f280
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 353 additions and 449 deletions

View File

@ -24,8 +24,11 @@ function TextToolTip({ text, url }: TextToolTipProps): JSX.Element {
); );
} }
TextToolTip.defaultProps = {
url: '',
};
interface TextToolTipProps { interface TextToolTipProps {
url: string; url?: string;
text: string; text: string;
} }

View File

@ -36,18 +36,17 @@ export const UpdateDashboard = async ({
nullZeroValues: '', nullZeroValues: '',
opacity: '', opacity: '',
panelTypes: graphType, panelTypes: graphType,
queryType: 0,
query: { query: {
queryType: EQueryType.QUERY_BUILDER, queryType: EQueryType.QUERY_BUILDER,
promQL: [ promQL: [
{ {
name: GetQueryName([]), name: GetQueryName([]) || '',
...PromQLQueryTemplate, ...PromQLQueryTemplate,
}, },
], ],
clickHouse: [ clickHouse: [
{ {
name: GetQueryName([]), name: GetQueryName([]) || '',
...ClickHouseQueryTemplate, ...ClickHouseQueryTemplate,
}, },
], ],
@ -55,14 +54,14 @@ export const UpdateDashboard = async ({
formulas: [], formulas: [],
queryBuilder: [ queryBuilder: [
{ {
name: GetQueryName([]), name: GetQueryName([]) || '',
...QueryBuilderQueryTemplate, ...QueryBuilderQueryTemplate,
}, },
], ],
}, },
}, },
queryData: { queryData: {
data: [], data: { queryData: [] },
error: false, error: false,
errorMessage: '', errorMessage: '',
loading: false, loading: false,

View File

@ -73,10 +73,7 @@ function ImportJSON({
...e, ...e,
queryData: { queryData: {
...e.queryData, ...e.queryData,
data: e.queryData.data.map((queryData) => ({ data: e.queryData.data,
...queryData,
queryData: [],
})),
error: false, error: false,
errorMessage: '', errorMessage: '',
loading: false, loading: false,

View File

@ -4,7 +4,7 @@ import React from 'react';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { AppState } from 'store/reducers'; import { AppState } from 'store/reducers';
import { PromQLWidgets, Widgets } from 'types/api/dashboard/getAll'; import { PromQLWidgets } from 'types/api/dashboard/getAll';
import MetricReducer from 'types/reducer/metrics'; import MetricReducer from 'types/reducer/metrics';
import { Card, GraphContainer, GraphTitle, Row } from '../styles'; import { Card, GraphContainer, GraphTitle, Row } from '../styles';

View File

@ -29,7 +29,7 @@ function External({ getWidget }: ExternalProps): JSX.Element {
widget={getWidget([ widget={getWidget([
{ {
query: `max((sum(rate(signoz_external_call_latency_count{service_name="${servicename}", status_code="STATUS_CODE_ERROR"${resourceAttributePromQLQuery}}[1m]) OR rate(signoz_external_call_latency_count{service_name="${servicename}", http_status_code=~"5.."${resourceAttributePromQLQuery}}[1m]) OR vector(0)) by (http_url))*100/sum(rate(signoz_external_call_latency_count{service_name="${servicename}"${resourceAttributePromQLQuery}}[1m])) by (http_url)) < 1000 OR vector(0)`, query: `max((sum(rate(signoz_external_call_latency_count{service_name="${servicename}", status_code="STATUS_CODE_ERROR"${resourceAttributePromQLQuery}}[1m]) OR rate(signoz_external_call_latency_count{service_name="${servicename}", http_status_code=~"5.."${resourceAttributePromQLQuery}}[1m]) OR vector(0)) by (http_url))*100/sum(rate(signoz_external_call_latency_count{service_name="${servicename}"${resourceAttributePromQLQuery}}[1m])) by (http_url)) < 1000 OR vector(0)`,
legend, legend: 'External Call Error Percentage',
}, },
])} ])}
yAxisUnit="%" yAxisUnit="%"

View File

@ -42,7 +42,8 @@ function Application({ getWidget }: DashboardProps): JSX.Element {
urlParams.set(METRICS_PAGE_QUERY_PARAM.endTime, tPlusOne.toString()); urlParams.set(METRICS_PAGE_QUERY_PARAM.endTime, tPlusOne.toString());
history.replace( history.replace(
`${ROUTES.TRACE `${
ROUTES.TRACE
}?${urlParams.toString()}&selected={"serviceName":["${servicename}"]}&filterToFetchData=["duration","status","serviceName"]&spanAggregateCurrentPage=1&selectedTags=${selectedTraceTags}&&isFilterExclude={"serviceName":false}&userSelectedFilter={"status":["error","ok"],"serviceName":["${servicename}"]}&spanAggregateCurrentPage=1&spanAggregateOrder=ascend`, }?${urlParams.toString()}&selected={"serviceName":["${servicename}"]}&filterToFetchData=["duration","status","serviceName"]&spanAggregateCurrentPage=1&selectedTags=${selectedTraceTags}&&isFilterExclude={"serviceName":false}&userSelectedFilter={"status":["error","ok"],"serviceName":["${servicename}"]}&spanAggregateCurrentPage=1&spanAggregateOrder=ascend`,
); );
}; };
@ -93,7 +94,8 @@ function Application({ getWidget }: DashboardProps): JSX.Element {
urlParams.set(METRICS_PAGE_QUERY_PARAM.endTime, tPlusOne.toString()); urlParams.set(METRICS_PAGE_QUERY_PARAM.endTime, tPlusOne.toString());
history.replace( history.replace(
`${ROUTES.TRACE `${
ROUTES.TRACE
}?${urlParams.toString()}?selected={"serviceName":["${servicename}"],"status":["error"]}&filterToFetchData=["duration","status","serviceName"]&spanAggregateCurrentPage=1&selectedTags=${selectedTraceTags}&isFilterExclude={"serviceName":false,"status":false}&userSelectedFilter={"serviceName":["${servicename}"],"status":["error"]}&spanAggregateCurrentPage=1&spanAggregateOrder=ascend`, }?${urlParams.toString()}?selected={"serviceName":["${servicename}"],"status":["error"]}&filterToFetchData=["duration","status","serviceName"]&spanAggregateCurrentPage=1&selectedTags=${selectedTraceTags}&isFilterExclude={"serviceName":false,"status":false}&userSelectedFilter={"serviceName":["${servicename}"],"status":["error"]}&spanAggregateCurrentPage=1&spanAggregateOrder=ascend`,
); );
}; };

View File

@ -20,7 +20,7 @@ const getWidget = (query: PromQLWidgets['query']): PromQLWidgets => {
panelTypes: 'TIME_SERIES', panelTypes: 'TIME_SERIES',
query, query,
queryData: { queryData: {
data: [], data: { queryData: [] },
error: false, error: false,
errorMessage: '', errorMessage: '',
loading: false, loading: false,

View File

@ -5,7 +5,7 @@ export const AggregateFunctions = Object.keys(EAggregateOperator)
.map((key) => { .map((key) => {
return { return {
label: key, label: key,
value: EAggregateOperator[key], value: EAggregateOperator[key as keyof typeof EAggregateOperator],
}; };
}); });

View File

@ -10,13 +10,21 @@ import React, { useState } from 'react';
import { QueryWrapper } from '../styles'; import { QueryWrapper } from '../styles';
interface IQueryHeaderProps {
disabled: boolean;
onDisable: VoidFunction;
name: string;
onDelete: VoidFunction;
children: React.ReactNode;
}
function QueryHeader({ function QueryHeader({
disabled, disabled,
onDisable, onDisable,
name, name,
onDelete, onDelete,
children, children,
}): JSX.Element { }: IQueryHeaderProps): JSX.Element {
const [collapse, setCollapse] = useState(false); const [collapse, setCollapse] = useState(false);
return ( return (
<QueryWrapper> <QueryWrapper>

View File

@ -2,23 +2,31 @@ import { PlusOutlined } from '@ant-design/icons';
import { ClickHouseQueryTemplate } from 'constants/dashboard'; import { ClickHouseQueryTemplate } from 'constants/dashboard';
import GetQueryName from 'lib/query/GetQueryName'; import GetQueryName from 'lib/query/GetQueryName';
import React from 'react'; import React from 'react';
import { Query } from 'types/api/dashboard/getAll';
import { WIDGET_CLICKHOUSE_QUERY_KEY_NAME } from '../../constants'; import { WIDGET_CLICKHOUSE_QUERY_KEY_NAME } from '../../constants';
import { QueryButton } from '../../styles'; import { QueryButton } from '../../styles';
import { IHandleUpdatedQuery } from '../../types';
import ClickHouseQueryBuilder from './query'; import ClickHouseQueryBuilder from './query';
import { IClickHouseQueryHandleChange } from './types';
interface IClickHouseQueryContainerProps {
queryData: Query;
updateQueryData: (args: IHandleUpdatedQuery) => void;
clickHouseQueries: Query['clickHouse'];
}
function ClickHouseQueryContainer({ function ClickHouseQueryContainer({
queryData, queryData,
updateQueryData, updateQueryData,
clickHouseQueries, clickHouseQueries,
}): JSX.Element | null { }: IClickHouseQueryContainerProps): JSX.Element | null {
const handleClickHouseQueryChange = ({ const handleClickHouseQueryChange = ({
queryIndex, queryIndex,
rawQuery, rawQuery,
legend, legend,
toggleDisable, toggleDisable,
toggleDelete, toggleDelete,
}): void => { }: IClickHouseQueryHandleChange): void => {
const allQueries = queryData[WIDGET_CLICKHOUSE_QUERY_KEY_NAME]; const allQueries = queryData[WIDGET_CLICKHOUSE_QUERY_KEY_NAME];
const currentIndexQuery = allQueries[queryIndex]; const currentIndexQuery = allQueries[queryIndex];
@ -40,7 +48,7 @@ function ClickHouseQueryContainer({
}; };
const addQueryHandler = (): void => { const addQueryHandler = (): void => {
queryData[WIDGET_CLICKHOUSE_QUERY_KEY_NAME].push({ queryData[WIDGET_CLICKHOUSE_QUERY_KEY_NAME].push({
name: GetQueryName(queryData[WIDGET_CLICKHOUSE_QUERY_KEY_NAME]), name: GetQueryName(queryData[WIDGET_CLICKHOUSE_QUERY_KEY_NAME]) || '',
...ClickHouseQueryTemplate, ...ClickHouseQueryTemplate,
}); });
updateQueryData({ updatedQuery: { ...queryData } }); updateQueryData({ updatedQuery: { ...queryData } });

View File

@ -1,14 +1,22 @@
import { Input } from 'antd'; import { Input } from 'antd';
import MonacoEditor from 'components/Editor'; import MonacoEditor from 'components/Editor';
import React from 'react'; import React from 'react';
import { IClickHouseQuery } from 'types/api/dashboard/getAll';
import QueryHeader from '../QueryHeader'; import QueryHeader from '../QueryHeader';
import { IClickHouseQueryHandleChange } from './types';
interface IClickHouseQueryBuilderProps {
queryData: IClickHouseQuery;
queryIndex: number;
handleQueryChange: (args: IClickHouseQueryHandleChange) => void;
}
function ClickHouseQueryBuilder({ function ClickHouseQueryBuilder({
queryData, queryData,
queryIndex, queryIndex,
handleQueryChange, handleQueryChange,
}): JSX.Element | null { }: IClickHouseQueryBuilderProps): JSX.Element | null {
if (queryData === undefined) { if (queryData === undefined) {
return null; return null;
} }
@ -26,7 +34,6 @@ function ClickHouseQueryBuilder({
> >
<MonacoEditor <MonacoEditor
language="sql" language="sql"
theme="vs-dark"
height="200px" height="200px"
onChange={(value): void => onChange={(value): void =>
handleQueryChange({ queryIndex, rawQuery: value }) handleQueryChange({ queryIndex, rawQuery: value })

View File

@ -0,0 +1,9 @@
import { IClickHouseQuery } from 'types/api/dashboard/getAll';
export interface IClickHouseQueryHandleChange {
queryIndex: number;
rawQuery?: IClickHouseQuery['rawQuery'];
legend?: IClickHouseQuery['legend'];
toggleDisable?: IClickHouseQuery['disabled'];
toggleDelete?: boolean;
}

View File

@ -1,95 +0,0 @@
import {
DeleteOutlined,
DownOutlined,
EyeFilled,
RightOutlined,
} from '@ant-design/icons';
import { Button, Row } from 'antd';
import MonacoEditor from 'components/Editor';
import React, { useState } from 'react';
import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
import AppReducer from 'types/reducer/app';
import { QueryBuilderWrapper, QueryWrapper } from '../styles';
import { TQueryCategories } from '../types';
import PromQLQueryBuilder from './promQL/promQL';
import MetricsBuilder from './queryBuilder/query';
function QueryBuilder({
name,
onDelete,
queryCategory,
queryData,
updateQueryData,
}: QueryBuilderProps): JSX.Element {
const { isDarkMode } = useSelector<AppState, AppReducer>((state) => state.app);
const [hideFromUI, setHideFromUI] = useState<boolean>(false);
const handleHideFromUI = (): void => {
setHideFromUI(!hideFromUI);
};
const handleClickhouseQueryChange = (clickHouseQuery): void => {
if (clickHouseQuery !== null) {
queryData.clickHouseQuery = clickHouseQuery;
}
updateQueryData(queryData);
};
const handlePromQLQueryChange = ({ query, legend }): void => {
if (query) queryData.promQL.query = query;
if (legend) queryData.promQL.legend = legend;
updateQueryData(queryData);
};
return (
<QueryWrapper>
<Row style={{ justifyContent: 'space-between' }}>
<Row>
<Button type="ghost" icon={<EyeFilled />}>
{name}
</Button>
<Button
type="ghost"
icon={hideFromUI ? <RightOutlined /> : <DownOutlined />}
onClick={handleHideFromUI}
/>
</Row>
<Button type="ghost" danger icon={<DeleteOutlined />} onClick={onDelete} />
</Row>
{!hideFromUI && (
<QueryBuilderWrapper isDarkMode={isDarkMode}>
{queryCategory === 0 ? (
<MetricsBuilder
queryData={queryData}
updateQueryData={updateQueryData}
metricName={queryData.queryBuilder.metricName}
/>
) : queryCategory === 1 ? (
<MonacoEditor
language="sql"
theme="vs-dark"
height="200px"
onChange={handleClickhouseQueryChange}
value={queryData.clickHouseQuery}
// options={options}
/>
) : queryCategory === 2 ? (
<PromQLQueryBuilder
query={queryData.promQL.query}
onQueryChange={(value) => handlePromQLQueryChange({ query: value })}
legend={queryData.promQL.legend}
onLegendChange={(value) => handlePromQLQueryChange({ legend: value })}
/>
) : null}
</QueryBuilderWrapper>
)}
</QueryWrapper>
);
}
interface QueryBuilderProps {
queryCategory: TQueryCategories;
name: string;
}
export default QueryBuilder;

View File

@ -2,25 +2,32 @@ import { PlusOutlined } from '@ant-design/icons';
import { PromQLQueryTemplate } from 'constants/dashboard'; import { PromQLQueryTemplate } from 'constants/dashboard';
import GetQueryName from 'lib/query/GetQueryName'; import GetQueryName from 'lib/query/GetQueryName';
import React from 'react'; import React from 'react';
import { IPromQLQuery, Query } from 'types/api/dashboard/getAll';
import { WIDGET_PROMQL_QUERY_KEY_NAME } from '../../constants'; import { WIDGET_PROMQL_QUERY_KEY_NAME } from '../../constants';
import { QueryButton } from '../../styles'; import { QueryButton } from '../../styles';
import { IHandleUpdatedQuery } from '../../types';
import PromQLQueryBuilder from './query'; import PromQLQueryBuilder from './query';
import { IPromQLQueryHandleChange } from './types';
interface IPromQLQueryContainerProps {
queryData: Query;
updateQueryData: (args: IHandleUpdatedQuery) => void;
promQLQueries: IPromQLQuery[];
}
function PromQLQueryContainer({ function PromQLQueryContainer({
queryData, queryData,
updateQueryData, updateQueryData,
promQLQueries, promQLQueries,
onQueryChange, }: IPromQLQueryContainerProps): JSX.Element | null {
onQueryAdd,
}): JSX.Element | null {
const handlePromQLQueryChange = ({ const handlePromQLQueryChange = ({
queryIndex, queryIndex,
query, query,
legend, legend,
toggleDisable, toggleDisable,
toggleDelete, toggleDelete,
}): void => { }: IPromQLQueryHandleChange): void => {
const allQueries = queryData[WIDGET_PROMQL_QUERY_KEY_NAME]; const allQueries = queryData[WIDGET_PROMQL_QUERY_KEY_NAME];
const currentIndexQuery = allQueries[queryIndex]; const currentIndexQuery = allQueries[queryIndex];
if (query) currentIndexQuery.query = query; if (query) currentIndexQuery.query = query;
@ -36,7 +43,7 @@ function PromQLQueryContainer({
}; };
const addQueryHandler = (): void => { const addQueryHandler = (): void => {
queryData[WIDGET_PROMQL_QUERY_KEY_NAME].push({ queryData[WIDGET_PROMQL_QUERY_KEY_NAME].push({
name: GetQueryName(queryData[WIDGET_PROMQL_QUERY_KEY_NAME]), name: GetQueryName(queryData[WIDGET_PROMQL_QUERY_KEY_NAME]) || '',
...PromQLQueryTemplate, ...PromQLQueryTemplate,
}); });
updateQueryData({ updatedQuery: { ...queryData } }); updateQueryData({ updatedQuery: { ...queryData } });
@ -48,7 +55,7 @@ function PromQLQueryContainer({
return ( return (
<> <>
{promQLQueries.map( {promQLQueries.map(
(q, idx): JSX.Element => ( (q: IPromQLQuery, idx: number): JSX.Element => (
<PromQLQueryBuilder <PromQLQueryBuilder
key={q.name} key={q.name}
queryIndex={idx} queryIndex={idx}

View File

@ -1,13 +1,21 @@
import { Input } from 'antd'; import { Input } from 'antd';
import React from 'react'; import React from 'react';
import { IPromQLQuery } from 'types/api/dashboard/getAll';
import QueryHeader from '../QueryHeader'; import QueryHeader from '../QueryHeader';
import { IPromQLQueryHandleChange } from './types';
interface IPromQLQueryBuilderProps {
queryData: IPromQLQuery;
queryIndex: number;
handleQueryChange: (args: IPromQLQueryHandleChange) => void;
}
function PromQLQueryBuilder({ function PromQLQueryBuilder({
queryData, queryData,
queryIndex, queryIndex,
handleQueryChange, handleQueryChange,
}): JSX.Element { }: IPromQLQueryBuilderProps): JSX.Element {
return ( return (
<QueryHeader <QueryHeader
name={queryData.name} name={queryData.name}

View File

@ -0,0 +1,9 @@
import { IPromQLQuery } from 'types/api/dashboard/getAll';
export interface IPromQLQueryHandleChange {
queryIndex: number;
query?: IPromQLQuery['query'];
legend?: IPromQLQuery['legend'];
toggleDisable?: IPromQLQuery['disabled'];
toggleDelete?: boolean;
}

View File

@ -1,12 +1,12 @@
import React from 'react'; import React from 'react';
import { QueryChipContainer, QueryChipItem } from './styles'; import { QueryChipContainer, QueryChipItem } from './styles';
import { IMetricBuilderTagKeyQuery } from './types'; import { ITagKeyValueQuery } from './types';
interface IQueryChipProps { interface IQueryChipProps {
queryData: IMetricBuilderTagKeyQuery; queryData: ITagKeyValueQuery;
onClose: (id: string) => void; onClose: (id: string) => void;
disabled: boolean; disabled?: boolean;
} }
export default function QueryChip({ export default function QueryChip({
@ -29,3 +29,6 @@ export default function QueryChip({
</QueryChipContainer> </QueryChipContainer>
); );
} }
QueryChip.defaultProps = {
disabled: false,
};

View File

@ -5,13 +5,14 @@ import { map } from 'lodash-es';
import React, { useCallback, useEffect, useState } from 'react'; import React, { useCallback, useEffect, useState } from 'react';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers'; import { AppState } from 'store/reducers';
import { IMetricsBuilderQuery } from 'types/api/dashboard/getAll';
import AppReducer from 'types/reducer/app'; import AppReducer from 'types/reducer/app';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import { ResourceAttributesFilterMachine } from './MetricTagKey.machine'; import { ResourceAttributesFilterMachine } from './MetricTagKey.machine';
import QueryChip from './QueryChip'; import QueryChip from './QueryChip';
import { QueryChipItem, SearchContainer } from './styles'; import { QueryChipItem, SearchContainer } from './styles';
import { IMetricBuilderTagKeyQuery, IOption } from './types'; import { IOption, ITagKeyValueQuery } from './types';
import { import {
createQuery, createQuery,
GetTagKeys, GetTagKeys,
@ -20,16 +21,22 @@ import {
SingleValueOperators, SingleValueOperators,
} from './utils'; } from './utils';
interface IMetricTagKeyFilterProps {
metricName: IMetricsBuilderQuery['metricName'];
onSetQuery: (args: IMetricsBuilderQuery['tagFilters']['items']) => void;
selectedTagFilters: IMetricsBuilderQuery['tagFilters']['items'];
}
function MetricTagKeyFilter({ function MetricTagKeyFilter({
metricName, metricName,
onSetQuery, onSetQuery,
selectedTagFilters: selectedTagQueries, selectedTagFilters: selectedTagQueries,
}): JSX.Element | null { }: IMetricTagKeyFilterProps): JSX.Element | null {
const { isDarkMode } = useSelector<AppState, AppReducer>((state) => state.app); const { isDarkMode } = useSelector<AppState, AppReducer>((state) => state.app);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [selectedValues, setSelectedValues] = useState<string[]>([]); const [selectedValues, setSelectedValues] = useState<string[]>([]);
const [staging, setStaging] = useState<string[]>([]); const [staging, setStaging] = useState<string[]>([]);
const [queries, setQueries] = useState<IMetricBuilderTagKeyQuery[]>([]); const [queries, setQueries] = useState<ITagKeyValueQuery[]>([]);
const [optionsData, setOptionsData] = useState<{ const [optionsData, setOptionsData] = useState<{
mode: undefined | 'tags' | 'multiple'; mode: undefined | 'tags' | 'multiple';
options: IOption[]; options: IOption[];
@ -39,7 +46,7 @@ function MetricTagKeyFilter({
}); });
const dispatchQueries = ( const dispatchQueries = (
updatedQueries: IMetricBuilderTagKeyQuery[], updatedQueries: IMetricsBuilderQuery['tagFilters']['items'],
): void => { ): void => {
onSetQuery(updatedQueries); onSetQuery(updatedQueries);
setQueries(updatedQueries); setQueries(updatedQueries);
@ -54,7 +61,7 @@ function MetricTagKeyFilter({
actions: { actions: {
onSelectTagKey: () => { onSelectTagKey: () => {
handleLoading(true); handleLoading(true);
GetTagKeys(metricName) GetTagKeys(metricName || '')
.then((tagKeys) => setOptionsData({ options: tagKeys, mode: undefined })) .then((tagKeys) => setOptionsData({ options: tagKeys, mode: undefined }))
.finally(() => { .finally(() => {
handleLoading(false); handleLoading(false);
@ -66,7 +73,7 @@ function MetricTagKeyFilter({
onSelectTagValue: () => { onSelectTagValue: () => {
handleLoading(true); handleLoading(true);
GetTagValues(staging[0], metricName) GetTagValues(staging[0], metricName || '')
.then((tagValuesOptions) => .then((tagValuesOptions) =>
setOptionsData({ options: tagValuesOptions, mode: 'tags' }), setOptionsData({ options: tagValuesOptions, mode: 'tags' }),
) )
@ -113,16 +120,17 @@ function MetricTagKeyFilter({
handleBlur(); handleBlur();
}, [handleBlur, metricName]); }, [handleBlur, metricName]);
const handleChange = (value: never): void => { const handleChange = (value: never | string[]): void => {
if (!optionsData.mode) { if (!optionsData.mode) {
setStaging((prevStaging) => [...prevStaging, value]); setStaging((prevStaging) => [...prevStaging, String(value)]);
setSelectedValues([]); setSelectedValues([]);
send('NEXT'); send('NEXT');
return; return;
} }
if ( if (
state.value === 'TagValue' && state.value === 'TagValue' &&
SingleValueOperators.includes(staging[staging.length - 1]) SingleValueOperators.includes(staging[staging.length - 1]) &&
Array.isArray(value)
) { ) {
setSelectedValues([value[value.length - 1]]); setSelectedValues([value[value.length - 1]]);
return; return;
@ -142,12 +150,6 @@ function MetricTagKeyFilter({
setSelectedValues([]); setSelectedValues([]);
}; };
const disabledOrEmpty = !!(
queries.length ||
staging.length ||
selectedValues.length
);
return ( return (
<SearchContainer isDarkMode={isDarkMode}> <SearchContainer isDarkMode={isDarkMode}>
<div style={{ display: 'inline-flex', flexWrap: 'wrap' }}> <div style={{ display: 'inline-flex', flexWrap: 'wrap' }}>
@ -162,7 +164,7 @@ function MetricTagKeyFilter({
)} )}
</div> </div>
<div> <div>
{map(staging, (item, idx) => { {map(staging, (item) => {
return <QueryChipItem key={uuid()}>{item}</QueryChipItem>; return <QueryChipItem key={uuid()}>{item}</QueryChipItem>;
})} })}
</div> </div>

View File

@ -4,7 +4,7 @@ import styled from 'styled-components';
export const SearchContainer = styled.div<{ export const SearchContainer = styled.div<{
isDarkMode: boolean; isDarkMode: boolean;
disabled: boolean; disabled?: boolean;
}>` }>`
background: ${({ isDarkMode }): string => (isDarkMode ? '#000' : '#fff')}; background: ${({ isDarkMode }): string => (isDarkMode ? '#000' : '#fff')};
flex: 1; flex: 1;

View File

@ -9,3 +9,10 @@ export interface IMetricBuilderTagKeyQuery {
operator: string; operator: string;
tagValue: string[]; tagValue: string[];
} }
export interface ITagKeyValueQuery {
id: string;
key: string;
op: string;
value: string[];
}

View File

@ -5,7 +5,7 @@ import {
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import { TagKeyOperator } from '../../Options'; import { TagKeyOperator } from '../../Options';
import { IMetricBuilderTagKeyQuery, IOption } from './types'; import { IOption, ITagKeyValueQuery } from './types';
export const OperatorSchema: IOption[] = TagKeyOperator; export const OperatorSchema: IOption[] = TagKeyOperator;
@ -40,12 +40,12 @@ export const GetTagValues = async (
export const createQuery = ( export const createQuery = (
selectedItems: Array<string | string[]> = [], selectedItems: Array<string | string[]> = [],
): IMetricBuilderTagKeyQuery | null => { ): ITagKeyValueQuery | null => {
if (selectedItems.length === 3) { if (selectedItems.length === 3) {
return { return {
id: uuid().slice(0, 8), id: uuid().slice(0, 8),
key: selectedItems[0] as string, key: typeof selectedItems[0] === 'string' ? selectedItems[0] : '',
op: selectedItems[1] as string, op: typeof selectedItems[1] === 'string' ? selectedItems[1] : '',
value: selectedItems[2] as string[], value: selectedItems[2] as string[],
}; };
} }

View File

@ -1,15 +1,22 @@
import { Input } from 'antd'; import { Input } from 'antd';
import React from 'react'; import React from 'react';
import { IMetricsBuilderFormula } from 'types/api/dashboard/getAll';
import QueryHeader from '../QueryHeader'; import QueryHeader from '../QueryHeader';
import { IQueryBuilderFormulaHandleChange } from './types';
const { TextArea } = Input; const { TextArea } = Input;
interface IMetricsBuilderFormulaProps {
formulaData: IMetricsBuilderFormula;
formulaIndex: number;
handleFormulaChange: (args: IQueryBuilderFormulaHandleChange) => void;
}
function MetricsBuilderFormula({ function MetricsBuilderFormula({
formulaData, formulaData,
formulaIndex, formulaIndex,
handleFormulaChange, handleFormulaChange,
}): JSX.Element { }: IMetricsBuilderFormulaProps): JSX.Element {
return ( return (
<QueryHeader <QueryHeader
name={formulaData.name} name={formulaData.name}

View File

@ -4,25 +4,39 @@ import {
QueryBuilderFormulaTemplate, QueryBuilderFormulaTemplate,
QueryBuilderQueryTemplate, QueryBuilderQueryTemplate,
} from 'constants/dashboard'; } from 'constants/dashboard';
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
import GetFormulaName from 'lib/query/GetFormulaName'; import GetFormulaName from 'lib/query/GetFormulaName';
import GetQueryName from 'lib/query/GetQueryName'; import GetQueryName from 'lib/query/GetQueryName';
import React from 'react'; import React from 'react';
import { Query } from 'types/api/dashboard/getAll';
import { import {
WIDGET_QUERY_BUILDER_FORMULA_KEY_NAME, WIDGET_QUERY_BUILDER_FORMULA_KEY_NAME,
WIDGET_QUERY_BUILDER_QUERY_KEY_NAME, WIDGET_QUERY_BUILDER_QUERY_KEY_NAME,
} from '../../constants'; } from '../../constants';
import { QueryButton } from '../../styles'; import { QueryButton } from '../../styles';
import { IHandleUpdatedQuery } from '../../types';
import MetricsBuilderFormula from './formula'; import MetricsBuilderFormula from './formula';
import MetricsBuilder from './query'; import MetricsBuilder from './query';
import {
IQueryBuilderFormulaHandleChange,
IQueryBuilderQueryHandleChange,
} from './types';
import { canCreateQueryAndFormula } from './utils'; import { canCreateQueryAndFormula } from './utils';
interface IQueryBuilderQueryContainerProps {
queryData: Query;
updateQueryData: (args: IHandleUpdatedQuery) => void;
metricsBuilderQueries: Query['metricsBuilder'];
selectedGraph: GRAPH_TYPES;
}
function QueryBuilderQueryContainer({ function QueryBuilderQueryContainer({
queryData, queryData,
updateQueryData, updateQueryData,
metricsBuilderQueries, metricsBuilderQueries,
selectedGraph, selectedGraph,
}): JSX.Element | null { }: IQueryBuilderQueryContainerProps): JSX.Element | null {
const handleQueryBuilderQueryChange = ({ const handleQueryBuilderQueryChange = ({
queryIndex, queryIndex,
aggregateFunction, aggregateFunction,
@ -33,7 +47,7 @@ function QueryBuilderQueryContainer({
toggleDisable, toggleDisable,
toggleDelete, toggleDelete,
reduceTo, reduceTo,
}): void => { }: IQueryBuilderQueryHandleChange): void => {
const allQueries = const allQueries =
queryData[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME].queryBuilder; queryData[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME].queryBuilder;
const currentIndexQuery = allQueries[queryIndex]; const currentIndexQuery = allQueries[queryIndex];
@ -73,7 +87,7 @@ function QueryBuilderQueryContainer({
expression, expression,
toggleDisable, toggleDisable,
toggleDelete, toggleDelete,
}): void => { }: IQueryBuilderFormulaHandleChange): void => {
const allFormulas = const allFormulas =
queryData[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME][ queryData[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME][
WIDGET_QUERY_BUILDER_FORMULA_KEY_NAME WIDGET_QUERY_BUILDER_FORMULA_KEY_NAME
@ -102,9 +116,9 @@ function QueryBuilderQueryContainer({
return; return;
} }
queryData[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME].queryBuilder.push({ queryData[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME].queryBuilder.push({
name: GetQueryName( name:
queryData[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME].queryBuilder, GetQueryName(queryData[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME].queryBuilder) ||
), '',
...QueryBuilderQueryTemplate, ...QueryBuilderQueryTemplate,
}); });
updateQueryData({ updatedQuery: { ...queryData } }); updateQueryData({ updatedQuery: { ...queryData } });
@ -121,11 +135,12 @@ function QueryBuilderQueryContainer({
queryData[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME][ queryData[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME][
WIDGET_QUERY_BUILDER_FORMULA_KEY_NAME WIDGET_QUERY_BUILDER_FORMULA_KEY_NAME
].push({ ].push({
name: GetFormulaName( name:
queryData[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME][ GetFormulaName(
WIDGET_QUERY_BUILDER_FORMULA_KEY_NAME queryData[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME][
], WIDGET_QUERY_BUILDER_FORMULA_KEY_NAME
), ],
) || '',
...QueryBuilderFormulaTemplate, ...QueryBuilderFormulaTemplate,
}); });
updateQueryData({ updatedQuery: { ...queryData } }); updateQueryData({ updatedQuery: { ...queryData } });

View File

@ -2,28 +2,40 @@ import { AutoComplete, Col, Input, Row, Select, Spin } from 'antd';
import { getMetricName } from 'api/metrics/getMetricName'; import { getMetricName } from 'api/metrics/getMetricName';
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider'; import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { IMetricsBuilderQuery } from 'types/api/dashboard/getAll';
import { EReduceOperator } from 'types/common/dashboard'; import { EReduceOperator } from 'types/common/dashboard';
import { TQueryCategories } from '../../types';
import { AggregateFunctions } from '../Options'; import { AggregateFunctions } from '../Options';
import QueryHeader from '../QueryHeader'; import QueryHeader from '../QueryHeader';
import MetricTagKeyFilter from './MetricTagKeyFilter'; import MetricTagKeyFilter from './MetricTagKeyFilter';
import { IOption } from './MetricTagKeyFilter/types';
import { GetTagKeys } from './MetricTagKeyFilter/utils'; import { GetTagKeys } from './MetricTagKeyFilter/utils';
import { IQueryBuilderQueryHandleChange } from './types';
const { Option } = Select; const { Option } = Select;
function MetricsBuilder({
queryData,
queryIndex,
handleQueryChange,
selectedGraph,
}: QueryBuilderProps): JSX.Element {
const [groupByOptions, setGroupByOptions] = useState([]);
const [metricName, setMetricName] = useState(queryData.metricName);
const [metricNameList, setMetricNameList] = useState([]); interface IMetricsBuilderProps {
queryIndex: number;
selectedGraph: GRAPH_TYPES;
queryData: IMetricsBuilderQuery;
handleQueryChange: (args: IQueryBuilderQueryHandleChange) => void;
}
function MetricsBuilder({
queryIndex,
selectedGraph,
queryData,
handleQueryChange,
}: IMetricsBuilderProps): JSX.Element {
const [groupByOptions, setGroupByOptions] = useState<IOption[]>([]);
const [metricName, setMetricName] = useState<string | null>(
queryData.metricName,
);
const [metricNameList, setMetricNameList] = useState<string[]>([]);
const [metricNameLoading, setMetricNameLoading] = useState(false); const [metricNameLoading, setMetricNameLoading] = useState(false);
const handleMetricNameSelect = (e): void => { const handleMetricNameSelect = (e: string): void => {
handleQueryChange({ queryIndex, metricName: e }); handleQueryChange({ queryIndex, metricName: e });
setMetricName(e); setMetricName(e);
}; };
@ -51,7 +63,7 @@ function MetricsBuilder({
}; };
useEffect(() => { useEffect(() => {
GetTagKeys(metricName).then((tagKeys) => { GetTagKeys(metricName || '').then((tagKeys) => {
setGroupByOptions(tagKeys); setGroupByOptions(tagKeys);
}); });
}, [metricName]); }, [metricName]);
@ -120,7 +132,9 @@ function MetricsBuilder({
<MetricTagKeyFilter <MetricTagKeyFilter
metricName={metricName} metricName={metricName}
selectedTagFilters={queryData.tagFilters.items} selectedTagFilters={queryData.tagFilters.items}
onSetQuery={(updatedTagFilters): void => onSetQuery={(
updatedTagFilters: IMetricsBuilderQuery['tagFilters']['items'],
): void =>
handleQueryChange({ queryIndex, tagFilters: updatedTagFilters }) handleQueryChange({ queryIndex, tagFilters: updatedTagFilters })
} }
/> />
@ -166,9 +180,13 @@ function MetricsBuilder({
.filter((op) => !(parseInt(op, 10) >= 0)) .filter((op) => !(parseInt(op, 10) >= 0))
.map((op) => ({ .map((op) => ({
label: op, label: op,
value: EReduceOperator[op], value: EReduceOperator[op as keyof typeof EReduceOperator],
}))} }))}
defaultValue={EReduceOperator[queryData.reduceTo]} defaultValue={
EReduceOperator[
(queryData.reduceTo as unknown) as keyof typeof EReduceOperator
]
}
onChange={(e): void => { onChange={(e): void => {
handleQueryChange({ queryIndex, reduceTo: e }); handleQueryChange({ queryIndex, reduceTo: e });
}} }}
@ -193,8 +211,4 @@ function MetricsBuilder({
); );
} }
interface QueryBuilderProps {
selectedGraph: GRAPH_TYPES;
queryCategory: TQueryCategories;
}
export default MetricsBuilder; export default MetricsBuilder;

View File

@ -0,0 +1,23 @@
import {
IMetricsBuilderFormula,
IMetricsBuilderQuery,
} from 'types/api/dashboard/getAll';
export interface IQueryBuilderQueryHandleChange {
queryIndex: number;
aggregateFunction?: IMetricsBuilderQuery['aggregateOperator'];
metricName?: IMetricsBuilderQuery['metricName'];
tagFilters?: IMetricsBuilderQuery['tagFilters']['items'];
groupBy?: IMetricsBuilderQuery['groupBy'];
legend?: IMetricsBuilderQuery['legend'];
toggleDisable?: boolean;
toggleDelete?: boolean;
reduceTo?: IMetricsBuilderQuery['reduceTo'];
}
export interface IQueryBuilderFormulaHandleChange {
formulaIndex: number;
expression?: IMetricsBuilderFormula['expression'];
toggleDisable?: IMetricsBuilderFormula['disabled'];
toggleDelete?: boolean;
}

View File

@ -1,3 +1,5 @@
import { Query } from 'types/api/dashboard/getAll';
import { import {
WIDGET_QUERY_BUILDER_FORMULA_KEY_NAME, WIDGET_QUERY_BUILDER_FORMULA_KEY_NAME,
WIDGET_QUERY_BUILDER_QUERY_KEY_NAME, WIDGET_QUERY_BUILDER_QUERY_KEY_NAME,
@ -5,7 +7,7 @@ import {
const QUERY_AND_FORMULA_LIMIT = 10; const QUERY_AND_FORMULA_LIMIT = 10;
export const canCreateQueryAndFormula = (query): boolean => { export const canCreateQueryAndFormula = (query: Query): boolean => {
const queries = query[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME].queryBuilder; const queries = query[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME].queryBuilder;
const formulas = const formulas =
query[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME][ query[WIDGET_QUERY_BUILDER_QUERY_KEY_NAME][

View File

@ -1,7 +1,15 @@
import { Tooltip } from 'antd'; import { Tooltip } from 'antd';
import React from 'react'; import React from 'react';
function TabHeader({ tabName, hasUnstagedChanges }): JSX.Element { interface ITabHeaderProps {
tabName: string;
hasUnstagedChanges: boolean;
}
function TabHeader({
tabName,
hasUnstagedChanges,
}: ITabHeaderProps): JSX.Element {
return ( return (
<div <div
style={{ style={{

View File

@ -1,17 +1,21 @@
/* eslint-disable */
// @ts-ignore
// @ts-nocheck
import { EQueryType } from 'types/common/dashboard'; import { EQueryType } from 'types/common/dashboard';
import { EQueryTypeToQueryKeyMapping } from './types'; import { EQueryTypeToQueryKeyMapping } from './types';
export const WIDGET_PROMQL_QUERY_KEY_NAME: string = EQueryTypeToQueryKeyMapping[ export const WIDGET_PROMQL_QUERY_KEY_NAME: EQueryTypeToQueryKeyMapping.PROM =
EQueryType[EQueryType.PROM] EQueryTypeToQueryKeyMapping[EQueryType[EQueryType.PROM]];
] as string;
export const WIDGET_CLICKHOUSE_QUERY_KEY_NAME: string = EQueryTypeToQueryKeyMapping[ export const WIDGET_CLICKHOUSE_QUERY_KEY_NAME: EQueryTypeToQueryKeyMapping.CLICKHOUSE = EQueryTypeToQueryKeyMapping[
EQueryType[EQueryType.CLICKHOUSE] EQueryType[EQueryType.CLICKHOUSE]
] as string; ] as string;
export const WIDGET_QUERY_BUILDER_QUERY_KEY_NAME: string = EQueryTypeToQueryKeyMapping[ export const WIDGET_QUERY_BUILDER_QUERY_KEY_NAME: EQueryTypeToQueryKeyMapping.QUERY_BUILDER = EQueryTypeToQueryKeyMapping[
EQueryType[EQueryType.QUERY_BUILDER] EQueryType[EQueryType.QUERY_BUILDER]
] as string; ] as string;
export const WIDGET_QUERY_BUILDER_FORMULA_KEY_NAME: string = 'formulas' as string; type TFormulas = 'formulas';
export const WIDGET_QUERY_BUILDER_FORMULA_KEY_NAME: TFormulas = 'formulas';

View File

@ -1,3 +1,6 @@
/* eslint-disable */
//@ts-nocheck
import { Button, Tabs } from 'antd'; import { Button, Tabs } from 'antd';
import TextToolTip from 'components/TextToolTip'; import TextToolTip from 'components/TextToolTip';
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider'; import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
@ -8,22 +11,13 @@ import { connect, useSelector } from 'react-redux';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
import { bindActionCreators, Dispatch } from 'redux'; import { bindActionCreators, Dispatch } from 'redux';
import { ThunkDispatch } from 'redux-thunk'; import { ThunkDispatch } from 'redux-thunk';
import { CreateQuery, CreateQueryProps } from 'store/actions';
import {
GetQueryResults,
GetQueryResultsProps,
} from 'store/actions/dashboard/getQueryResults';
import { import {
UpdateQuery, UpdateQuery,
UpdateQueryProps, UpdateQueryProps,
} from 'store/actions/dashboard/updateQuery'; } from 'store/actions/dashboard/updateQuery';
import {
UpdateQueryType,
UpdateQueryTypeProps,
} from 'store/actions/dashboard/updateQueryType';
import { AppState } from 'store/reducers'; import { AppState } from 'store/reducers';
import AppActions from 'types/actions'; import AppActions from 'types/actions';
import { Widgets } from 'types/api/dashboard/getAll'; import { Query, Widgets } from 'types/api/dashboard/getAll';
import { EQueryType } from 'types/common/dashboard'; import { EQueryType } from 'types/common/dashboard';
import DashboardReducer from 'types/reducer/dashboards'; import DashboardReducer from 'types/reducer/dashboards';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
@ -46,11 +40,13 @@ function QuerySection({
updateQuery, updateQuery,
selectedGraph, selectedGraph,
}: QueryProps): JSX.Element { }: QueryProps): JSX.Element {
const [localQueryChanges, setLocalQueryChanges] = useState({}); const [localQueryChanges, setLocalQueryChanges] = useState<Query>({} as Query);
const [rctTabKey, setRctTabKey] = useState({ const [rctTabKey, setRctTabKey] = useState<
Record<keyof typeof EQueryType, string>
>({
QUERY_BUILDER: uuid(), QUERY_BUILDER: uuid(),
CLICKHOUSE: uuid(), CLICKHOUSE: uuid(),
PROMQL: uuid(), PROM: uuid(),
}); });
const { dashboards } = useSelector<AppState, DashboardReducer>( const { dashboards } = useSelector<AppState, DashboardReducer>(
(state) => state.dashboards, (state) => state.dashboards,
@ -73,104 +69,80 @@ function QuerySection({
selectedWidget.query.queryType, selectedWidget.query.queryType,
); );
const { query = [] } = selectedWidget || {}; const { query } = selectedWidget || {};
useEffect(() => { useEffect(() => {
setLocalQueryChanges(cloneDeep(query)); setLocalQueryChanges(cloneDeep(query) as Query);
}, [query]); }, [query]);
const queryOnClickHandler = () => {
setLocalQueryChanges([
...localQueryChanges,
// {
// name: GetQueryName(localQueryChanges),
// disabled: false,
// promQL: {
// query: '',
// legend: '',
// },
// clickHouseQuery: '',
// queryBuilder: {
// metricName: null,
// aggregateOperator: null,
// tagFilters: {
// op: 'AND',
// items: [],
// },
// groupBy: [],
// },
// },
]);
};
const queryDiff = (queryA, queryB, queryCategory) => { const queryDiff = (
queryA: Query,
queryB: Query,
queryCategory: EQueryType,
): boolean => {
const keyOfConcern = getQueryKey(queryCategory); const keyOfConcern = getQueryKey(queryCategory);
return !isEqual(queryA[keyOfConcern], queryB[keyOfConcern]); return !isEqual(queryA[keyOfConcern], queryB[keyOfConcern]);
}; };
useEffect(() => { useEffect(() => {
handleUnstagedChanges( handleUnstagedChanges(
queryDiff(query, localQueryChanges, parseInt(queryCategory)), queryDiff(query, localQueryChanges, parseInt(`${queryCategory}`, 10)),
); );
}, [handleUnstagedChanges, localQueryChanges, query, queryCategory]); }, [handleUnstagedChanges, localQueryChanges, query, queryCategory]);
const purgeLocalChanges = () => { const regenRctKeys = (): void => {
setLocalQueryChanges(query);
};
const regenRctKeys = () => {
setRctTabKey((prevState) => { setRctTabKey((prevState) => {
Object.keys(prevState).map((key) => { const newState = prevState;
prevState[key] = uuid(); Object.keys(newState).forEach((key) => {
newState[key as keyof typeof EQueryType] = uuid();
}); });
return cloneDeep(prevState); return cloneDeep(newState);
}); });
}; };
const handleStageQuery = () => { const handleStageQuery = (): void => {
updateQuery({ updateQuery({
updatedQuery: localQueryChanges, updatedQuery: localQueryChanges,
widgetId: urlQuery.get('widgetId'), widgetId: urlQuery.get('widgetId') || '',
yAxisUnit: selectedWidget.yAxisUnit, yAxisUnit: selectedWidget.yAxisUnit,
}); });
}; };
const handleQueryCategoryChange = (qCategory): void => { const handleQueryCategoryChange = (qCategory: string): void => {
// If true, then it means that the user has made some changes and haven't staged them // If true, then it means that the user has made some changes and haven't staged them
const unstagedChanges = queryDiff( const unstagedChanges = queryDiff(
query, query,
localQueryChanges, localQueryChanges,
parseInt(queryCategory), parseInt(`${queryCategory}`, 10),
); );
if (unstagedChanges && showUnstagedStashConfirmBox()) { if (unstagedChanges && showUnstagedStashConfirmBox()) {
// eslint-disable-next-line no-alert
window.confirm( window.confirm(
"You are trying to navigate to different tab with unstaged changes. Your current changes will be purged. Press 'Stage & Run Query' to stage them.", "You are trying to navigate to different tab with unstaged changes. Your current changes will be purged. Press 'Stage & Run Query' to stage them.",
); );
return; return;
} }
setQueryCategory(parseInt(qCategory)); setQueryCategory(parseInt(`${qCategory}`, 10));
const newLocalQuery = { const newLocalQuery = {
...cloneDeep(query), ...cloneDeep(query),
queryType: parseInt(qCategory), queryType: parseInt(`${qCategory}`, 10),
}; };
setLocalQueryChanges(newLocalQuery); setLocalQueryChanges(newLocalQuery);
regenRctKeys(); regenRctKeys();
updateQuery({ updateQuery({
updatedQuery: newLocalQuery, updatedQuery: newLocalQuery,
widgetId: urlQuery.get('widgetId'), widgetId: urlQuery.get('widgetId') || '',
yAxisUnit: selectedWidget.yAxisUnit, yAxisUnit: selectedWidget.yAxisUnit,
}); });
}; };
const handleLocalQueryUpdate = ({ updatedQuery }) => {
setLocalQueryChanges(updatedQuery);
};
const handleDeleteQuery = ({ currentIndex }) => { const handleLocalQueryUpdate = ({
setLocalQueryChanges((prevState) => { updatedQuery,
prevState.splice(currentIndex, 1); }: IHandleUpdatedQuery): void => {
return [...prevState]; setLocalQueryChanges(updatedQuery);
});
}; };
return ( return (
@ -211,7 +183,7 @@ function QuerySection({
<QueryBuilderQueryContainer <QueryBuilderQueryContainer
key={rctTabKey.QUERY_BUILDER} key={rctTabKey.QUERY_BUILDER}
queryData={localQueryChanges} queryData={localQueryChanges}
updateQueryData={({ updatedQuery }) => { updateQueryData={({ updatedQuery }: IHandleUpdatedQuery): void => {
handleLocalQueryUpdate({ updatedQuery }); handleLocalQueryUpdate({ updatedQuery });
}} }}
metricsBuilderQueries={ metricsBuilderQueries={
@ -236,7 +208,7 @@ function QuerySection({
<ClickHouseQueryContainer <ClickHouseQueryContainer
key={rctTabKey.CLICKHOUSE} key={rctTabKey.CLICKHOUSE}
queryData={localQueryChanges} queryData={localQueryChanges}
updateQueryData={({ updatedQuery }) => { updateQueryData={({ updatedQuery }: IHandleUpdatedQuery): void => {
handleLocalQueryUpdate({ updatedQuery }); handleLocalQueryUpdate({ updatedQuery });
}} }}
clickHouseQueries={localQueryChanges[WIDGET_CLICKHOUSE_QUERY_KEY_NAME]} clickHouseQueries={localQueryChanges[WIDGET_CLICKHOUSE_QUERY_KEY_NAME]}
@ -256,9 +228,9 @@ function QuerySection({
key={EQueryType.PROM.toString()} key={EQueryType.PROM.toString()}
> >
<PromQLQueryContainer <PromQLQueryContainer
key={rctTabKey.PROMQL} key={rctTabKey.PROM}
queryData={localQueryChanges} queryData={localQueryChanges}
updateQueryData={({ updatedQuery }) => { updateQueryData={({ updatedQuery }: IHandleUpdatedQuery): void => {
handleLocalQueryUpdate({ updatedQuery }); handleLocalQueryUpdate({ updatedQuery });
}} }}
promQLQueries={localQueryChanges[WIDGET_PROMQL_QUERY_KEY_NAME]} promQLQueries={localQueryChanges[WIDGET_PROMQL_QUERY_KEY_NAME]}

View File

@ -1,3 +1,5 @@
import { Query } from 'types/api/dashboard/getAll';
export type TQueryCategories = 'query_builder' | 'clickhouse_query' | 'promql'; export type TQueryCategories = 'query_builder' | 'clickhouse_query' | 'promql';
export enum EQueryCategories { export enum EQueryCategories {
@ -11,3 +13,7 @@ export enum EQueryTypeToQueryKeyMapping {
CLICKHOUSE = 'clickHouse', CLICKHOUSE = 'clickHouse',
PROM = 'promQL', PROM = 'promQL',
} }
export interface IHandleUpdatedQuery {
updatedQuery: Query;
}

View File

@ -2,6 +2,10 @@ import { EQueryType } from 'types/common/dashboard';
import { EQueryTypeToQueryKeyMapping } from '../types'; import { EQueryTypeToQueryKeyMapping } from '../types';
export const getQueryKey = (queryCategory): EQueryTypeToQueryKeyMapping => { export const getQueryKey = (
return EQueryTypeToQueryKeyMapping[EQueryType[queryCategory] as unknown]; queryCategory: EQueryType,
): EQueryTypeToQueryKeyMapping => {
return EQueryTypeToQueryKeyMapping[
EQueryType[queryCategory] as keyof typeof EQueryTypeToQueryKeyMapping
];
}; };

View File

@ -4,7 +4,7 @@ import { EQueryType } from 'types/common/dashboard';
import { Tag } from '../styles'; import { Tag } from '../styles';
interface IQueryTypeTagProps { interface IQueryTypeTagProps {
queryType: EQueryType; queryType: EQueryType | undefined;
} }
function QueryTypeTag({ queryType }: IQueryTypeTagProps): JSX.Element { function QueryTypeTag({ queryType }: IQueryTypeTagProps): JSX.Element {
switch (queryType) { switch (queryType) {

View File

@ -1,8 +1,13 @@
import React from 'react'; import React from 'react';
import { EQueryType } from 'types/common/dashboard';
import QueryTypeTag from '../QueryTypeTag'; import QueryTypeTag from '../QueryTypeTag';
function PlotTag({ queryType }): JSX.Element | null { interface IPlotTagProps {
queryType: EQueryType;
}
function PlotTag({ queryType }: IPlotTagProps): JSX.Element | null {
if (queryType === undefined) { if (queryType === undefined) {
return null; return null;
} }

View File

@ -41,8 +41,7 @@ function WidgetGraph({
</NotFoundContainer> </NotFoundContainer>
); );
} }
if (queryData.data.queryData.length === 0) {
if (queryData.data.length === 0 || queryData.data[0].queryData.length === 0) {
return ( return (
<NotFoundContainer> <NotFoundContainer>
<Typography>No Data</Typography> <Typography>No Data</Typography>
@ -51,7 +50,7 @@ function WidgetGraph({
} }
const chartDataSet = getChartData({ const chartDataSet = getChartData({
queryData: queryData.data, queryData: [queryData.data],
}); });
return ( return (

View File

@ -136,9 +136,9 @@ function NewWidget({
}, [dashboardId, dispatch]); }, [dashboardId, dispatch]);
const getQueryResult = useCallback(() => { const getQueryResult = useCallback(() => {
if (selectedWidget?.id.length !== 0) { if (selectedWidget?.id.length !== 0 && selectedWidget?.query) {
getQueryResults({ getQueryResults({
query: selectedWidget?.query || [], query: selectedWidget?.query,
selectedTime: selectedTime.enum, selectedTime: selectedTime.enum,
widgetId: selectedWidget?.id || '', widgetId: selectedWidget?.id || '',
graphType: selectedGraph, graphType: selectedGraph,

View File

@ -6,30 +6,31 @@ import convertIntoEpoc from './covertIntoEpoc';
import { colors } from './getRandomColor'; import { colors } from './getRandomColor';
const getChartData = ({ queryData }: GetChartDataProps): ChartData => { const getChartData = ({ queryData }: GetChartDataProps): ChartData => {
const response = queryData.map(({ queryData }) => { const response = queryData.map(
return queryData.map((e) => { ({ queryData, query: queryG, legend: legendG }) => {
const { values = [], metric, legend, queryName } = e || {}; return queryData.map((e) => {
const { values = [], metric, legend, queryName } = e || {};
const labelNames = getLabelName(
metric,
queryName || queryG || '', // query
legend || legendG || '',
);
const dataValue = values?.map((e) => {
const [first = 0, second = ''] = e || [];
return {
first: new Date(parseInt(convertIntoEpoc(first * 1000), 10)), // converting in ms
second: Number(parseFloat(second)),
};
});
const labelNames = getLabelName(
metric,
queryName || '', // query
legend || '',
);
const dataValue = values?.map((e) => {
const [first = 0, second = ''] = e || [];
return { return {
first: new Date(parseInt(convertIntoEpoc(first * 1000), 10)), // converting in ms label: labelNames !== 'undefined' ? labelNames : '',
second: Number(parseFloat(second)), first: dataValue.map((e) => e.first),
second: dataValue.map((e) => e.second),
}; };
}); });
},
return { );
label: labelNames !== 'undefined' ? labelNames : '',
first: dataValue.map((e) => e.first),
second: dataValue.map((e) => e.second),
};
});
});
const allLabels = response const allLabels = response
.map((e) => e.map((e) => e.label)) .map((e) => e.map((e) => e.label))
.reduce((a, b) => [...a, ...b], []); .reduce((a, b) => [...a, ...b], []);
@ -58,7 +59,7 @@ const getChartData = ({ queryData }: GetChartDataProps): ChartData => {
}; };
interface GetChartDataProps { interface GetChartDataProps {
queryData: Widgets['queryData']['data']; queryData: Widgets['queryData']['data'][];
} }
export default getChartData; export default getChartData;

View File

@ -2,13 +2,16 @@ import { sortBy } from 'lodash-es';
const MAX_QUERIES = 20; const MAX_QUERIES = 20;
function GetFormulaName(formulas = []): string | null { function GetFormulaName(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
formulas: { name: string; [key: string]: any }[] = [],
): string | null {
if (!formulas.length) return 'F1'; if (!formulas.length) return 'F1';
if (formulas.length === MAX_QUERIES) { if (formulas.length === MAX_QUERIES) {
return null; return null;
} }
const formulasNameNumbered = sortBy( const formulasNameNumbered = sortBy(
formulas.map(({ name }) => { formulas.map(({ name }: { name: string }) => {
return parseInt(name.slice(1), 10); return parseInt(name.slice(1), 10);
}), }),
(e) => e, (e) => e,
@ -22,6 +25,7 @@ function GetFormulaName(formulas = []): string | null {
} }
// formulaIteratorIdx += 1; // formulaIteratorIdx += 1;
} }
return null;
} }
export default GetFormulaName; export default GetFormulaName;

View File

@ -1,26 +1,26 @@
import { sortBy } from 'lodash-es'; import { sortBy } from 'lodash-es';
const MAX_QUERIES = 26; const MAX_QUERIES = 26;
function GetQueryName(queries = []): string | null { function GetQueryName(queries: { name: string }[] = []): string | null {
if (!queries.length) return 'A'; if (!queries.length) return 'A';
if (queries.length === MAX_QUERIES) { if (queries.length === MAX_QUERIES) {
return null; return null;
} }
const sortedQueries = sortBy(queries, function (q) { const sortedQueries = sortBy(queries, (q) => {
return q.name; return q.name;
}); });
let query_iterator_idx = 0; let queryIteratorIdx = 0;
for ( for (
let charItr = 'A'.charCodeAt(0); let charItr = 'A'.charCodeAt(0);
charItr <= 'A'.charCodeAt(0) + MAX_QUERIES; charItr <= 'A'.charCodeAt(0) + MAX_QUERIES;
charItr += 1 charItr += 1
) { ) {
if (charItr !== sortedQueries[query_iterator_idx]?.name.charCodeAt(0)) { if (charItr !== sortedQueries[queryIteratorIdx]?.name.charCodeAt(0)) {
return String.fromCharCode(charItr); return String.fromCharCode(charItr);
} }
query_iterator_idx += 1; queryIteratorIdx += 1;
} }
return null; return null;

View File

@ -1,17 +0,0 @@
import { Dispatch } from 'redux';
import AppActions from 'types/actions';
export const CreateQuery = ({ widgetId }: CreateQueryProps) => (
dispatch: Dispatch<AppActions>,
): void => {
dispatch({
type: 'CREATE_NEW_QUERY',
payload: {
widgetId,
},
});
};
export interface CreateQueryProps {
widgetId: string;
}

View File

@ -46,11 +46,10 @@ export const GetDashboard = ({
title: '', title: '',
queryType: 0, queryType: 0,
queryData: { queryData: {
data: [ data: {
{ queryData: [],
queryData: [], },
},
],
error: false, error: false,
errorMessage: '', errorMessage: '',
loading: false, loading: false,

View File

@ -1,3 +1,7 @@
/* eslint-disable */
// @ts-ignore
// @ts-nocheck
import { getMetricsQueryRange } from 'api/metrics/getQueryRange'; import { getMetricsQueryRange } from 'api/metrics/getQueryRange';
import { AxiosError } from 'axios'; import { AxiosError } from 'axios';
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider'; import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
@ -36,6 +40,7 @@ export async function GetMetricQueryRange({
EQueryTypeToQueryKeyMapping[EQueryType[query.queryType]]; EQueryTypeToQueryKeyMapping[EQueryType[query.queryType]];
const queryData = query[queryKey]; const queryData = query[queryKey];
const legendMap: Record<string, string> = {}; const legendMap: Record<string, string> = {};
const QueryPayload = { const QueryPayload = {
dataSource: EDataSource.METRICS, dataSource: EDataSource.METRICS,
compositeMetricQuery: { compositeMetricQuery: {
@ -175,12 +180,6 @@ export const GetQueryResults = (
return; return;
} }
// const data = response.map((e) => ({
// query: e.query,
// legend: e.legend || '',
// queryData: e.queryData.payload?.result || [],
// }));
dispatch({ dispatch({
type: 'QUERY_SUCCESS', type: 'QUERY_SUCCESS',
payload: { payload: {
@ -216,7 +215,7 @@ export const GetQueryResults = (
export interface GetQueryResultsProps { export interface GetQueryResultsProps {
widgetId: string; widgetId: string;
selectedTime: timePreferenceType; selectedTime: timePreferenceType;
query: Query[]; query: Query;
graphType: ITEMS; graphType: ITEMS;
globalSelectedInterval: GlobalReducer['selectedTime']; globalSelectedInterval: GlobalReducer['selectedTime'];
} }

View File

@ -1,5 +1,4 @@
export * from './applySettingsToPanel'; export * from './applySettingsToPanel';
export * from './createQuery';
export * from './deleteDashboard'; export * from './deleteDashboard';
export * from './deleteQuery'; export * from './deleteQuery';
export * from './getAllDashboard'; export * from './getAllDashboard';

View File

@ -82,7 +82,6 @@ export const SaveDashboard = ({
...allLayout.slice(emptyLayoutIndex + 1, allLayout.length), ...allLayout.slice(emptyLayoutIndex + 1, allLayout.length),
]; ];
}; };
const allLayout = getAllLayout(); const allLayout = getAllLayout();
const response = await updateDashboardApi({ const response = await updateDashboardApi({
data: { data: {
@ -109,12 +108,6 @@ export const SaveDashboard = ({
panelTypes: search.get('graphType') as Widgets['panelTypes'], panelTypes: search.get('graphType') as Widgets['panelTypes'],
queryData: { queryData: {
...selectedWidget.queryData, ...selectedWidget.queryData,
data: [
...selectedWidget.queryData.data.map((e) => ({
...e,
queryData: [],
})),
],
}, },
}, },
...afterWidget, ...afterWidget,

View File

@ -1,21 +0,0 @@
import { Dispatch } from 'redux';
import AppActions from 'types/actions';
export const UpdateQueryType = (
props: UpdateQueryTypeProps,
): ((dispatch: Dispatch<AppActions>) => void) => {
return (dispatch: Dispatch<AppActions>): void => {
dispatch({
type: 'UPDATE_QUERY_TYPE',
payload: {
queryType: props.queryType,
widgetId: props.widgetId,
},
});
};
};
export interface UpdateQueryTypeProps {
widgetId: string;
queryType: number;
}

View File

@ -19,7 +19,6 @@ import {
TOGGLE_EDIT_MODE, TOGGLE_EDIT_MODE,
UPDATE_DASHBOARD, UPDATE_DASHBOARD,
UPDATE_QUERY, UPDATE_QUERY,
UPDATE_QUERY_TYPE,
UPDATE_TITLE_DESCRIPTION_TAGS_SUCCESS, UPDATE_TITLE_DESCRIPTION_TAGS_SUCCESS,
} from 'types/actions/dashboard'; } from 'types/actions/dashboard';
import InitialValueTypes from 'types/reducer/dashboards'; import InitialValueTypes from 'types/reducer/dashboards';
@ -216,6 +215,7 @@ const dashboard = (
case QUERY_SUCCESS: { case QUERY_SUCCESS: {
const { widgetId, data: queryDataResponse } = action.payload; const { widgetId, data: queryDataResponse } = action.payload;
const { dashboards } = state; const { dashboards } = state;
const [selectedDashboard] = dashboards; const [selectedDashboard] = dashboards;
const { data } = selectedDashboard; const { data } = selectedDashboard;
@ -243,7 +243,7 @@ const dashboard = (
{ {
...selectedWidget, ...selectedWidget,
queryData: { queryData: {
data: [queryDataResponse], data: queryDataResponse,
error: selectedWidget.queryData.error, error: selectedWidget.queryData.error,
errorMessage: selectedWidget.queryData.errorMessage, errorMessage: selectedWidget.queryData.errorMessage,
loading: false, loading: false,
@ -397,88 +397,6 @@ const dashboard = (
], ],
}; };
} }
case UPDATE_QUERY_TYPE: {
const { widgetId, queryType } = action.payload;
const { dashboards } = state;
const [selectedDashboard] = dashboards;
const { data } = selectedDashboard;
const { widgets = [] } = data;
const selectedWidgetIndex = widgets.findIndex((e) => e.id === widgetId) || 0;
const preWidget = widgets?.slice(0, selectedWidgetIndex) || [];
const afterWidget =
widgets?.slice(
selectedWidgetIndex + 1, // this is never undefined
widgets.length,
) || [];
const selectedWidget = widgets[selectedWidgetIndex];
return {
...state,
dashboards: [
{
...selectedDashboard,
data: {
...data,
widgets: [
...preWidget,
{
...selectedWidget,
queryType,
},
...afterWidget,
],
},
},
],
};
}
// case DELETE_QUERY: {
// const { currentIndex, widgetId } = action.payload;
// const { dashboards } = state;
// const [selectedDashboard] = dashboards;
// const { data } = selectedDashboard;
// const { widgets = [] } = data;
// const selectedWidgetIndex = widgets.findIndex((e) => e.id === widgetId) || 0;
// const preWidget = widgets?.slice(0, selectedWidgetIndex) || [];
// const afterWidget =
// widgets?.slice(
// selectedWidgetIndex + 1, // this is never undefined
// widgets.length,
// ) || [];
// const selectedWidget = widgets[selectedWidgetIndex];
// const { query } = selectedWidget;
// const preQuery = query.slice(0, currentIndex);
// const postQuery = query.slice(currentIndex + 1, query.length);
// return {
// ...state,
// dashboards: [
// {
// ...selectedDashboard,
// data: {
// ...data,
// widgets: [
// ...preWidget,
// {
// ...selectedWidget,
// query: [...preQuery, ...postQuery],
// },
// ...afterWidget,
// ],
// },
// },
// ],
// };
// }
default: default:
return state; return state;

View File

@ -41,7 +41,6 @@ export const DELETE_WIDGET_ERROR = 'DELETE_WIDGET_ERROR';
export const IS_ADD_WIDGET = 'IS_ADD_WIDGET'; export const IS_ADD_WIDGET = 'IS_ADD_WIDGET';
export const DELETE_QUERY = 'DELETE_QUERY'; export const DELETE_QUERY = 'DELETE_QUERY';
export const UPDATE_QUERY_TYPE = 'UPDATE_QUERY_TYPE';
export const FLUSH_DASHBOARD = 'FLUSH_DASHBOARD'; export const FLUSH_DASHBOARD = 'FLUSH_DASHBOARD';
interface GetDashboard { interface GetDashboard {
type: typeof GET_DASHBOARD; type: typeof GET_DASHBOARD;
@ -124,7 +123,7 @@ export interface QuerySuccessPayload {
// legend: string; // legend: string;
queryData: QueryData[]; queryData: QueryData[];
// query: string // query: string
}[]; };
} }
interface QuerySuccess { interface QuerySuccess {
type: typeof QUERY_SUCCESS; type: typeof QUERY_SUCCESS;
@ -172,14 +171,6 @@ interface DeleteQuery {
payload: DeleteQueryProps; payload: DeleteQueryProps;
} }
interface UpdateQueryType {
type: typeof UPDATE_QUERY_TYPE;
payload: {
queryType: number;
widgetId: string;
};
}
interface FlushDashboard { interface FlushDashboard {
type: typeof FLUSH_DASHBOARD; type: typeof FLUSH_DASHBOARD;
} }
@ -203,5 +194,4 @@ export type DashboardActions =
| IsAddWidget | IsAddWidget
| UpdateQuery | UpdateQuery
| DeleteQuery | DeleteQuery
| UpdateQueryType
| FlushDashboard; | FlushDashboard;

View File

@ -1,7 +1,11 @@
import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider'; import { GRAPH_TYPES } from 'container/NewDashboard/ComponentsSlider';
import { timePreferenceType } from 'container/NewWidget/RightContainer/timeItems'; import { timePreferenceType } from 'container/NewWidget/RightContainer/timeItems';
import { Layout } from 'react-grid-layout'; import { Layout } from 'react-grid-layout';
import { EAggregateOperator, EQueryType } from 'types/common/dashboard'; import {
EAggregateOperator,
EQueryType,
EReduceOperator,
} from 'types/common/dashboard';
import { QueryData } from '../widgets/getQuery'; import { QueryData } from '../widgets/getQuery';
@ -38,10 +42,10 @@ export interface IBaseWidget {
error: boolean; error: boolean;
errorMessage: string; errorMessage: string;
data: { data: {
// legend?: string; query?: string;
legend?: string;
queryData: QueryData[]; queryData: QueryData[];
// query: string; };
}[];
}; };
stepSize?: number; stepSize?: number;
yAxisUnit?: string; yAxisUnit?: string;
@ -74,8 +78,9 @@ export interface IMetricsBuilderQuery {
name: string; name: string;
legend: string; legend: string;
metricName: string | null; metricName: string | null;
groupBy: string[]; groupBy?: string[];
tagFilters: IQueryBuilderTagFilters; tagFilters: IQueryBuilderTagFilters;
reduceTo?: EReduceOperator;
} }
export interface IQueryBuilderTagFilters { export interface IQueryBuilderTagFilters {

View File

@ -1,6 +1,6 @@
import { QueryData } from '../widgets/getQuery'; import { QueryData } from '../widgets/getQuery';
export type MetricsRangeProps = any; export type MetricsRangeProps = never;
export interface MetricRangePayloadProps { export interface MetricRangePayloadProps {
data: { data: {
result: QueryData[]; result: QueryData[];

View File

@ -9,6 +9,7 @@ export interface QueryData {
[key: string]: string; [key: string]: string;
}; };
queryName: string; queryName: string;
legend?: string;
values: [number, string][]; values: [number, string][];
} }