mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 06:58:58 +08:00
feat(builder): add additional filter toggler (#2549)
* feat(builder): add additional filter toggler * feat(builder): add filters deps from another --------- Co-authored-by: Palash Gupta <palashgdev@gmail.com>
This commit is contained in:
parent
9ac2308b89
commit
7c952fd9cd
63
frontend/src/constants/queryBuilder.ts
Normal file
63
frontend/src/constants/queryBuilder.ts
Normal file
@ -0,0 +1,63 @@
|
||||
// ** TODO: use it for creating formula names
|
||||
// import { createNewFormulaName } from 'lib/newQueryBuilder/createNewFormulaName';
|
||||
// ** Helpers
|
||||
import { createNewQueryName } from 'lib/newQueryBuilder/createNewQueryName';
|
||||
import { LocalDataType } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { IBuilderQueryForm } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import {
|
||||
BoolOperators,
|
||||
DataSource,
|
||||
LogsAggregatorOperator,
|
||||
MetricAggregateOperator,
|
||||
NumberOperators,
|
||||
StringOperators,
|
||||
TracesAggregatorOperator,
|
||||
} from 'types/common/queryBuilder';
|
||||
|
||||
export enum QueryBuilderKeys {
|
||||
GET_AGGREGATE_ATTRIBUTE = 'GET_AGGREGATE_ATTRIBUTE',
|
||||
GET_AGGREGATE_KEYS = 'GET_AGGREGATE_KEYS',
|
||||
}
|
||||
|
||||
export const mapOfOperators: Record<DataSource, string[]> = {
|
||||
metrics: Object.values(MetricAggregateOperator),
|
||||
logs: Object.values(LogsAggregatorOperator),
|
||||
traces: Object.values(TracesAggregatorOperator),
|
||||
};
|
||||
|
||||
export const mapOfFilters: Record<DataSource, string[]> = {
|
||||
// eslint-disable-next-line sonarjs/no-duplicate-string
|
||||
metrics: ['Having', 'Aggregation interval'],
|
||||
logs: ['Limit', 'Having', 'Order by', 'Aggregation interval'],
|
||||
traces: ['Limit', 'Having', 'Order by', 'Aggregation interval'],
|
||||
};
|
||||
|
||||
export const initialAggregateAttribute: IBuilderQueryForm['aggregateAttribute'] = {
|
||||
dataType: null,
|
||||
key: '',
|
||||
isColumn: null,
|
||||
type: null,
|
||||
};
|
||||
|
||||
export const initialQueryBuilderFormValues: IBuilderQueryForm = {
|
||||
dataSource: DataSource.METRICS,
|
||||
queryName: createNewQueryName([]),
|
||||
aggregateOperator: Object.values(MetricAggregateOperator)[0],
|
||||
aggregateAttribute: initialAggregateAttribute,
|
||||
tagFilters: [],
|
||||
expression: '',
|
||||
disabled: false,
|
||||
having: [],
|
||||
stepInterval: 30,
|
||||
limit: 10,
|
||||
orderBy: [],
|
||||
groupBy: [],
|
||||
legend: '',
|
||||
reduceTo: '',
|
||||
};
|
||||
|
||||
export const operatorsByTypes: Record<LocalDataType, string[]> = {
|
||||
string: Object.values(StringOperators),
|
||||
number: Object.values(NumberOperators),
|
||||
bool: Object.values(BoolOperators),
|
||||
};
|
@ -1,4 +0,0 @@
|
||||
export enum QueryBuilderKeys {
|
||||
GET_AGGREGATE_ATTRIBUTE = 'GET_AGGREGATE_ATTRIBUTE',
|
||||
GET_AGGREGATE_KEYS = 'GET_AGGREGATE_KEYS',
|
||||
}
|
@ -169,7 +169,7 @@ function QuerySection({
|
||||
/>
|
||||
|
||||
// TODO: uncomment for testing new QueryBuilder
|
||||
// <QueryBuilder />
|
||||
// <QueryBuilder panelType={selectedGraph} />
|
||||
),
|
||||
},
|
||||
{
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { ITEMS } from 'container/NewDashboard/ComponentsSlider/menuItems';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
export type QueryBuilderConfig =
|
||||
@ -9,4 +10,5 @@ export type QueryBuilderConfig =
|
||||
|
||||
export type QueryBuilderProps = {
|
||||
config?: QueryBuilderConfig;
|
||||
panelType?: ITEMS;
|
||||
};
|
||||
|
@ -1,35 +1,87 @@
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import { Button, Col, Row } from 'antd';
|
||||
// ** Hooks
|
||||
import { useQueryBuilder } from 'hooks/useQueryBuilder';
|
||||
import React from 'react';
|
||||
import { MAX_FORMULAS } from 'lib/newQueryBuilder/createNewFormulaName';
|
||||
// ** Constants
|
||||
import { MAX_QUERIES } from 'lib/newQueryBuilder/createNewQueryName';
|
||||
import React, { memo, useEffect, useMemo } from 'react';
|
||||
|
||||
// ** Components
|
||||
import { Query } from './components';
|
||||
// ** Types
|
||||
import { QueryBuilderProps } from './QueryBuilder.interfaces';
|
||||
// ** Styles
|
||||
|
||||
// TODO: I think it can be components switcher, because if we have different views based on the data source, we can render based on source
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export function QueryBuilder({ config }: QueryBuilderProps): JSX.Element {
|
||||
const { queryBuilderData } = useQueryBuilder();
|
||||
export const QueryBuilder = memo(function QueryBuilder({
|
||||
config,
|
||||
panelType,
|
||||
}: QueryBuilderProps): JSX.Element {
|
||||
const {
|
||||
queryBuilderData,
|
||||
setupInitialDataSource,
|
||||
addNewQuery,
|
||||
} = useQueryBuilder();
|
||||
|
||||
// Here we can use Form from antd library and fill context data or edit
|
||||
// Connect form with adding or removing items from the list
|
||||
useEffect(() => {
|
||||
if (config && config.queryVariant === 'static') {
|
||||
setupInitialDataSource(config.initialDataSource);
|
||||
}
|
||||
|
||||
// Here will be map of query queryBuilderData.queryData and queryBuilderData.queryFormulas components
|
||||
// Each component can be part of antd Form list where we can add or remove items
|
||||
// Also need decide to make a copy of queryData for working with form or not and after it set the full new list with formulas or queries to the context
|
||||
// With button to add him
|
||||
return (
|
||||
<div>
|
||||
{queryBuilderData.queryData.map((query, index) => (
|
||||
<Query
|
||||
key={query.queryName}
|
||||
index={index}
|
||||
isAvailableToDisable={queryBuilderData.queryData.length > 1}
|
||||
queryVariant={config?.queryVariant || 'dropdown'}
|
||||
query={query}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
return (): void => {
|
||||
setupInitialDataSource(null);
|
||||
};
|
||||
}, [config, setupInitialDataSource]);
|
||||
|
||||
const isDisabledQueryButton = useMemo(
|
||||
() => queryBuilderData.queryData.length >= MAX_QUERIES,
|
||||
[queryBuilderData],
|
||||
);
|
||||
}
|
||||
|
||||
const isDisabledFormulaButton = useMemo(
|
||||
() => queryBuilderData.queryData.length >= MAX_FORMULAS,
|
||||
[queryBuilderData],
|
||||
);
|
||||
|
||||
return (
|
||||
<Row gutter={[0, 20]} justify="start">
|
||||
<Col span={24}>
|
||||
<Row gutter={[0, 50]}>
|
||||
{queryBuilderData.queryData.map((query, index) => (
|
||||
<Col key={query.queryName} span={24}>
|
||||
<Query
|
||||
index={index}
|
||||
isAvailableToDisable={queryBuilderData.queryData.length > 1}
|
||||
queryVariant={config?.queryVariant || 'dropdown'}
|
||||
query={query}
|
||||
panelType={panelType}
|
||||
/>
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
</Col>
|
||||
|
||||
<Row gutter={[20, 0]}>
|
||||
<Col>
|
||||
<Button
|
||||
disabled={isDisabledQueryButton}
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={addNewQuery}
|
||||
>
|
||||
Query
|
||||
</Button>
|
||||
</Col>
|
||||
<Col>
|
||||
<Button
|
||||
disabled={isDisabledFormulaButton}
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
>
|
||||
Formula
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</Row>
|
||||
);
|
||||
});
|
||||
|
@ -0,0 +1,5 @@
|
||||
import { PropsWithChildren } from 'react';
|
||||
|
||||
export type AdditionalFiltersProps = PropsWithChildren & {
|
||||
listOfAdditionalFilter: string[];
|
||||
};
|
@ -0,0 +1,40 @@
|
||||
import { MinusSquareOutlined, PlusSquareOutlined } from '@ant-design/icons';
|
||||
import { Typography } from 'antd';
|
||||
import styled, { css } from 'styled-components';
|
||||
|
||||
const IconCss = css`
|
||||
margin-right: 0.6875rem;
|
||||
transition: all 0.2s ease;
|
||||
`;
|
||||
|
||||
export const StyledIconOpen = styled(PlusSquareOutlined)`
|
||||
${IconCss}
|
||||
`;
|
||||
|
||||
export const StyledIconClose = styled(MinusSquareOutlined)`
|
||||
${IconCss}
|
||||
`;
|
||||
|
||||
export const StyledWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: fit-content;
|
||||
`;
|
||||
|
||||
export const StyledInner = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 0.875rem;
|
||||
min-height: 1.375rem;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
${StyledIconOpen}, ${StyledIconClose} {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const StyledLink = styled(Typography.Link)`
|
||||
pointer-events: none;
|
||||
`;
|
@ -0,0 +1,52 @@
|
||||
import { Row } from 'antd';
|
||||
import React, { Fragment, memo, ReactNode, useState } from 'react';
|
||||
|
||||
// ** Types
|
||||
import { AdditionalFiltersProps } from './AdditionalFiltersToggler.interfaces';
|
||||
// ** Styles
|
||||
import {
|
||||
StyledIconClose,
|
||||
StyledIconOpen,
|
||||
StyledInner,
|
||||
StyledLink,
|
||||
StyledWrapper,
|
||||
} from './AdditionalFiltersToggler.styled';
|
||||
|
||||
export const AdditionalFiltersToggler = memo(function AdditionalFiltersToggler({
|
||||
children,
|
||||
listOfAdditionalFilter,
|
||||
}: AdditionalFiltersProps): JSX.Element {
|
||||
const [isOpenedFilters, setIsOpenedFilters] = useState<boolean>(false);
|
||||
|
||||
const handleToggleOpenFilters = (): void => {
|
||||
setIsOpenedFilters((prevState) => !prevState);
|
||||
};
|
||||
|
||||
const filtersTexts: ReactNode = listOfAdditionalFilter.map((str, index) => {
|
||||
const isNextLast = index + 1 === listOfAdditionalFilter.length - 1;
|
||||
if (index === listOfAdditionalFilter.length - 1) {
|
||||
return (
|
||||
<Fragment key={str}>
|
||||
and <StyledLink>{str.toUpperCase()}</StyledLink>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<span key={str}>
|
||||
<StyledLink>{str.toUpperCase()}</StyledLink>
|
||||
{isNextLast ? ' ' : ', '}
|
||||
</span>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<StyledInner onClick={handleToggleOpenFilters}>
|
||||
{isOpenedFilters ? <StyledIconClose /> : <StyledIconOpen />}
|
||||
{!isOpenedFilters && <span>Add conditions for {filtersTexts}</span>}
|
||||
</StyledInner>
|
||||
{isOpenedFilters && <Row>{children}</Row>}
|
||||
</StyledWrapper>
|
||||
);
|
||||
});
|
@ -0,0 +1 @@
|
||||
export { AdditionalFiltersToggler } from './AdditionalFiltersToggler';
|
@ -1,5 +1,5 @@
|
||||
import { Select } from 'antd';
|
||||
import React from 'react';
|
||||
import React, { memo } from 'react';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
import { SelectOption } from 'types/common/select';
|
||||
// ** Helpers
|
||||
@ -10,7 +10,9 @@ import { QueryLabelProps } from './DataSourceDropdown.interfaces';
|
||||
|
||||
const dataSourceMap = [DataSource.LOGS, DataSource.METRICS, DataSource.TRACES];
|
||||
|
||||
export function DataSourceDropdown(props: QueryLabelProps): JSX.Element {
|
||||
export const DataSourceDropdown = memo(function DataSourceDropdown(
|
||||
props: QueryLabelProps,
|
||||
): JSX.Element {
|
||||
const { onChange, value, style } = props;
|
||||
|
||||
const dataSourceOptions: SelectOption<
|
||||
@ -30,4 +32,4 @@ export function DataSourceDropdown(props: QueryLabelProps): JSX.Element {
|
||||
style={style}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -5,6 +5,7 @@ export const StyledLabel = styled.div`
|
||||
width: fit-content;
|
||||
min-height: 2rem;
|
||||
display: inline-flex;
|
||||
white-space: nowrap;
|
||||
align-items: center;
|
||||
border-radius: 0.125rem;
|
||||
border: 0.0625rem solid #434343;
|
||||
|
@ -1,10 +1,12 @@
|
||||
import React from 'react';
|
||||
import React, { memo } from 'react';
|
||||
|
||||
// ** Types
|
||||
import { FilterLabelProps } from './FilterLabel.interfaces';
|
||||
// ** Styles
|
||||
import { StyledLabel } from './FilterLabel.styled';
|
||||
|
||||
export function FilterLabel({ label }: FilterLabelProps): JSX.Element {
|
||||
export const FilterLabel = memo(function FilterLabel({
|
||||
label,
|
||||
}: FilterLabelProps): JSX.Element {
|
||||
return <StyledLabel>{label}</StyledLabel>;
|
||||
}
|
||||
});
|
||||
|
@ -1,12 +1,13 @@
|
||||
import { Button } from 'antd';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const StyledButton = styled(Button)<{ isAvailableToDisable: boolean }>`
|
||||
export const StyledButton = styled(Button)<{ $isAvailableToDisable: boolean }>`
|
||||
min-width: 2rem;
|
||||
height: 2.25rem;
|
||||
padding: 0.125rem;
|
||||
padding: ${(props): string =>
|
||||
props.$isAvailableToDisable ? '0.43rem' : '0.43rem 0.68rem'};
|
||||
border-radius: 0.375rem;
|
||||
margin-right: 0.1rem;
|
||||
pointer-events: ${(props): string =>
|
||||
props.isAvailableToDisable ? 'default' : 'none'};
|
||||
props.$isAvailableToDisable ? 'default' : 'none'};
|
||||
`;
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { EyeFilled, EyeInvisibleFilled } from '@ant-design/icons';
|
||||
import { ButtonProps } from 'antd';
|
||||
import React from 'react';
|
||||
import React, { memo } from 'react';
|
||||
|
||||
// ** Types
|
||||
import { ListMarkerProps } from './ListMarker.interfaces';
|
||||
// ** Styles
|
||||
import { StyledButton } from './ListMarker.styled';
|
||||
|
||||
export function ListMarker({
|
||||
export const ListMarker = memo(function ListMarker({
|
||||
isDisabled,
|
||||
labelName,
|
||||
index,
|
||||
@ -30,10 +30,10 @@ export function ListMarker({
|
||||
icon={buttonProps.icon}
|
||||
onClick={buttonProps.onClick}
|
||||
className={className}
|
||||
isAvailableToDisable={isAvailableToDisable}
|
||||
$isAvailableToDisable={isAvailableToDisable}
|
||||
style={style}
|
||||
>
|
||||
{labelName}
|
||||
</StyledButton>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { ITEMS } from 'container/NewDashboard/ComponentsSlider/menuItems';
|
||||
import { IBuilderQueryForm } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
export type QueryProps = {
|
||||
@ -5,4 +6,5 @@ export type QueryProps = {
|
||||
isAvailableToDisable: boolean;
|
||||
query: IBuilderQueryForm;
|
||||
queryVariant: 'static' | 'dropdown';
|
||||
panelType?: ITEMS;
|
||||
};
|
||||
|
@ -0,0 +1,22 @@
|
||||
import { CloseCircleOutlined } from '@ant-design/icons';
|
||||
import { Row } from 'antd';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const StyledDeleteEntity = styled(CloseCircleOutlined)`
|
||||
position: absolute;
|
||||
top: 0.9375rem;
|
||||
right: 0.9375rem;
|
||||
z-index: 1;
|
||||
cursor: pointer;
|
||||
opacity: 0.45;
|
||||
width: 1.3125rem;
|
||||
height: 1.3125rem;
|
||||
svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
export const StyledRow = styled(Row)`
|
||||
padding-right: 3rem;
|
||||
`;
|
@ -1,7 +1,14 @@
|
||||
/* eslint-disable react/jsx-props-no-spreading */
|
||||
import { Col, Row } from 'antd';
|
||||
import { Col, Input, Row } from 'antd';
|
||||
// ** Constants
|
||||
import {
|
||||
initialAggregateAttribute,
|
||||
mapOfFilters,
|
||||
mapOfOperators,
|
||||
} from 'constants/queryBuilder';
|
||||
import { initialQueryBuilderFormValues } from 'constants/queryBuilder';
|
||||
// ** Components
|
||||
import {
|
||||
AdditionalFiltersToggler,
|
||||
DataSourceDropdown,
|
||||
FilterLabel,
|
||||
ListMarker,
|
||||
@ -10,64 +17,178 @@ import {
|
||||
AggregatorFilter,
|
||||
GroupByFilter,
|
||||
OperatorsSelect,
|
||||
ReduceToFilter,
|
||||
} from 'container/QueryBuilder/filters';
|
||||
// Context
|
||||
import { useQueryBuilder } from 'hooks/useQueryBuilder';
|
||||
import { findDataTypeOfOperator } from 'lib/query/findDataTypeOfOperator';
|
||||
// ** Hooks
|
||||
import React from 'react';
|
||||
import React, { memo, useCallback, useMemo } from 'react';
|
||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { IBuilderQueryForm } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
// ** Constants
|
||||
import {
|
||||
LogsAggregatorOperator,
|
||||
MetricAggregateOperator,
|
||||
TracesAggregatorOperator,
|
||||
} from 'types/common/queryBuilder';
|
||||
import { transformToUpperCase } from 'utils/transformToUpperCase';
|
||||
|
||||
// ** Types
|
||||
import { QueryProps } from './Query.interfaces';
|
||||
// ** Styles
|
||||
import { StyledDeleteEntity, StyledRow } from './Query.styled';
|
||||
|
||||
const mapOfOperators: Record<DataSource, string[]> = {
|
||||
metrics: Object.values(MetricAggregateOperator),
|
||||
logs: Object.values(LogsAggregatorOperator),
|
||||
traces: Object.values(TracesAggregatorOperator),
|
||||
};
|
||||
|
||||
export function Query({
|
||||
export const Query = memo(function Query({
|
||||
index,
|
||||
isAvailableToDisable,
|
||||
queryVariant,
|
||||
query,
|
||||
panelType,
|
||||
}: QueryProps): JSX.Element {
|
||||
const { handleSetQueryData } = useQueryBuilder();
|
||||
const {
|
||||
handleSetQueryData,
|
||||
removeEntityByIndex,
|
||||
initialDataSource,
|
||||
} = useQueryBuilder();
|
||||
|
||||
const currentListOfOperators = mapOfOperators[query.dataSource];
|
||||
const currentListOfOperators = useMemo(
|
||||
() => mapOfOperators[query.dataSource],
|
||||
[query],
|
||||
);
|
||||
const listOfAdditionalFilters = useMemo(() => mapOfFilters[query.dataSource], [
|
||||
query,
|
||||
]);
|
||||
|
||||
const handleChangeOperator = (value: string): void => {
|
||||
handleSetQueryData(index, { aggregateOperator: value });
|
||||
};
|
||||
const handleChangeOperator = useCallback(
|
||||
(value: string): void => {
|
||||
const aggregateDataType: BaseAutocompleteData['dataType'] =
|
||||
query.aggregateAttribute.dataType;
|
||||
|
||||
const handleChangeDataSource = (nextSource: DataSource): void => {
|
||||
handleSetQueryData(index, { dataSource: nextSource });
|
||||
};
|
||||
const newQuery: IBuilderQueryForm = {
|
||||
...query,
|
||||
aggregateOperator: value,
|
||||
having: [],
|
||||
};
|
||||
|
||||
const handleToggleDisableQuery = (): void => {
|
||||
handleSetQueryData(index, { disabled: !query.disabled });
|
||||
};
|
||||
if (!aggregateDataType || query.dataSource === DataSource.METRICS) {
|
||||
handleSetQueryData(index, newQuery);
|
||||
return;
|
||||
}
|
||||
|
||||
const handleChangeAggregatorAttribute = (
|
||||
value: BaseAutocompleteData,
|
||||
): void => {
|
||||
handleSetQueryData(index, { aggregateAttribute: value });
|
||||
};
|
||||
switch (aggregateDataType) {
|
||||
case 'string':
|
||||
case 'bool': {
|
||||
const typeOfValue = findDataTypeOfOperator(value);
|
||||
|
||||
const handleChangeGroupByKeys = (values: BaseAutocompleteData[]): void => {
|
||||
handleSetQueryData(index, { groupBy: values });
|
||||
};
|
||||
handleSetQueryData(index, {
|
||||
...newQuery,
|
||||
...(typeOfValue === 'number'
|
||||
? { aggregateAttribute: initialAggregateAttribute }
|
||||
: {}),
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
case 'float64':
|
||||
case 'int64': {
|
||||
handleSetQueryData(index, newQuery);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
handleSetQueryData(index, newQuery);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
[index, query, handleSetQueryData],
|
||||
);
|
||||
|
||||
const handleChangeAggregatorAttribute = useCallback(
|
||||
(value: BaseAutocompleteData): void => {
|
||||
const newQuery: IBuilderQueryForm = {
|
||||
...query,
|
||||
aggregateAttribute: value,
|
||||
};
|
||||
|
||||
handleSetQueryData(index, newQuery);
|
||||
},
|
||||
[index, query, handleSetQueryData],
|
||||
);
|
||||
|
||||
const handleChangeDataSource = useCallback(
|
||||
(nextSource: DataSource): void => {
|
||||
let newQuery: IBuilderQueryForm = {
|
||||
...query,
|
||||
dataSource: nextSource,
|
||||
};
|
||||
|
||||
if (nextSource !== query.dataSource) {
|
||||
const initCopy = {
|
||||
...(initialQueryBuilderFormValues as Partial<IBuilderQueryForm>),
|
||||
};
|
||||
delete initCopy.queryName;
|
||||
|
||||
newQuery = {
|
||||
...newQuery,
|
||||
...initCopy,
|
||||
dataSource: initialDataSource || nextSource,
|
||||
aggregateOperator: mapOfOperators[nextSource][0],
|
||||
};
|
||||
}
|
||||
|
||||
handleSetQueryData(index, newQuery);
|
||||
},
|
||||
[index, query, initialDataSource, handleSetQueryData],
|
||||
);
|
||||
|
||||
const handleToggleDisableQuery = useCallback((): void => {
|
||||
const newQuery: IBuilderQueryForm = {
|
||||
...query,
|
||||
disabled: !query.disabled,
|
||||
};
|
||||
|
||||
handleSetQueryData(index, newQuery);
|
||||
}, [index, query, handleSetQueryData]);
|
||||
|
||||
const handleChangeGroupByKeys = useCallback(
|
||||
(values: BaseAutocompleteData[]): void => {
|
||||
const newQuery: IBuilderQueryForm = {
|
||||
...query,
|
||||
groupBy: values,
|
||||
};
|
||||
|
||||
handleSetQueryData(index, newQuery);
|
||||
},
|
||||
[index, query, handleSetQueryData],
|
||||
);
|
||||
|
||||
const handleChangeQueryLegend = useCallback(
|
||||
(e: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
const newQuery: IBuilderQueryForm = {
|
||||
...query,
|
||||
legend: e.target.value,
|
||||
};
|
||||
handleSetQueryData(index, newQuery);
|
||||
},
|
||||
[index, query, handleSetQueryData],
|
||||
);
|
||||
|
||||
const handleChangeReduceTo = useCallback(
|
||||
(value: string): void => {
|
||||
const newQuery: IBuilderQueryForm = {
|
||||
...query,
|
||||
reduceTo: value,
|
||||
};
|
||||
handleSetQueryData(index, newQuery);
|
||||
},
|
||||
[index, query, handleSetQueryData],
|
||||
);
|
||||
|
||||
const handleDeleteQuery = useCallback(() => {
|
||||
removeEntityByIndex('queryData', index);
|
||||
}, [removeEntityByIndex, index]);
|
||||
|
||||
return (
|
||||
<Row gutter={[0, 15]}>
|
||||
<StyledRow gutter={[0, 15]}>
|
||||
<StyledDeleteEntity onClick={handleDeleteQuery} />
|
||||
<Col span={24}>
|
||||
<Row wrap={false} align="middle">
|
||||
<Col span={24}>
|
||||
@ -92,14 +213,14 @@ export function Query({
|
||||
</Col>
|
||||
<Col span={11}>
|
||||
<Row gutter={[11, 5]}>
|
||||
<Col flex="95px">
|
||||
<Col flex="5.93rem">
|
||||
<OperatorsSelect
|
||||
value={query.aggregateOperator || currentListOfOperators[0]}
|
||||
onChange={handleChangeOperator}
|
||||
operators={currentListOfOperators}
|
||||
/>
|
||||
</Col>
|
||||
<Col flex="1 1 200px">
|
||||
<Col flex="1 1 12.5rem">
|
||||
<AggregatorFilter
|
||||
onChange={handleChangeAggregatorAttribute}
|
||||
query={query}
|
||||
@ -109,14 +230,32 @@ export function Query({
|
||||
</Col>
|
||||
<Col span={11} offset={2}>
|
||||
<Row gutter={[11, 5]}>
|
||||
<Col flex="95px">
|
||||
<FilterLabel label="Group by" />
|
||||
<Col flex="5.93rem">
|
||||
<FilterLabel label={panelType === 'VALUE' ? 'Reduce to' : 'Group by'} />
|
||||
</Col>
|
||||
<Col flex="1 1 200px">
|
||||
<GroupByFilter query={query} onChange={handleChangeGroupByKeys} />
|
||||
<Col flex="1 1 12.5rem">
|
||||
{panelType === 'VALUE' ? (
|
||||
<ReduceToFilter query={query} onChange={handleChangeReduceTo} />
|
||||
) : (
|
||||
<GroupByFilter query={query} onChange={handleChangeGroupByKeys} />
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
<Col span={24}>
|
||||
<AdditionalFiltersToggler listOfAdditionalFilter={listOfAdditionalFilters}>
|
||||
{/* TODO: Render filter by Col component */}
|
||||
test additional filter
|
||||
</AdditionalFiltersToggler>
|
||||
</Col>
|
||||
<Row style={{ width: '100%' }}>
|
||||
<Input
|
||||
onChange={handleChangeQueryLegend}
|
||||
size="middle"
|
||||
value={query.legend}
|
||||
addonBefore="Legend Format"
|
||||
/>
|
||||
</Row>
|
||||
</StyledRow>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -1,3 +1,4 @@
|
||||
export { AdditionalFiltersToggler } from './AdditionalFiltersToggler';
|
||||
export { DataSourceDropdown } from './DataSourceDropdown';
|
||||
export { FilterLabel } from './FilterLabel';
|
||||
export { Formula } from './Formula';
|
||||
|
@ -2,8 +2,9 @@
|
||||
import { AutoComplete, Spin } from 'antd';
|
||||
// ** Api
|
||||
import { getAggregateAttribute } from 'api/queryBuilder/getAggregateAttribute';
|
||||
import { initialAggregateAttribute } from 'constants/queryBuilder';
|
||||
import { transformStringWithPrefix } from 'lib/query/transformStringWithPrefix';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import React, { memo, useMemo, useState } from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { SelectOption } from 'types/common/select';
|
||||
import { transformToUpperCase } from 'utils/transformToUpperCase';
|
||||
@ -11,7 +12,7 @@ import { transformToUpperCase } from 'utils/transformToUpperCase';
|
||||
// ** Types
|
||||
import { AgregatorFilterProps } from './AggregatorFilter.intefaces';
|
||||
|
||||
export function AggregatorFilter({
|
||||
export const AggregatorFilter = memo(function AggregatorFilter({
|
||||
onChange,
|
||||
query,
|
||||
}: AgregatorFilterProps): JSX.Element {
|
||||
@ -50,7 +51,7 @@ export function AggregatorFilter({
|
||||
const handleChangeAttribute = (value: string): void => {
|
||||
const currentAttributeObj = data?.payload?.attributeKeys?.find(
|
||||
(item) => item.key === value,
|
||||
) || { key: value, type: null, dataType: null, isColumn: null };
|
||||
) || { ...initialAggregateAttribute, key: value };
|
||||
|
||||
onChange(currentAttributeObj);
|
||||
};
|
||||
@ -79,4 +80,4 @@ export function AggregatorFilter({
|
||||
onChange={handleChangeAttribute}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -2,11 +2,11 @@ import { Select, Spin } from 'antd';
|
||||
// ** Api
|
||||
import { getAggregateKeys } from 'api/queryBuilder/getAttributeKeys';
|
||||
// ** Constants
|
||||
import { QueryBuilderKeys } from 'constants/useQueryKeys';
|
||||
import { QueryBuilderKeys } from 'constants/queryBuilder';
|
||||
// ** Components
|
||||
// ** Helpers
|
||||
import { transformStringWithPrefix } from 'lib/query/transformStringWithPrefix';
|
||||
import React, { useState } from 'react';
|
||||
import React, { memo, useState } from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { SelectOption } from 'types/common/select';
|
||||
@ -17,7 +17,7 @@ import {
|
||||
GroupByFilterValue,
|
||||
} from './GroupByFilter.interfaces';
|
||||
|
||||
export function GroupByFilter({
|
||||
export const GroupByFilter = memo(function GroupByFilter({
|
||||
query,
|
||||
onChange,
|
||||
}: GroupByFilterProps): JSX.Element {
|
||||
@ -97,4 +97,4 @@ export function GroupByFilter({
|
||||
onChange={handleChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Select } from 'antd';
|
||||
import React from 'react';
|
||||
import React, { memo } from 'react';
|
||||
// ** Types
|
||||
import { SelectOption } from 'types/common/select';
|
||||
// ** Helpers
|
||||
@ -7,7 +7,7 @@ import { transformToUpperCase } from 'utils/transformToUpperCase';
|
||||
|
||||
import { OperatorsSelectProps } from './OperatorsSelect.interfaces';
|
||||
|
||||
export function OperatorsSelect({
|
||||
export const OperatorsSelect = memo(function OperatorsSelect({
|
||||
operators,
|
||||
value,
|
||||
onChange,
|
||||
@ -30,4 +30,4 @@ export function OperatorsSelect({
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -0,0 +1,7 @@
|
||||
import { SelectProps } from 'antd';
|
||||
import { IBuilderQueryForm } from 'types/api/queryBuilder/queryBuilderData';
|
||||
|
||||
export type ReduceToFilterProps = Omit<SelectProps, 'onChange' | 'value'> & {
|
||||
query: IBuilderQueryForm;
|
||||
onChange: (value: string) => void;
|
||||
};
|
@ -0,0 +1,26 @@
|
||||
import { Select } from 'antd';
|
||||
import React, { memo } from 'react';
|
||||
// ** Types
|
||||
import { EReduceOperator } from 'types/common/queryBuilder';
|
||||
import { SelectOption } from 'types/common/select';
|
||||
|
||||
import { ReduceToFilterProps } from './ReduceToFilter.interfaces';
|
||||
|
||||
export const ReduceToFilter = memo(function ReduceToFilter({
|
||||
query,
|
||||
onChange,
|
||||
}: ReduceToFilterProps): JSX.Element {
|
||||
const options: SelectOption<string, string>[] = Object.values(
|
||||
EReduceOperator,
|
||||
).map((str) => ({ label: str, value: str }));
|
||||
|
||||
return (
|
||||
<Select
|
||||
placeholder="Reduce to"
|
||||
style={{ width: '100%' }}
|
||||
options={options}
|
||||
value={query.reduceTo}
|
||||
onChange={onChange}
|
||||
/>
|
||||
);
|
||||
});
|
@ -0,0 +1 @@
|
||||
export { ReduceToFilter } from './ReduceToFilter';
|
@ -1,3 +1,4 @@
|
||||
export { AggregatorFilter } from './AggregatorFilter';
|
||||
export { GroupByFilter } from './GroupByFilter';
|
||||
export { OperatorsSelect } from './OperatorsSelect';
|
||||
export { ReduceToFilter } from './ReduceToFilter';
|
||||
|
9
frontend/src/lib/newQueryBuilder/createNewFormulaName.ts
Normal file
9
frontend/src/lib/newQueryBuilder/createNewFormulaName.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export const MAX_FORMULAS = 20;
|
||||
|
||||
const currentArray: string[] = Array.from(
|
||||
Array(MAX_FORMULAS),
|
||||
(_, i) => `F${i + 1}`,
|
||||
);
|
||||
|
||||
export const createNewFormulaName = (index: number): string =>
|
||||
currentArray[index];
|
14
frontend/src/lib/newQueryBuilder/createNewQueryName.ts
Normal file
14
frontend/src/lib/newQueryBuilder/createNewQueryName.ts
Normal file
@ -0,0 +1,14 @@
|
||||
export const MAX_QUERIES = 26;
|
||||
|
||||
const alpha: number[] = Array.from(Array(MAX_QUERIES), (_, i) => i + 65);
|
||||
const alphabet: string[] = alpha.map((str) => String.fromCharCode(str));
|
||||
|
||||
export const createNewQueryName = (existNames: string[]): string => {
|
||||
for (let i = 0; i < alphabet.length; i += 1) {
|
||||
if (!existNames.includes(alphabet[i])) {
|
||||
return alphabet[i];
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
22
frontend/src/lib/query/findDataTypeOfOperator.ts
Normal file
22
frontend/src/lib/query/findDataTypeOfOperator.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { operatorsByTypes } from 'constants/queryBuilder';
|
||||
import { LocalDataType } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
|
||||
export const findDataTypeOfOperator = (value: string): LocalDataType | null => {
|
||||
const entries = Object.entries(operatorsByTypes) as [
|
||||
LocalDataType,
|
||||
string[],
|
||||
][];
|
||||
|
||||
for (let i = 0; i < entries.length; i += 1) {
|
||||
for (let j = 0; j < entries[i][1].length; j += 1) {
|
||||
const currentOperator = entries[i][1][j];
|
||||
const type = entries[i][0];
|
||||
|
||||
if (currentOperator === value) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
@ -1,4 +1,11 @@
|
||||
// ** Helpers
|
||||
// ** Constants
|
||||
import { initialQueryBuilderFormValues } from 'constants/queryBuilder';
|
||||
import { mapOfOperators } from 'constants/queryBuilder';
|
||||
import {
|
||||
createNewQueryName,
|
||||
MAX_QUERIES,
|
||||
} from 'lib/newQueryBuilder/createNewQueryName';
|
||||
import React, {
|
||||
createContext,
|
||||
PropsWithChildren,
|
||||
@ -14,17 +21,20 @@ import {
|
||||
} from 'types/api/queryBuilder/queryBuilderData';
|
||||
import {
|
||||
DataSource,
|
||||
MetricAggregateOperator,
|
||||
QueryBuilderContextType,
|
||||
QueryBuilderData,
|
||||
} from 'types/common/queryBuilder';
|
||||
|
||||
export const QueryBuilderContext = createContext<QueryBuilderContextType>({
|
||||
queryBuilderData: { queryData: [], queryFormulas: [] },
|
||||
initialDataSource: null,
|
||||
resetQueryBuilderData: () => {},
|
||||
handleSetQueryData: () => {},
|
||||
handleSetFormulaData: () => {},
|
||||
initQueryBuilderData: () => {},
|
||||
setupInitialDataSource: () => {},
|
||||
removeEntityByIndex: () => {},
|
||||
addNewQuery: () => {},
|
||||
});
|
||||
|
||||
const initialQueryBuilderData: QueryBuilderData = {
|
||||
@ -35,27 +45,15 @@ const initialQueryBuilderData: QueryBuilderData = {
|
||||
export function QueryBuilderProvider({
|
||||
children,
|
||||
}: PropsWithChildren): JSX.Element {
|
||||
// ** TODO: get queryId from url for getting data for query builder
|
||||
// ** TODO: type the params which will be used for request of the data for query builder
|
||||
// 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<DataSource | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
// TODO: when initialDataSource will be setuped, on create button initial dataSource will from initialDataSource
|
||||
const [queryBuilderData, setQueryBuilderData] = useState<QueryBuilderData>({
|
||||
// ** TODO temporary initial value for first query for testing first filters
|
||||
queryData: [
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
{
|
||||
dataSource: DataSource.METRICS,
|
||||
queryName: 'A',
|
||||
aggregateOperator: Object.values(MetricAggregateOperator)[0],
|
||||
aggregateAttribute: {
|
||||
dataType: null,
|
||||
key: '',
|
||||
isColumn: null,
|
||||
type: null,
|
||||
},
|
||||
groupBy: [],
|
||||
},
|
||||
],
|
||||
queryData: [],
|
||||
queryFormulas: [],
|
||||
});
|
||||
|
||||
@ -64,7 +62,8 @@ export function QueryBuilderProvider({
|
||||
setQueryBuilderData(initialQueryBuilderData);
|
||||
}, []);
|
||||
|
||||
// ** Method for setupping query builder data
|
||||
// ** Method for setuping query builder data
|
||||
// ** Before setuping transform data from backend to frontend format
|
||||
const initQueryBuilderData = useCallback(
|
||||
(queryBuilderData: QueryBuilderData): void => {
|
||||
setQueryBuilderData(queryBuilderData);
|
||||
@ -72,44 +71,112 @@ export function QueryBuilderProvider({
|
||||
[],
|
||||
);
|
||||
|
||||
const handleSetQueryData = useCallback(
|
||||
(index: number, newQueryData: Partial<IBuilderQueryForm>): void => {
|
||||
const updatedQueryBuilderData = queryBuilderData.queryData.map((item, idx) =>
|
||||
index === idx ? { ...item, ...newQueryData } : item,
|
||||
);
|
||||
|
||||
setQueryBuilderData((prevState) => ({
|
||||
...prevState,
|
||||
queryData: updatedQueryBuilderData,
|
||||
}));
|
||||
const removeEntityByIndex = useCallback(
|
||||
(type: keyof QueryBuilderData, index: number) => {
|
||||
setQueryBuilderData((prevState) => {
|
||||
const currentArray: (IBuilderQueryForm | IBuilderFormula)[] =
|
||||
prevState[type];
|
||||
return {
|
||||
...prevState,
|
||||
[type]: currentArray.filter((item, i) => index !== i),
|
||||
};
|
||||
});
|
||||
},
|
||||
[queryBuilderData],
|
||||
[],
|
||||
);
|
||||
|
||||
const createNewQuery = useCallback(
|
||||
(queries: IBuilderQueryForm[]): IBuilderQueryForm => {
|
||||
const existNames = queries.map((item) => item.queryName);
|
||||
|
||||
const newQuery: IBuilderQueryForm = {
|
||||
...initialQueryBuilderFormValues,
|
||||
queryName: createNewQueryName(existNames),
|
||||
...(initialDataSource
|
||||
? {
|
||||
dataSource: initialDataSource,
|
||||
aggregateOperator: mapOfOperators[initialDataSource][0],
|
||||
expression: createNewQueryName(existNames),
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
|
||||
return newQuery;
|
||||
},
|
||||
[initialDataSource],
|
||||
);
|
||||
|
||||
const addNewQuery = useCallback(() => {
|
||||
setQueryBuilderData((prevState) => {
|
||||
if (prevState.queryData.length >= MAX_QUERIES) return prevState;
|
||||
|
||||
const newQuery = createNewQuery(prevState.queryData);
|
||||
|
||||
return { ...prevState, queryData: [...prevState.queryData, newQuery] };
|
||||
});
|
||||
}, [createNewQuery]);
|
||||
|
||||
const setupInitialDataSource = useCallback(
|
||||
(newInitialDataSource: DataSource | null) =>
|
||||
setInitialDataSource(newInitialDataSource),
|
||||
[],
|
||||
);
|
||||
|
||||
const updateQueryBuilderData = useCallback(
|
||||
(
|
||||
queries: IBuilderQueryForm[],
|
||||
index: number,
|
||||
newQueryData: IBuilderQueryForm,
|
||||
) => queries.map((item, idx) => (index === idx ? newQueryData : item)),
|
||||
[],
|
||||
);
|
||||
|
||||
const handleSetQueryData = useCallback(
|
||||
(index: number, newQueryData: IBuilderQueryForm): void => {
|
||||
setQueryBuilderData((prevState) => {
|
||||
const updatedQueryBuilderData = updateQueryBuilderData(
|
||||
prevState.queryData,
|
||||
index,
|
||||
newQueryData,
|
||||
);
|
||||
|
||||
return {
|
||||
...prevState,
|
||||
queryData: updatedQueryBuilderData,
|
||||
};
|
||||
});
|
||||
},
|
||||
[updateQueryBuilderData],
|
||||
);
|
||||
const handleSetFormulaData = useCallback(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
(index: number, formulaData: IBuilderFormula): void => {},
|
||||
[],
|
||||
);
|
||||
|
||||
// ** TODO: Discuss with Palash how the state of the queryBuilder and queryFormulas
|
||||
// ** TODO: should be filled from url
|
||||
|
||||
// ** TODO: put these values and setter to the context value
|
||||
console.log(queryBuilderData.queryData);
|
||||
|
||||
const contextValues: QueryBuilderContextType = useMemo(
|
||||
() => ({
|
||||
queryBuilderData,
|
||||
initialDataSource,
|
||||
resetQueryBuilderData,
|
||||
handleSetQueryData,
|
||||
handleSetFormulaData,
|
||||
initQueryBuilderData,
|
||||
setupInitialDataSource,
|
||||
removeEntityByIndex,
|
||||
addNewQuery,
|
||||
}),
|
||||
[
|
||||
queryBuilderData,
|
||||
initialDataSource,
|
||||
resetQueryBuilderData,
|
||||
handleSetQueryData,
|
||||
handleSetFormulaData,
|
||||
initQueryBuilderData,
|
||||
setupInitialDataSource,
|
||||
removeEntityByIndex,
|
||||
addNewQuery,
|
||||
],
|
||||
);
|
||||
|
||||
|
@ -1,5 +1,9 @@
|
||||
export type LocalDataType = 'number' | 'string' | 'bool';
|
||||
|
||||
export type DataType = 'int64' | 'float64' | 'string' | 'bool';
|
||||
|
||||
export interface BaseAutocompleteData {
|
||||
dataType: 'number' | 'string' | 'boolean' | null;
|
||||
dataType: DataType | null;
|
||||
isColumn: boolean | null;
|
||||
key: string;
|
||||
type: 'tag' | 'resource' | null;
|
||||
|
@ -23,6 +23,12 @@ export interface TagFilter {
|
||||
op: string;
|
||||
}
|
||||
|
||||
export interface Having {
|
||||
key: string;
|
||||
value: string;
|
||||
op: string;
|
||||
}
|
||||
|
||||
// Type for query builder
|
||||
export type IBuilderQuery = {
|
||||
queryName: string;
|
||||
@ -33,12 +39,14 @@ export type IBuilderQuery = {
|
||||
groupBy: BaseAutocompleteData[];
|
||||
expression: string;
|
||||
disabled: boolean;
|
||||
having?: string;
|
||||
limit?: number;
|
||||
orderBy?: string[];
|
||||
reduceTo?: string;
|
||||
having: Having[];
|
||||
limit: number;
|
||||
stepInterval: number;
|
||||
orderBy: string[];
|
||||
reduceTo: string;
|
||||
};
|
||||
|
||||
export type IBuilderQueryForm = Omit<IBuilderQuery, 'aggregateAttribute'> & {
|
||||
aggregateAttribute: BaseAutocompleteData;
|
||||
legend: string;
|
||||
};
|
||||
|
@ -9,6 +9,49 @@ export enum DataSource {
|
||||
LOGS = 'logs',
|
||||
}
|
||||
|
||||
export enum StringOperators {
|
||||
NOOP = 'noop',
|
||||
COUNT = 'count',
|
||||
COUNT_DISTINCT = 'count_distinct',
|
||||
}
|
||||
|
||||
export enum NumberOperators {
|
||||
SUM = 'sum',
|
||||
AVG = 'avg',
|
||||
MAX = 'max',
|
||||
MIN = 'min',
|
||||
P05 = 'p05',
|
||||
P10 = 'p10',
|
||||
P20 = 'p20',
|
||||
P25 = 'p25',
|
||||
P50 = 'p50',
|
||||
P75 = 'p75',
|
||||
P90 = 'p90',
|
||||
P95 = 'p95',
|
||||
P99 = 'p99',
|
||||
RATE = 'rate',
|
||||
SUM_RATE = 'sum_rate',
|
||||
AVG_RATE = 'avg_rate',
|
||||
MAX_RATE = 'max_rate',
|
||||
MIN_RATE = 'min_rate',
|
||||
RATE_SUM = 'rate_sum',
|
||||
RATE_AVG = 'rate_avg',
|
||||
RATE_MIN = 'rate_min',
|
||||
RATE_MAX = 'rate_max',
|
||||
HIST_QUANTILE_50 = 'hist_quantile_50',
|
||||
HIST_QUANTILE_75 = 'hist_quantile_75',
|
||||
HIST_QUANTILE_90 = 'hist_quantile_90',
|
||||
HIST_QUANTILE_95 = 'hist_quantile_95',
|
||||
HIST_QUANTILE_99 = 'hist_quantile_99',
|
||||
}
|
||||
|
||||
// TODO: add boolean operators from backend
|
||||
export enum BoolOperators {
|
||||
NOOP = 'noop',
|
||||
COUNT = 'count',
|
||||
COUNT_DISTINCT = 'count_distinct',
|
||||
}
|
||||
|
||||
export enum MetricAggregateOperator {
|
||||
NOOP = 'noop',
|
||||
COUNT = 'count',
|
||||
@ -82,6 +125,14 @@ export enum LogsAggregatorOperator {
|
||||
RATE = 'rate',
|
||||
}
|
||||
|
||||
export enum EReduceOperator {
|
||||
LATEST_OF_VALUES_IN_TIMEFRAME = 'Latest of values in timeframe',
|
||||
'SUM_OF_VALUES_IN_TIMEFRAME' = 'Sum of values in timeframe',
|
||||
'AVERAGE_OF_VALUES_IN_TIMEFRAME' = 'Average of values in timeframe',
|
||||
'MAX_OF_VALUES_IN_TIMEFRAME' = 'Max of values in timeframe',
|
||||
'MIN_OF_VALUES_IN_TIMEFRAME' = 'Min of values in timeframe',
|
||||
}
|
||||
|
||||
export type QueryBuilderData = {
|
||||
queryData: IBuilderQueryForm[];
|
||||
queryFormulas: IBuilderFormula[];
|
||||
@ -90,11 +141,12 @@ export type QueryBuilderData = {
|
||||
// ** TODO: temporary types for context, fix it during development
|
||||
export type QueryBuilderContextType = {
|
||||
queryBuilderData: QueryBuilderData;
|
||||
initialDataSource: DataSource | null;
|
||||
resetQueryBuilderData: () => void;
|
||||
handleSetQueryData: (
|
||||
index: number,
|
||||
queryData: Partial<IBuilderQueryForm>,
|
||||
) => void;
|
||||
handleSetQueryData: (index: number, queryData: IBuilderQueryForm) => void;
|
||||
handleSetFormulaData: (index: number, formulaData: IBuilderFormula) => void;
|
||||
initQueryBuilderData: (queryBuilderData: QueryBuilderData) => void;
|
||||
setupInitialDataSource: (newInitialDataSource: DataSource | null) => void;
|
||||
removeEntityByIndex: (type: keyof QueryBuilderData, index: number) => void;
|
||||
addNewQuery: () => void;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user