mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 02:29:03 +08:00
feat: tag filtering frontend changes (#2116)
feat: tag filtering frontend changes
This commit is contained in:
parent
ba6818f487
commit
05ce03e67d
@ -12,7 +12,9 @@ const getSpans = async (
|
||||
const updatedSelectedTags = props.selectedTags.map((e) => ({
|
||||
Key: e.Key[0],
|
||||
Operator: e.Operator,
|
||||
Values: e.Values,
|
||||
StringValues: e.StringValues,
|
||||
NumberValues: e.NumberValues,
|
||||
BoolValues: e.BoolValues,
|
||||
}));
|
||||
|
||||
const exclude: string[] = [];
|
||||
|
@ -30,7 +30,9 @@ const getSpanAggregate = async (
|
||||
const updatedSelectedTags = props.selectedTags.map((e) => ({
|
||||
Key: e.Key[0],
|
||||
Operator: e.Operator,
|
||||
Values: e.Values,
|
||||
StringValues: e.StringValues,
|
||||
NumberValues: e.NumberValues,
|
||||
BoolValues: e.BoolValues,
|
||||
}));
|
||||
|
||||
const other = Object.fromEntries(props.selectedFilter);
|
||||
|
@ -11,9 +11,11 @@ const getTagValue = async (
|
||||
const response = await axios.post<PayloadProps>(`/getTagValues`, {
|
||||
start: props.start.toString(),
|
||||
end: props.end.toString(),
|
||||
tagKey: props.tagKey,
|
||||
tagKey: {
|
||||
Key: props.tagKey.Key,
|
||||
Type: props.tagKey.Type,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
|
@ -8,11 +8,11 @@ export const OperatorConversions: Array<{
|
||||
{
|
||||
label: 'IN',
|
||||
metricValue: '=~',
|
||||
traceValue: 'in',
|
||||
traceValue: 'In',
|
||||
},
|
||||
{
|
||||
label: 'Not IN',
|
||||
metricValue: '!~',
|
||||
traceValue: 'not in',
|
||||
traceValue: 'NotIn',
|
||||
},
|
||||
];
|
||||
|
@ -1,13 +1,15 @@
|
||||
import { AutoComplete, AutoCompleteProps, Input, notification } from 'antd';
|
||||
import { AutoComplete, Input } from 'antd';
|
||||
import getTagFilters from 'api/trace/getTagFilter';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { TraceReducer } from 'types/reducer/trace';
|
||||
|
||||
import { getTagKeyOptions, onTagKeySelect } from './utils';
|
||||
|
||||
function TagsKey(props: TagsKeysProps): JSX.Element {
|
||||
const [selectLoading, setSelectLoading] = useState<boolean>(false);
|
||||
const globalTime = useSelector<AppState, GlobalReducer>(
|
||||
(state) => state.globalTime,
|
||||
);
|
||||
@ -18,64 +20,48 @@ function TagsKey(props: TagsKeysProps): JSX.Element {
|
||||
|
||||
const traces = useSelector<AppState, TraceReducer>((state) => state.traces);
|
||||
|
||||
const [options, setOptions] = useState<AutoCompleteProps['options']>([]);
|
||||
const { isLoading, data } = useQuery(
|
||||
[
|
||||
'getTagKeys',
|
||||
globalTime.minTime,
|
||||
globalTime.maxTime,
|
||||
traces.selectedFilter,
|
||||
traces.isFilterExclude,
|
||||
],
|
||||
{
|
||||
queryFn: () =>
|
||||
getTagFilters({
|
||||
start: globalTime.minTime,
|
||||
end: globalTime.maxTime,
|
||||
other: Object.fromEntries(traces.selectedFilter),
|
||||
isFilterExclude: traces.isFilterExclude,
|
||||
}),
|
||||
cacheTime: 120000,
|
||||
},
|
||||
);
|
||||
|
||||
const onSearchHandler = useCallback(async () => {
|
||||
try {
|
||||
setSelectLoading(true);
|
||||
const response = await getTagFilters({
|
||||
start: globalTime.minTime,
|
||||
end: globalTime.maxTime,
|
||||
other: Object.fromEntries(traces.selectedFilter),
|
||||
isFilterExclude: traces.isFilterExclude,
|
||||
});
|
||||
const options = useMemo(() => getTagKeyOptions(data?.payload), [data]);
|
||||
|
||||
if (response.statusCode === 200) {
|
||||
if (response.payload === null) {
|
||||
setOptions([
|
||||
{
|
||||
value: '',
|
||||
label: 'No tags available',
|
||||
},
|
||||
]);
|
||||
} else {
|
||||
setOptions(
|
||||
response.payload.map((e) => ({
|
||||
value: e.tagKeys,
|
||||
label: e.tagKeys,
|
||||
})),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
notification.error({
|
||||
message: response.error || 'Something went wrong',
|
||||
});
|
||||
}
|
||||
setSelectLoading(false);
|
||||
} catch (error) {
|
||||
notification.error({
|
||||
message: 'Something went wrong',
|
||||
});
|
||||
setSelectLoading(false);
|
||||
}
|
||||
}, [globalTime, traces]);
|
||||
|
||||
const counter = useRef(0);
|
||||
|
||||
useEffect(() => {
|
||||
if (counter.current === 0 && selectedKey.length === 0) {
|
||||
counter.current = 1;
|
||||
onSearchHandler();
|
||||
}
|
||||
}, [onSearchHandler, selectedKey.length]);
|
||||
const onSelectHandler = useCallback(
|
||||
(value: unknown) =>
|
||||
onTagKeySelect(
|
||||
value,
|
||||
options,
|
||||
setSelectedKey,
|
||||
setLocalSelectedTags,
|
||||
index,
|
||||
tag,
|
||||
),
|
||||
[index, options, setLocalSelectedTags, tag],
|
||||
);
|
||||
|
||||
return (
|
||||
<AutoComplete
|
||||
dropdownClassName="certain-category-search-dropdown"
|
||||
dropdownMatchSelectWidth={500}
|
||||
style={{ width: '100%' }}
|
||||
value={selectedKey}
|
||||
allowClear
|
||||
disabled={isLoading}
|
||||
notFoundContent="No tags available"
|
||||
showSearch
|
||||
options={options?.map((e) => ({
|
||||
label: e.label?.toString(),
|
||||
@ -85,27 +71,9 @@ function TagsKey(props: TagsKeysProps): JSX.Element {
|
||||
option?.label?.toUpperCase().indexOf(inputValue.toUpperCase()) !== -1
|
||||
}
|
||||
onChange={(e): void => setSelectedKey(e)}
|
||||
onSelect={(value: unknown): void => {
|
||||
if (
|
||||
typeof value === 'string' &&
|
||||
options &&
|
||||
options.find((option) => option.value === value)
|
||||
) {
|
||||
setSelectedKey(value);
|
||||
|
||||
setLocalSelectedTags((tags) => [
|
||||
...tags.slice(0, index),
|
||||
{
|
||||
Key: [value],
|
||||
Operator: tag.Operator,
|
||||
Values: tag.Values,
|
||||
},
|
||||
...tags.slice(index + 1, tags.length),
|
||||
]);
|
||||
}
|
||||
}}
|
||||
onSelect={onSelectHandler}
|
||||
>
|
||||
<Input disabled={selectLoading} placeholder="Please select" />
|
||||
<Input placeholder="Please select" />
|
||||
</AutoComplete>
|
||||
);
|
||||
}
|
||||
|
@ -1,81 +1,169 @@
|
||||
import { Select } from 'antd';
|
||||
import { BaseOptionType } from 'antd/es/select';
|
||||
import getTagValue from 'api/trace/getTagValue';
|
||||
import React, { useState } from 'react';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { TraceReducer } from 'types/reducer/trace';
|
||||
|
||||
import { AutoCompleteComponent } from './styles';
|
||||
import { SelectComponent } from './styles';
|
||||
import {
|
||||
disableTagValue,
|
||||
extractTagKey,
|
||||
extractTagType,
|
||||
getInitialLocalValue,
|
||||
getTagValueOptions,
|
||||
onTagValueChange,
|
||||
selectOptions,
|
||||
TagValueTypes,
|
||||
} from './utils';
|
||||
|
||||
function TagValue(props: TagValueProps): JSX.Element {
|
||||
const { tag, setLocalSelectedTags, index, tagKey } = props;
|
||||
const {
|
||||
Key: selectedKey,
|
||||
Operator: selectedOperator,
|
||||
Values: selectedValues,
|
||||
StringValues: selectedStringValues,
|
||||
NumberValues: selectedNumberValues,
|
||||
BoolValues: selectedBoolValues,
|
||||
} = tag;
|
||||
const [localValue, setLocalValue] = useState<string>(selectedValues[0]);
|
||||
|
||||
const [localTagValue, setLocalTagValue] = useState<TagValueTypes[]>(
|
||||
getInitialLocalValue(
|
||||
selectedNumberValues,
|
||||
selectedBoolValues,
|
||||
selectedStringValues,
|
||||
),
|
||||
);
|
||||
|
||||
const globalReducer = useSelector<AppState, GlobalReducer>(
|
||||
(state) => state.globalTime,
|
||||
);
|
||||
|
||||
const tagType = useMemo(() => extractTagType(tagKey), [tagKey]);
|
||||
|
||||
const { isLoading, data } = useQuery(
|
||||
['tagKey', globalReducer.minTime, globalReducer.maxTime, tagKey],
|
||||
['tagKey', globalReducer.minTime, globalReducer.maxTime, tagKey, tagType],
|
||||
{
|
||||
queryFn: () =>
|
||||
getTagValue({
|
||||
end: globalReducer.maxTime,
|
||||
start: globalReducer.minTime,
|
||||
tagKey,
|
||||
tagKey: {
|
||||
Key: extractTagKey(tagKey),
|
||||
Type: tagType,
|
||||
},
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
<AutoCompleteComponent
|
||||
options={data?.payload?.map((e) => ({
|
||||
label: e.tagValues,
|
||||
value: e.tagValues,
|
||||
}))}
|
||||
allowClear
|
||||
defaultOpen
|
||||
showSearch
|
||||
filterOption={(inputValue, option): boolean =>
|
||||
option?.label.toUpperCase().indexOf(inputValue.toUpperCase()) !== -1
|
||||
const tagValueDisabled = useMemo(
|
||||
() =>
|
||||
disableTagValue(
|
||||
selectedOperator,
|
||||
setLocalTagValue,
|
||||
selectedKey,
|
||||
setLocalSelectedTags,
|
||||
index,
|
||||
),
|
||||
[index, selectedKey, selectedOperator, setLocalSelectedTags],
|
||||
);
|
||||
|
||||
const onSetLocalValue = useCallback(() => {
|
||||
setLocalTagValue([]);
|
||||
}, []);
|
||||
|
||||
const onSelectedHandler = useCallback(
|
||||
(value: unknown) => {
|
||||
if (
|
||||
typeof value === 'number' ||
|
||||
(typeof value === 'string' && !Number.isNaN(Number(value)) && value !== ' ')
|
||||
) {
|
||||
setLocalTagValue([value]);
|
||||
setLocalSelectedTags((tags) => [
|
||||
...tags.slice(0, index),
|
||||
{
|
||||
Key: selectedKey,
|
||||
Operator: selectedOperator,
|
||||
StringValues: [],
|
||||
NumberValues: [Number(value)],
|
||||
BoolValues: [],
|
||||
},
|
||||
...tags.slice(index + 1, tags.length),
|
||||
]);
|
||||
} else if (
|
||||
typeof value === 'boolean' ||
|
||||
value === 'true' ||
|
||||
value === 'false'
|
||||
) {
|
||||
setLocalTagValue([value]);
|
||||
setLocalSelectedTags((tags) => [
|
||||
...tags.slice(0, index),
|
||||
{
|
||||
Key: selectedKey,
|
||||
Operator: selectedOperator,
|
||||
StringValues: [],
|
||||
NumberValues: [],
|
||||
BoolValues: [value === 'true' || value === true],
|
||||
},
|
||||
...tags.slice(index + 1, tags.length),
|
||||
]);
|
||||
} else if (typeof value === 'string') {
|
||||
setLocalTagValue([value]);
|
||||
setLocalSelectedTags((tags) => [
|
||||
...tags.slice(0, index),
|
||||
{
|
||||
Key: selectedKey,
|
||||
Operator: selectedOperator,
|
||||
StringValues: [value],
|
||||
NumberValues: [],
|
||||
BoolValues: [],
|
||||
},
|
||||
...tags.slice(index + 1, tags.length),
|
||||
]);
|
||||
}
|
||||
disabled={isLoading}
|
||||
value={localValue}
|
||||
onChange={(values): void => {
|
||||
if (typeof values === 'string') {
|
||||
setLocalValue(values);
|
||||
}
|
||||
}}
|
||||
onSelect={(value: unknown): void => {
|
||||
if (typeof value === 'string') {
|
||||
setLocalValue(value);
|
||||
setLocalSelectedTags((tags) => [
|
||||
...tags.slice(0, index),
|
||||
{
|
||||
Key: selectedKey,
|
||||
Operator: selectedOperator,
|
||||
Values: [value],
|
||||
},
|
||||
...tags.slice(index + 1, tags.length),
|
||||
]);
|
||||
}
|
||||
}}
|
||||
},
|
||||
[index, selectedKey, selectedOperator, setLocalSelectedTags],
|
||||
);
|
||||
|
||||
const onChangeHandler = useCallback(
|
||||
(value: unknown) => onTagValueChange(value, setLocalTagValue),
|
||||
[],
|
||||
);
|
||||
|
||||
const getFilterOptions = useCallback(
|
||||
(inputValue: string, option?: BaseOptionType): boolean => {
|
||||
if (typeof option?.label === 'string') {
|
||||
return option?.label.toUpperCase().indexOf(inputValue.toUpperCase()) !== -1;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
return (
|
||||
<SelectComponent
|
||||
loading={isLoading}
|
||||
options={getTagValueOptions(data?.payload, tagType)}
|
||||
mode="tags"
|
||||
allowClear
|
||||
onClear={onSetLocalValue}
|
||||
onDeselect={onSetLocalValue}
|
||||
showSearch
|
||||
filterOption={getFilterOptions}
|
||||
disabled={isLoading || tagValueDisabled}
|
||||
value={localTagValue}
|
||||
onChange={onChangeHandler}
|
||||
onSelect={onSelectedHandler}
|
||||
>
|
||||
{data &&
|
||||
data.payload &&
|
||||
data.payload.map((suggestion) => (
|
||||
<Select.Option key={suggestion.tagValues} value={suggestion.tagValues}>
|
||||
{suggestion.tagValues}
|
||||
</Select.Option>
|
||||
))}
|
||||
</AutoCompleteComponent>
|
||||
{selectOptions(data?.payload, tagType)?.map((suggestion) => (
|
||||
<Select.Option key={suggestion.toString()} value={suggestion}>
|
||||
{suggestion}
|
||||
</Select.Option>
|
||||
))}
|
||||
</SelectComponent>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -6,24 +6,90 @@ import { TraceReducer } from 'types/reducer/trace';
|
||||
import { Container, IconContainer, SelectComponent } from './styles';
|
||||
import TagsKey from './TagKey';
|
||||
import TagValue from './TagValue';
|
||||
import { mapOperators } from './utils';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
type Tags = FlatArray<TraceReducer['selectedTags'], 1>['Operator'];
|
||||
|
||||
interface AllMenuProps {
|
||||
const StringBoolNumber = ['string', 'number', 'bool'];
|
||||
const Number = ['number'];
|
||||
const String = ['string'];
|
||||
export interface AllMenuProps {
|
||||
key: Tags | '';
|
||||
value: string;
|
||||
supportedTypes: string[];
|
||||
}
|
||||
|
||||
const AllMenu: AllMenuProps[] = [
|
||||
export const AllMenu: AllMenuProps[] = [
|
||||
{
|
||||
key: 'in',
|
||||
value: 'IN',
|
||||
key: 'Equals',
|
||||
value: 'EQUALS',
|
||||
supportedTypes: StringBoolNumber,
|
||||
},
|
||||
{
|
||||
key: 'not in',
|
||||
key: 'NotEquals',
|
||||
value: 'NOT EQUALS',
|
||||
supportedTypes: StringBoolNumber,
|
||||
},
|
||||
{
|
||||
key: 'In',
|
||||
value: 'IN',
|
||||
supportedTypes: String,
|
||||
},
|
||||
{
|
||||
key: 'NotIn',
|
||||
value: 'NOT IN',
|
||||
supportedTypes: String,
|
||||
},
|
||||
{
|
||||
key: 'Exists',
|
||||
value: 'EXISTS',
|
||||
supportedTypes: StringBoolNumber,
|
||||
},
|
||||
{
|
||||
key: 'NotExists',
|
||||
value: 'NOT EXISTS',
|
||||
supportedTypes: StringBoolNumber,
|
||||
},
|
||||
{
|
||||
key: 'GreaterThan',
|
||||
value: 'GREATER THAN',
|
||||
supportedTypes: Number,
|
||||
},
|
||||
{
|
||||
key: 'LessThan',
|
||||
value: 'LESS THAN',
|
||||
supportedTypes: Number,
|
||||
},
|
||||
{
|
||||
key: 'GreaterThanEquals',
|
||||
value: 'GREATER THAN OR EQUALS',
|
||||
supportedTypes: Number,
|
||||
},
|
||||
{
|
||||
key: 'LessThanEquals',
|
||||
value: 'LESS THAN OR EQUALS',
|
||||
supportedTypes: Number,
|
||||
},
|
||||
{
|
||||
key: 'StartsWith',
|
||||
value: 'STARTS WITH',
|
||||
supportedTypes: String,
|
||||
},
|
||||
{
|
||||
key: 'NotStartsWith',
|
||||
value: 'NOT STARTS WITH',
|
||||
supportedTypes: String,
|
||||
},
|
||||
{
|
||||
key: 'Contains',
|
||||
value: 'CONTAINS',
|
||||
supportedTypes: String,
|
||||
},
|
||||
{
|
||||
key: 'NotContains',
|
||||
value: 'NOT CONTAINS',
|
||||
supportedTypes: String,
|
||||
},
|
||||
];
|
||||
|
||||
@ -38,7 +104,9 @@ function SingleTags(props: AllTagsProps): JSX.Element {
|
||||
const {
|
||||
Key: selectedKey,
|
||||
Operator: selectedOperator,
|
||||
Values: selectedValues,
|
||||
StringValues: selectedStringValues,
|
||||
NumberValues: selectedNumberValues,
|
||||
BoolValues: selectedBoolValues,
|
||||
} = tag;
|
||||
|
||||
const onDeleteTagHandler = (index: number): void => {
|
||||
@ -51,7 +119,9 @@ function SingleTags(props: AllTagsProps): JSX.Element {
|
||||
...localSelectedTags.slice(0, index),
|
||||
{
|
||||
Key: selectedKey,
|
||||
Values: selectedValues,
|
||||
StringValues: selectedStringValues,
|
||||
NumberValues: selectedNumberValues,
|
||||
BoolValues: selectedBoolValues,
|
||||
Operator: key as Tags,
|
||||
},
|
||||
...localSelectedTags.slice(index + 1, localSelectedTags.length),
|
||||
@ -70,11 +140,14 @@ function SingleTags(props: AllTagsProps): JSX.Element {
|
||||
onChange={onChangeOperatorHandler}
|
||||
value={AllMenu.find((e) => e.key === selectedOperator)?.value || ''}
|
||||
>
|
||||
{AllMenu.map((e) => (
|
||||
<Option key={e.value} value={e.key}>
|
||||
{e.value}
|
||||
</Option>
|
||||
))}
|
||||
{
|
||||
// filter out the operator that does not include supported type of the selected key
|
||||
mapOperators(selectedKey).map((e) => (
|
||||
<Option key={e.value} value={e.key}>
|
||||
{e.value}
|
||||
</Option>
|
||||
))
|
||||
}
|
||||
</SelectComponent>
|
||||
|
||||
{selectedKey[0] ? (
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { AutoComplete, Select, Space } from 'antd';
|
||||
import { Select, Space } from 'antd';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const SpaceComponent = styled(Space)`
|
||||
@ -7,12 +7,6 @@ export const SpaceComponent = styled(Space)`
|
||||
}
|
||||
`;
|
||||
|
||||
export const SelectComponent = styled(Select)`
|
||||
&&& {
|
||||
width: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
export const Container = styled(Space)`
|
||||
&&& {
|
||||
display: flex;
|
||||
@ -37,7 +31,7 @@ export const IconContainer = styled.div`
|
||||
margin-left: 1.125rem;
|
||||
`;
|
||||
|
||||
export const AutoCompleteComponent = styled(AutoComplete)`
|
||||
export const SelectComponent = styled(Select)`
|
||||
&&& {
|
||||
width: 100%;
|
||||
}
|
||||
|
204
frontend/src/container/Trace/Search/AllTags/Tag/utils.ts
Normal file
204
frontend/src/container/Trace/Search/AllTags/Tag/utils.ts
Normal file
@ -0,0 +1,204 @@
|
||||
import { AutoCompleteProps } from 'antd';
|
||||
import { DefaultOptionType } from 'antd/es/select';
|
||||
import { PayloadProps as TagKeyPayload } from 'types/api/trace/getTagFilters';
|
||||
import { PayloadProps as TagValuePayload } from 'types/api/trace/getTagValue';
|
||||
import { OperatorValues, Tags } from 'types/reducer/trace';
|
||||
|
||||
import { AllMenu, AllMenuProps } from '.';
|
||||
|
||||
export type TagValueTypes = string | number | boolean;
|
||||
/**
|
||||
* @description extract tag filters from payload
|
||||
*/
|
||||
export const extractTagFilters = (
|
||||
payload: TagKeyPayload,
|
||||
): DefaultOptionType[] => {
|
||||
const tagFilters: string[] = [];
|
||||
payload.stringTagKeys.forEach((element) => {
|
||||
tagFilters.push(`${element}.(string)`);
|
||||
});
|
||||
payload.numberTagKeys.forEach((element) => {
|
||||
tagFilters.push(`${element}.(number)`);
|
||||
});
|
||||
payload.boolTagKeys.forEach((element) => {
|
||||
tagFilters.push(`${element}.(bool)`);
|
||||
});
|
||||
return tagFilters.map((e) => ({
|
||||
value: e,
|
||||
label: e,
|
||||
}));
|
||||
};
|
||||
|
||||
export const extractTagType = (tagKey: string): string => {
|
||||
if (tagKey?.includes('.(string)')) {
|
||||
return 'string';
|
||||
}
|
||||
if (tagKey?.includes('.(number)')) {
|
||||
return 'number';
|
||||
}
|
||||
if (tagKey?.includes('.(bool)')) {
|
||||
return 'bool';
|
||||
}
|
||||
return 'string';
|
||||
};
|
||||
|
||||
export const extractTagKey = (tagKey: string): string => {
|
||||
const tag = tagKey.split('.(');
|
||||
if (tag && tag.length > 0) {
|
||||
return tag[0];
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
export function onTagValueChange(
|
||||
values: unknown,
|
||||
setLocalValue: React.Dispatch<React.SetStateAction<TagValueTypes[]>>,
|
||||
): void {
|
||||
if (Array.isArray(values) && values.length > 0) {
|
||||
if (typeof values[0] === 'number' || typeof values[0] === 'boolean') {
|
||||
setLocalValue(values);
|
||||
} else if (typeof values[0] === 'string') {
|
||||
if (values[0] === 'true' || values[0] === 'false') {
|
||||
setLocalValue([values[0] === 'true']);
|
||||
} else if (values[0] !== ' ' && !Number.isNaN(Number(values[0]))) {
|
||||
setLocalValue([Number(values[0])]);
|
||||
} else {
|
||||
setLocalValue([values[0]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function disableTagValue(
|
||||
selectedOperator: OperatorValues,
|
||||
setLocalValue: React.Dispatch<React.SetStateAction<TagValueTypes[]>>,
|
||||
selectedKeys: string[],
|
||||
setLocalSelectedTags: React.Dispatch<React.SetStateAction<Tags[]>>,
|
||||
index: number,
|
||||
): boolean {
|
||||
if (selectedOperator === 'Exists' || selectedOperator === 'NotExists') {
|
||||
setLocalValue([]);
|
||||
setLocalSelectedTags((tags) => [
|
||||
...tags.slice(0, index),
|
||||
{
|
||||
Key: selectedKeys,
|
||||
Operator: selectedOperator,
|
||||
StringValues: [],
|
||||
NumberValues: [],
|
||||
BoolValues: [],
|
||||
},
|
||||
...tags.slice(index + 1, tags.length),
|
||||
]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function getInitialLocalValue(
|
||||
selectedNumberValues: number[],
|
||||
selectedBoolValues: boolean[],
|
||||
selectedStringValues: string[],
|
||||
): TagValueTypes[] {
|
||||
if (selectedStringValues && selectedStringValues.length > 0) {
|
||||
return selectedStringValues;
|
||||
}
|
||||
if (selectedNumberValues && selectedNumberValues.length > 0) {
|
||||
return selectedNumberValues;
|
||||
}
|
||||
if (selectedBoolValues && selectedBoolValues.length > 0) {
|
||||
return selectedBoolValues;
|
||||
}
|
||||
return selectedStringValues;
|
||||
}
|
||||
|
||||
export function getTagValueOptions(
|
||||
payload: TagValuePayload | null | undefined,
|
||||
tagType: string,
|
||||
): Array<{ label: string; value: TagValueTypes }> | undefined {
|
||||
if (tagType === 'string') {
|
||||
return payload?.stringTagValues?.map((e) => ({
|
||||
label: e,
|
||||
value: e,
|
||||
}));
|
||||
}
|
||||
if (tagType === 'number') {
|
||||
return payload?.numberTagValues?.map((e) => ({
|
||||
label: e.toString(),
|
||||
value: e,
|
||||
}));
|
||||
}
|
||||
if (tagType === 'bool') {
|
||||
return payload?.boolTagValues?.map((e) => ({
|
||||
label: e.toString(),
|
||||
value: e,
|
||||
}));
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
export function getTagKeyOptions(
|
||||
payload: TagKeyPayload | null | undefined,
|
||||
): DefaultOptionType[] {
|
||||
if (payload === null) {
|
||||
return [
|
||||
{
|
||||
value: '',
|
||||
label: 'No tags available',
|
||||
},
|
||||
];
|
||||
}
|
||||
if (payload != null) {
|
||||
return extractTagFilters(payload);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
export function selectOptions(
|
||||
payload: TagValuePayload | null | undefined,
|
||||
tagType: string,
|
||||
): string[] | boolean[] | number[] | undefined {
|
||||
if (tagType === 'string') {
|
||||
return payload?.stringTagValues;
|
||||
}
|
||||
if (tagType === 'number') {
|
||||
return payload?.numberTagValues;
|
||||
}
|
||||
if (tagType === 'bool') {
|
||||
return payload?.boolTagValues;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
export function mapOperators(selectedKey: string[]): AllMenuProps[] {
|
||||
return AllMenu.filter((e) =>
|
||||
e?.supportedTypes?.includes(extractTagType(selectedKey[0])),
|
||||
);
|
||||
}
|
||||
|
||||
export function onTagKeySelect(
|
||||
value: unknown,
|
||||
options: AutoCompleteProps['options'],
|
||||
setSelectedKey: React.Dispatch<React.SetStateAction<string>>,
|
||||
setLocalSelectedTags: React.Dispatch<React.SetStateAction<Tags[]>>,
|
||||
index: number,
|
||||
tag: Tags,
|
||||
): void {
|
||||
if (
|
||||
typeof value === 'string' &&
|
||||
options &&
|
||||
options.find((option) => option.value === value)
|
||||
) {
|
||||
setSelectedKey(value);
|
||||
setLocalSelectedTags((tags) => [
|
||||
...tags.slice(0, index),
|
||||
{
|
||||
Key: [value],
|
||||
Operator: tag.Operator,
|
||||
StringValues: tag.StringValues,
|
||||
NumberValues: tag.NumberValues,
|
||||
BoolValues: tag.BoolValues,
|
||||
},
|
||||
...tags.slice(index + 1, tags.length),
|
||||
]);
|
||||
}
|
||||
}
|
@ -41,8 +41,10 @@ function AllTags({
|
||||
...tags,
|
||||
{
|
||||
Key: [],
|
||||
Operator: 'in',
|
||||
Values: [],
|
||||
Operator: 'Equals',
|
||||
StringValues: [],
|
||||
NumberValues: [],
|
||||
BoolValues: [],
|
||||
},
|
||||
]);
|
||||
};
|
||||
@ -110,7 +112,7 @@ function AllTags({
|
||||
</Button>
|
||||
|
||||
<Text ellipsis>
|
||||
Results will include spans with ALL the specified tags ( Rows are `anded`
|
||||
Results will include spans with ALL the specified tags ( Rows are `ANDed`
|
||||
)
|
||||
</Text>
|
||||
</Space>
|
||||
|
@ -1,5 +1,8 @@
|
||||
import { AllMenu } from 'container/Trace/Search/AllTags/Tag';
|
||||
import { TraceReducer } from 'types/reducer/trace';
|
||||
|
||||
import { extractTagType, TagValueTypes } from './AllTags/Tag/utils';
|
||||
|
||||
type Tags = TraceReducer['selectedTags'];
|
||||
|
||||
interface PayloadProps<T> {
|
||||
@ -7,79 +10,144 @@ interface PayloadProps<T> {
|
||||
payload: T;
|
||||
}
|
||||
|
||||
function extractValues(
|
||||
tagType: string,
|
||||
filters: string[],
|
||||
isError: boolean,
|
||||
): [number[], boolean[], string[], boolean] {
|
||||
const StringValues: string[] = [];
|
||||
const NumberValues: number[] = [];
|
||||
const BoolValues: boolean[] = [];
|
||||
let isErr = isError;
|
||||
if (tagType === 'string') {
|
||||
StringValues.push(...filters);
|
||||
} else if (tagType === 'number') {
|
||||
filters.forEach((element) => {
|
||||
const num = Number(element);
|
||||
isErr = Number.isNaN(num) ? true : isError;
|
||||
NumberValues.push(num);
|
||||
});
|
||||
} else if (tagType === 'bool') {
|
||||
filters.forEach((element) => {
|
||||
if (element === 'true') {
|
||||
BoolValues.push(true);
|
||||
} else if (element === 'false') {
|
||||
BoolValues.push(false);
|
||||
} else {
|
||||
isErr = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
return [NumberValues, BoolValues, StringValues, isErr];
|
||||
}
|
||||
|
||||
export const parseQueryToTags = (query: string): PayloadProps<Tags> => {
|
||||
let isError = false;
|
||||
|
||||
// Split the query string by ' AND '
|
||||
const noOfTags = query.split(' AND ');
|
||||
|
||||
// Map over each tag
|
||||
const tags: Tags = noOfTags.map((filter) => {
|
||||
const isInPresent = filter.includes('in');
|
||||
const isNotInPresent = filter.includes('not in');
|
||||
// Find the operator used in the filter
|
||||
const operator =
|
||||
AllMenu.find((e) => `${filter} `.includes(` ${e.key} `))?.key || '';
|
||||
|
||||
if (!isNotInPresent && !isInPresent) {
|
||||
isError = true;
|
||||
// Split the filter by the operator
|
||||
const [tagName, tagValues] = filter.split(operator).map((e) => e.trim());
|
||||
|
||||
// If the operator is Exists or NotExists, then return the tag object without values
|
||||
if (operator === 'Exists' || operator === 'NotExists') {
|
||||
return {
|
||||
Key: [tagName],
|
||||
StringValues: [],
|
||||
NumberValues: [],
|
||||
BoolValues: [],
|
||||
Operator: operator as FlatArray<Tags, 1>['Operator'],
|
||||
};
|
||||
}
|
||||
// Check for errors in the filter
|
||||
isError = operator.length === 0 || !tagName || !tagValues ? true : isError;
|
||||
|
||||
const isPresentSplit = isInPresent ? 'in' : '';
|
||||
// Remove the first and last brackets from the tagValues
|
||||
const formattedTagValues = tagValues.slice(1, -1);
|
||||
|
||||
const splitBy = isNotInPresent ? 'not in' : isPresentSplit;
|
||||
|
||||
if (splitBy.length === 0) {
|
||||
isError = true;
|
||||
}
|
||||
|
||||
const filteredtags = filter.split(splitBy).map((e) => e.trim());
|
||||
|
||||
if (filteredtags.length !== 2) {
|
||||
isError = true;
|
||||
}
|
||||
|
||||
const filterForTags = filteredtags[1];
|
||||
|
||||
if (!filterForTags) {
|
||||
isError = true;
|
||||
}
|
||||
|
||||
const removingFirstAndLastBrackets = `${filterForTags?.slice(1, -1)}`;
|
||||
|
||||
const noofFilters = removingFirstAndLastBrackets
|
||||
// Split the tagValues by ',' and remove any quotes
|
||||
const filters = formattedTagValues
|
||||
.split(',')
|
||||
.map((e) => e.replaceAll(/"/g, ''));
|
||||
|
||||
noofFilters.forEach((e) => {
|
||||
// Check for errors in the filters
|
||||
filters.forEach((e) => {
|
||||
const firstChar = e.charAt(0);
|
||||
const lastChar = e.charAt(e.length - 1);
|
||||
|
||||
if (firstChar === '"' && lastChar === '"') {
|
||||
isError = true;
|
||||
}
|
||||
isError = firstChar === '"' && lastChar === '"' ? true : isError;
|
||||
});
|
||||
|
||||
// Extract the tag type
|
||||
const tagType = extractTagType(tagName);
|
||||
|
||||
// Extract the values for the tag
|
||||
const [NumberValues, BoolValues, StringValues, isErr] = extractValues(
|
||||
tagType,
|
||||
filters,
|
||||
isError,
|
||||
);
|
||||
isError = isErr;
|
||||
|
||||
// Return the tag object
|
||||
return {
|
||||
Key: [filteredtags[0]],
|
||||
Values: noofFilters,
|
||||
Operator: splitBy as FlatArray<Tags, 1>['Operator'],
|
||||
Key: [tagName],
|
||||
StringValues,
|
||||
NumberValues,
|
||||
BoolValues,
|
||||
Operator: operator as FlatArray<Tags, 1>['Operator'],
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
isError,
|
||||
payload: tags,
|
||||
};
|
||||
};
|
||||
|
||||
const formatValues = (values: TagValueTypes[]): string =>
|
||||
values.map((e) => `"${e.toString().replaceAll(/"/g, '')}"`).join(',');
|
||||
|
||||
export const parseTagsToQuery = (tags: Tags): PayloadProps<string> => {
|
||||
let isError = false;
|
||||
|
||||
// Map over each tag
|
||||
const payload = tags
|
||||
.map(({ Values, Key, Operator }) => {
|
||||
if (Key[0] === undefined) {
|
||||
.map(({ StringValues, NumberValues, BoolValues, Key, Operator }) => {
|
||||
// Check if the key of the tag is undefined
|
||||
if (!Key[0]) {
|
||||
isError = true;
|
||||
}
|
||||
if (Operator === 'Exists' || Operator === 'NotExists') {
|
||||
return `${Key[0]} ${Operator}`;
|
||||
}
|
||||
// Check if the tag has string values
|
||||
if (StringValues.length > 0) {
|
||||
// Format the string values and join them with a ','
|
||||
const formattedStringValues = formatValues(StringValues);
|
||||
return `${Key[0]} ${Operator} (${formattedStringValues})`;
|
||||
}
|
||||
|
||||
return `${Key[0]} ${Operator} (${Values.map(
|
||||
(e) => `"${e.replaceAll(/"/g, '')}"`,
|
||||
).join(',')})`;
|
||||
// Check if the tag has number values
|
||||
if (NumberValues.length > 0) {
|
||||
// Format the number values and join them with a ','
|
||||
const formattedNumberValues = formatValues(NumberValues);
|
||||
return `${Key[0]} ${Operator} (${formattedNumberValues})`;
|
||||
}
|
||||
|
||||
// Check if the tag has boolean values
|
||||
if (BoolValues.length > 0) {
|
||||
// Format the boolean values and join them with a ','
|
||||
const formattedBoolValues = formatValues(BoolValues);
|
||||
return `${Key[0]} ${Operator} (${formattedBoolValues})`;
|
||||
}
|
||||
|
||||
return '';
|
||||
})
|
||||
.join(' AND ');
|
||||
|
||||
|
@ -1,70 +1,72 @@
|
||||
import { DefaultOptionType } from 'antd/es/select';
|
||||
|
||||
interface Dropdown {
|
||||
key: string;
|
||||
displayValue: string;
|
||||
yAxisUnit?: string;
|
||||
}
|
||||
|
||||
export const groupBy: Dropdown[] = [
|
||||
export const groupBy: DefaultOptionType[] = [
|
||||
{
|
||||
key: '',
|
||||
displayValue: 'None',
|
||||
label: 'None',
|
||||
value: '',
|
||||
},
|
||||
{
|
||||
key: 'serviceName',
|
||||
displayValue: 'Service Name',
|
||||
label: 'Service Name',
|
||||
value: 'serviceName',
|
||||
},
|
||||
|
||||
{
|
||||
displayValue: 'Operation',
|
||||
key: 'operation',
|
||||
label: 'Operation',
|
||||
value: 'name',
|
||||
},
|
||||
{
|
||||
displayValue: 'HTTP url',
|
||||
key: 'httpUrl',
|
||||
label: 'HTTP URL',
|
||||
value: 'httpUrl',
|
||||
},
|
||||
{
|
||||
displayValue: 'HTTP method',
|
||||
key: 'httpMethod',
|
||||
label: 'HTTP Method',
|
||||
value: 'httpMethod',
|
||||
},
|
||||
{
|
||||
displayValue: 'HTTP host',
|
||||
key: 'httpHost',
|
||||
label: 'HTTP Host',
|
||||
value: 'httpHost',
|
||||
},
|
||||
{
|
||||
displayValue: 'HTTP route',
|
||||
key: 'httpRoute',
|
||||
label: 'HTTP Route',
|
||||
value: 'httpRoute',
|
||||
},
|
||||
{
|
||||
displayValue: 'HTTP status code',
|
||||
key: 'httpCode',
|
||||
label: 'RPC Method',
|
||||
value: 'rpcMethod',
|
||||
},
|
||||
{
|
||||
displayValue: 'RPC Method',
|
||||
key: 'rpcMethod',
|
||||
label: 'Status Code',
|
||||
value: 'responseStatusCode',
|
||||
},
|
||||
{
|
||||
displayValue: 'Status Code',
|
||||
key: 'responseStatusCode',
|
||||
label: 'Database Name',
|
||||
value: 'dbName',
|
||||
},
|
||||
{
|
||||
displayValue: 'Database name',
|
||||
key: 'dbName',
|
||||
label: 'Database System',
|
||||
value: 'dbSystem',
|
||||
},
|
||||
{
|
||||
displayValue: 'Database operation',
|
||||
key: 'dbSystem',
|
||||
label: 'Database Operation',
|
||||
value: 'dbOperation',
|
||||
},
|
||||
{
|
||||
displayValue: 'Messaging System',
|
||||
key: 'msgSystem',
|
||||
label: 'Messaging System',
|
||||
value: 'msgSystem',
|
||||
},
|
||||
{
|
||||
displayValue: 'Messaging Operation',
|
||||
key: 'msgOperation',
|
||||
label: 'Messaging Operation',
|
||||
value: 'msgOperation',
|
||||
},
|
||||
{
|
||||
displayValue: 'Component',
|
||||
key: 'component',
|
||||
label: 'Component',
|
||||
value: 'component',
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -1,17 +1,21 @@
|
||||
import { Space } from 'antd';
|
||||
import React from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { Dispatch } from 'redux';
|
||||
import { AutoComplete, Input, Space } from 'antd';
|
||||
import getTagFilters from 'api/trace/getTagFilter';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import AppActions from 'types/actions';
|
||||
import {
|
||||
UPDATE_SELECTED_FUNCTION,
|
||||
UPDATE_SELECTED_GROUP_BY,
|
||||
} from 'types/actions/trace';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { TraceReducer } from 'types/reducer/trace';
|
||||
|
||||
import { functions, groupBy } from './config';
|
||||
import { functions } from './config';
|
||||
import { SelectComponent } from './styles';
|
||||
import {
|
||||
getSelectedValue,
|
||||
initOptions,
|
||||
onClickSelectedFunctionHandler,
|
||||
onClickSelectedGroupByHandler,
|
||||
selectedGroupByValue,
|
||||
} from './utils';
|
||||
|
||||
const { Option } = SelectComponent;
|
||||
|
||||
@ -20,36 +24,32 @@ function TraceGraphFilter(): JSX.Element {
|
||||
AppState,
|
||||
TraceReducer
|
||||
>((state) => state.traces);
|
||||
const dispatch = useDispatch<Dispatch<AppActions>>();
|
||||
const globalTime = useSelector<AppState, GlobalReducer>(
|
||||
(state) => state.globalTime,
|
||||
);
|
||||
const traces = useSelector<AppState, TraceReducer>((state) => state.traces);
|
||||
|
||||
const onClickSelectedFunctionHandler = (ev: unknown): void => {
|
||||
if (typeof ev === 'string') {
|
||||
const selected = functions.find((e) => e.key === ev);
|
||||
if (selected) {
|
||||
dispatch({
|
||||
type: UPDATE_SELECTED_FUNCTION,
|
||||
payload: {
|
||||
selectedFunction: selected.key,
|
||||
yAxisUnit: selected.yAxisUnit,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
const { isLoading, data } = useQuery(
|
||||
[
|
||||
'getTagKeys',
|
||||
globalTime.minTime,
|
||||
globalTime.maxTime,
|
||||
traces.selectedFilter,
|
||||
traces.isFilterExclude,
|
||||
],
|
||||
{
|
||||
queryFn: () =>
|
||||
getTagFilters({
|
||||
start: globalTime.minTime,
|
||||
end: globalTime.maxTime,
|
||||
other: Object.fromEntries(traces.selectedFilter),
|
||||
isFilterExclude: traces.isFilterExclude,
|
||||
}),
|
||||
cacheTime: 120000,
|
||||
},
|
||||
);
|
||||
|
||||
const onClickSelectedGroupByHandler = (ev: unknown): void => {
|
||||
if (typeof ev === 'string') {
|
||||
const selected = groupBy.find((e) => e.key === ev);
|
||||
if (selected) {
|
||||
dispatch({
|
||||
type: UPDATE_SELECTED_GROUP_BY,
|
||||
payload: {
|
||||
selectedGroupBy: selected.key,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
const options = useMemo(() => initOptions(data?.payload), [data?.payload]);
|
||||
|
||||
return (
|
||||
<Space>
|
||||
@ -59,7 +59,7 @@ function TraceGraphFilter(): JSX.Element {
|
||||
dropdownMatchSelectWidth
|
||||
data-testid="selectedFunction"
|
||||
id="selectedFunction"
|
||||
value={functions.find((e) => selectedFunction === e.key)?.displayValue}
|
||||
value={getSelectedValue(selectedFunction)}
|
||||
onChange={onClickSelectedFunctionHandler}
|
||||
>
|
||||
{functions.map((value) => (
|
||||
@ -70,21 +70,16 @@ function TraceGraphFilter(): JSX.Element {
|
||||
</SelectComponent>
|
||||
|
||||
<label htmlFor="selectedGroupBy">Group By</label>
|
||||
<SelectComponent
|
||||
<AutoComplete
|
||||
dropdownMatchSelectWidth
|
||||
id="selectedGroupBy"
|
||||
data-testid="selectedGroupBy"
|
||||
value={groupBy.find((e) => selectedGroupBy === e.key)?.displayValue}
|
||||
onChange={onClickSelectedGroupByHandler}
|
||||
options={options}
|
||||
value={selectedGroupByValue(selectedGroupBy, options)}
|
||||
onChange={onClickSelectedGroupByHandler(options)}
|
||||
>
|
||||
{groupBy.map(
|
||||
(value): JSX.Element => (
|
||||
<Option value={value.key} key={value.key}>
|
||||
{value.displayValue}
|
||||
</Option>
|
||||
),
|
||||
)}
|
||||
</SelectComponent>
|
||||
<Input disabled={isLoading} placeholder="Please select" />
|
||||
</AutoComplete>
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
|
73
frontend/src/container/Trace/TraceGraphFilter/utils.ts
Normal file
73
frontend/src/container/Trace/TraceGraphFilter/utils.ts
Normal file
@ -0,0 +1,73 @@
|
||||
import { DefaultOptionType } from 'antd/es/select';
|
||||
import { ReactNode } from 'react';
|
||||
import store from 'store';
|
||||
import {
|
||||
UPDATE_SELECTED_FUNCTION,
|
||||
UPDATE_SELECTED_GROUP_BY,
|
||||
} from 'types/actions/trace';
|
||||
import { PayloadProps } from 'types/api/trace/getTagFilters';
|
||||
|
||||
import { extractTagFilters } from '../Search/AllTags/Tag/utils';
|
||||
import { functions, groupBy } from './config';
|
||||
|
||||
export function groupByValues(
|
||||
tagFilters: DefaultOptionType[],
|
||||
): DefaultOptionType[] {
|
||||
const result: DefaultOptionType[] = [...groupBy];
|
||||
tagFilters.forEach((e) => {
|
||||
result.push(e);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
export function initOptions(
|
||||
payload: PayloadProps | null | undefined,
|
||||
): DefaultOptionType[] {
|
||||
if (payload) {
|
||||
return groupByValues(extractTagFilters(payload));
|
||||
}
|
||||
return groupBy;
|
||||
}
|
||||
|
||||
export function onClickSelectedGroupByHandler(options: DefaultOptionType[]) {
|
||||
return (ev: unknown): void => {
|
||||
const { dispatch } = store;
|
||||
if (typeof ev === 'string' && options) {
|
||||
const selected = options.find((e) => e.value === ev);
|
||||
if (selected) {
|
||||
dispatch({
|
||||
type: UPDATE_SELECTED_GROUP_BY,
|
||||
payload: {
|
||||
selectedGroupBy: selected.value ? selected.value.toString() : '',
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function onClickSelectedFunctionHandler(value: unknown): void {
|
||||
const { dispatch } = store;
|
||||
if (typeof value === 'string') {
|
||||
const selected = functions.find((e) => e.key === value);
|
||||
if (selected) {
|
||||
dispatch({
|
||||
type: UPDATE_SELECTED_FUNCTION,
|
||||
payload: {
|
||||
selectedFunction: selected.key,
|
||||
yAxisUnit: selected.yAxisUnit,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
export function selectedGroupByValue(
|
||||
selectedGroupBy: string,
|
||||
options: DefaultOptionType[],
|
||||
): ReactNode {
|
||||
return options.find((e) => selectedGroupBy === e.value)?.label;
|
||||
}
|
||||
|
||||
export function getSelectedValue(selectedFunction: string): unknown {
|
||||
return functions.find((e) => selectedFunction === e.key)?.displayValue;
|
||||
}
|
@ -43,7 +43,9 @@ export const convertRawQueriesToTraceSelectedTags = (
|
||||
? [convertMetricKeyToTrace(query.tagKey)]
|
||||
: (convertMetricKeyToTrace(query.tagKey) as never),
|
||||
Operator: convertOperatorLabelToTraceOperator(query.operator),
|
||||
Values: query.tagValue,
|
||||
StringValues: query.tagValue,
|
||||
NumberValues: [],
|
||||
BoolValues: [],
|
||||
}));
|
||||
|
||||
/**
|
||||
|
@ -9,8 +9,8 @@ export interface Props {
|
||||
isFilterExclude: TraceReducer['isFilterExclude'];
|
||||
}
|
||||
|
||||
interface TagsKeys {
|
||||
tagKeys: string;
|
||||
export interface PayloadProps {
|
||||
stringTagKeys: string[];
|
||||
numberTagKeys: string[];
|
||||
boolTagKeys: string[];
|
||||
}
|
||||
|
||||
export type PayloadProps = TagsKeys[];
|
||||
|
@ -3,11 +3,14 @@ import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
export interface Props {
|
||||
start: GlobalReducer['minTime'];
|
||||
end: GlobalReducer['maxTime'];
|
||||
tagKey: string;
|
||||
tagKey: {
|
||||
Key: string;
|
||||
Type: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface Value {
|
||||
tagValues: string;
|
||||
export interface PayloadProps {
|
||||
stringTagValues: string[];
|
||||
numberTagValues: number[];
|
||||
boolTagValues: boolean[];
|
||||
}
|
||||
|
||||
export type PayloadProps = Value[];
|
||||
|
@ -49,15 +49,33 @@ interface SpansAggregateData {
|
||||
export interface Tags {
|
||||
Key: string[];
|
||||
Operator: OperatorValues;
|
||||
Values: string[];
|
||||
StringValues: string[];
|
||||
NumberValues: number[];
|
||||
BoolValues: boolean[];
|
||||
}
|
||||
|
||||
export interface TagsAPI {
|
||||
Key: string;
|
||||
Operator: OperatorValues;
|
||||
Values: string[];
|
||||
StringValues: string[];
|
||||
NumberValues: number[];
|
||||
BoolValues: boolean[];
|
||||
}
|
||||
export type OperatorValues = 'not in' | 'in';
|
||||
export type OperatorValues =
|
||||
| 'NotIn'
|
||||
| 'In'
|
||||
| 'Equals'
|
||||
| 'NotEquals'
|
||||
| 'Contains'
|
||||
| 'NotContains'
|
||||
| 'GreaterThan'
|
||||
| 'Exists'
|
||||
| 'NotExists'
|
||||
| 'LessThan'
|
||||
| 'GreaterThanEquals'
|
||||
| 'LessThanEquals'
|
||||
| 'StartsWith'
|
||||
| 'NotStartsWith';
|
||||
|
||||
export type TraceFilterEnum =
|
||||
| 'component'
|
||||
|
Loading…
x
Reference in New Issue
Block a user