fix: added attribute check for log details filtering (#8427)

* fix: added attribute check for log details filtering

* chore: added unit tests

* chore: add the missing args to onClickHandler to fix the failing build

---------

Co-authored-by: ahmadshaheer <ashaheerki@gmail.com>
This commit is contained in:
Sahil Khan 2025-07-22 14:51:07 +05:30 committed by GitHub
parent b053ce23cd
commit fe95ee716a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 371 additions and 5 deletions

View File

@ -58,6 +58,7 @@ export interface ActionItemProps {
operator: string,
isJSON?: boolean,
dataType?: DataTypes,
fieldType?: string,
) => void;
}

View File

@ -14,6 +14,7 @@ import { ResizeTable } from 'components/ResizeTable';
import { OPERATORS } from 'constants/queryBuilder';
import ROUTES from 'constants/routes';
import { RESTRICTED_SELECTED_FIELDS } from 'container/LogsFilters/config';
import { MetricsType } from 'container/MetricsApplication/constant';
import { FontSize, OptionsQuery } from 'container/OptionsMenu/types';
import { useIsDarkMode } from 'hooks/useDarkMode';
import history from 'lib/history';
@ -113,6 +114,7 @@ function TableView({
fieldKey: string,
fieldValue: string,
dataType: string | undefined,
fieldType: string | undefined,
): void => {
const validatedFieldValue = removeJSONStringifyQuotes(fieldValue);
if (onClickActionItem) {
@ -122,6 +124,7 @@ function TableView({
operator,
undefined,
dataType as DataTypes,
fieldType,
);
}
};
@ -131,8 +134,9 @@ function TableView({
fieldKey: string,
fieldValue: string,
dataType: string | undefined,
fieldType: MetricsType | undefined,
) => (): void => {
handleClick(operator, fieldKey, fieldValue, dataType);
handleClick(operator, fieldKey, fieldValue, dataType, fieldType);
if (operator === OPERATORS['=']) {
setIsFilterInLoading(true);
}

View File

@ -11,6 +11,7 @@ import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
import { OPERATORS } from 'constants/queryBuilder';
import ROUTES from 'constants/routes';
import { RESTRICTED_SELECTED_FIELDS } from 'container/LogsFilters/config';
import { MetricsType } from 'container/MetricsApplication/constant';
import dompurify from 'dompurify';
import { ArrowDownToDot, ArrowUpFromDot, Ellipsis } from 'lucide-react';
import { useTimezone } from 'providers/Timezone';
@ -46,6 +47,7 @@ interface ITableViewActionsProps {
fieldKey: string,
fieldValue: string,
dataType: string | undefined,
logType: MetricsType | undefined,
) => () => void;
}
@ -127,7 +129,7 @@ export default function TableViewActions(
} = props;
const { pathname } = useLocation();
const { dataType } = getFieldAttributes(record.field);
const { dataType, logType: fieldType } = getFieldAttributes(record.field);
// there is no option for where clause in old logs explorer and live logs page
const isOldLogsExplorerOrLiveLogsPage = useMemo(
@ -234,6 +236,7 @@ export default function TableViewActions(
fieldFilterKey,
parseFieldValue(fieldData.value),
dataType,
fieldType,
)}
/>
</Tooltip>
@ -252,6 +255,7 @@ export default function TableViewActions(
fieldFilterKey,
parseFieldValue(fieldData.value),
dataType,
fieldType,
)}
/>
</Tooltip>
@ -312,6 +316,7 @@ export default function TableViewActions(
fieldFilterKey,
parseFieldValue(fieldData.value),
dataType,
fieldType,
)}
/>
</Tooltip>
@ -330,6 +335,7 @@ export default function TableViewActions(
fieldFilterKey,
parseFieldValue(fieldData.value),
dataType,
fieldType,
)}
/>
</Tooltip>

View File

@ -2,6 +2,7 @@ import { getAggregateKeys } from 'api/queryBuilder/getAttributeKeys';
import { SOMETHING_WENT_WRONG } from 'constants/api';
import { OPERATORS, QueryBuilderKeys } from 'constants/queryBuilder';
import ROUTES from 'constants/routes';
import { MetricsType } from 'container/MetricsApplication/constant';
import { getOperatorValue } from 'container/QueryBuilder/filters/QueryBuilderSearch/utils';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useNotifications } from 'hooks/useNotifications';
@ -82,6 +83,7 @@ export const useActiveLog = (): UseActiveLog => {
operator: string,
isJSON?: boolean,
dataType?: DataTypes,
fieldType?: MetricsType | undefined,
): Promise<void> => {
try {
const keysAutocompleteResponse = await queryClient.fetchQuery(
@ -104,6 +106,7 @@ export const useActiveLog = (): UseActiveLog => {
fieldKey,
isJSON,
dataType,
fieldType,
);
const currentOperator = getOperatorValue(operator);

View File

@ -1,9 +1,14 @@
import { initialAutocompleteData } from 'constants/queryBuilder';
import {
baseAutoCompleteIdKeysOrder,
initialAutocompleteData,
} from 'constants/queryBuilder';
import { MetricsType } from 'container/MetricsApplication/constant';
import {
BaseAutocompleteData,
DataTypes,
} from 'types/api/queryBuilder/queryAutocompleteResponse';
import { createIdFromObjectFields } from '../createIdFromObjectFields';
import { chooseAutocompleteFromCustomValue } from '../newQueryBuilder/chooseAutocompleteFromCustomValue';
describe('chooseAutocompleteFromCustomValue', () => {
@ -13,36 +18,108 @@ describe('chooseAutocompleteFromCustomValue', () => {
key: 'string_key',
dataType: DataTypes.String,
isJSON: false,
isColumn: false,
type: '',
id: createIdFromObjectFields(
{
dataType: DataTypes.String,
key: 'string_key',
isColumn: false,
type: '',
},
baseAutoCompleteIdKeysOrder,
),
},
{
key: 'number_key',
dataType: DataTypes.Float64,
isJSON: false,
isColumn: false,
type: '',
id: createIdFromObjectFields(
{
dataType: DataTypes.Float64,
key: 'number_key',
isColumn: false,
type: '',
},
baseAutoCompleteIdKeysOrder,
),
},
{
key: 'bool_key',
dataType: DataTypes.bool,
isJSON: false,
isColumn: false,
type: '',
id: createIdFromObjectFields(
{ dataType: DataTypes.bool, key: 'bool_key', isColumn: false, type: '' },
baseAutoCompleteIdKeysOrder,
),
},
{
key: 'float_key',
dataType: DataTypes.Float64,
isJSON: false,
isColumn: false,
type: '',
id: createIdFromObjectFields(
{
dataType: DataTypes.Float64,
key: 'float_key',
isColumn: false,
type: '',
},
baseAutoCompleteIdKeysOrder,
),
},
{
key: 'unknown_key',
dataType: DataTypes.EMPTY,
isJSON: false,
isColumn: false,
type: '',
id: createIdFromObjectFields(
{
dataType: DataTypes.EMPTY,
key: 'unknown_key',
isColumn: false,
type: '',
},
baseAutoCompleteIdKeysOrder,
),
},
{
key: 'duplicate_key',
dataType: DataTypes.String,
isJSON: false,
isColumn: false,
type: '',
id: createIdFromObjectFields(
{
dataType: DataTypes.String,
key: 'duplicate_key',
isColumn: false,
type: '',
},
baseAutoCompleteIdKeysOrder,
),
},
{
key: 'duplicate_key',
dataType: DataTypes.Float64,
isJSON: false,
isColumn: false,
type: '',
id: createIdFromObjectFields(
{
dataType: DataTypes.Float64,
key: 'duplicate_key',
isColumn: false,
type: '',
},
baseAutoCompleteIdKeysOrder,
),
},
] as BaseAutocompleteData[];
@ -115,7 +192,23 @@ describe('chooseAutocompleteFromCustomValue', () => {
// Test case: Perfect match with isJSON true in sourceList
it('should return matching element with isJSON true', () => {
const jsonSourceList = [
{ key: 'json_key', dataType: DataTypes.String, isJSON: true },
{
key: 'json_key',
dataType: DataTypes.String,
isJSON: true,
isColumn: false,
type: '',
id: createIdFromObjectFields(
{
dataType: DataTypes.String,
key: 'json_key',
isColumn: false,
type: '',
isJSON: true,
},
baseAutoCompleteIdKeysOrder,
),
},
];
const result = chooseAutocompleteFromCustomValue(
jsonSourceList as BaseAutocompleteData[],
@ -293,4 +386,258 @@ describe('chooseAutocompleteFromCustomValue', () => {
});
});
});
describe('when element with same value, same data type, and same fieldType found in sourceList', () => {
const fieldTypeMockSourceList = [
{
key: 'tag_key',
dataType: DataTypes.String,
isJSON: false,
type: MetricsType.Tag,
isColumn: false,
id: createIdFromObjectFields(
{
dataType: DataTypes.String,
key: 'tag_key',
isColumn: false,
type: MetricsType.Tag,
},
baseAutoCompleteIdKeysOrder,
),
},
{
key: 'resource_key',
dataType: DataTypes.Float64,
isJSON: false,
type: MetricsType.Resource,
isColumn: false,
id: createIdFromObjectFields(
{
dataType: DataTypes.Float64,
key: 'resource_key',
isColumn: false,
type: MetricsType.Resource,
},
baseAutoCompleteIdKeysOrder,
),
},
{
key: 'scope_key',
dataType: DataTypes.bool,
isJSON: false,
type: MetricsType.Scope,
isColumn: false,
id: createIdFromObjectFields(
{
dataType: DataTypes.bool,
key: 'scope_key',
isColumn: false,
type: MetricsType.Scope,
},
baseAutoCompleteIdKeysOrder,
),
},
{
key: 'tag_key_duplicate',
dataType: DataTypes.String,
isJSON: false,
type: MetricsType.Tag,
isColumn: false,
id: createIdFromObjectFields(
{
dataType: DataTypes.String,
key: 'tag_key_duplicate',
isColumn: false,
type: MetricsType.Tag,
},
baseAutoCompleteIdKeysOrder,
),
},
{
key: 'tag_key_duplicate',
dataType: DataTypes.String,
isJSON: false,
type: MetricsType.Resource,
isColumn: false,
id: createIdFromObjectFields(
{
dataType: DataTypes.String,
key: 'tag_key_duplicate',
isColumn: false,
type: MetricsType.Resource,
},
baseAutoCompleteIdKeysOrder,
),
},
] as BaseAutocompleteData[];
it('should return matching element for Tag fieldType', () => {
const result = chooseAutocompleteFromCustomValue(
fieldTypeMockSourceList,
'tag_key',
false,
'string' as DataTypes,
MetricsType.Tag,
);
expect(result).toEqual(fieldTypeMockSourceList[0]);
});
it('should return matching element for Resource fieldType', () => {
const result = chooseAutocompleteFromCustomValue(
fieldTypeMockSourceList,
'resource_key',
false,
'number' as DataTypes,
MetricsType.Resource,
);
expect(result).toEqual(fieldTypeMockSourceList[1]);
});
it('should return matching element for Scope fieldType', () => {
const result = chooseAutocompleteFromCustomValue(
fieldTypeMockSourceList,
'scope_key',
false,
'bool' as DataTypes,
MetricsType.Scope,
);
expect(result).toEqual(fieldTypeMockSourceList[2]);
});
it('should return the correct duplicate with matching fieldType', () => {
const result = chooseAutocompleteFromCustomValue(
fieldTypeMockSourceList,
'tag_key_duplicate',
false,
'string' as DataTypes,
MetricsType.Resource,
);
expect(result).toEqual(fieldTypeMockSourceList[4]);
});
});
describe('when element with same value and data type but different fieldType found in sourceList', () => {
const fieldTypeMockSourceList = [
{
key: 'test_key',
dataType: DataTypes.String,
isJSON: false,
type: MetricsType.Tag,
isColumn: false,
id: createIdFromObjectFields(
{
dataType: DataTypes.String,
key: 'test_key',
isColumn: false,
type: MetricsType.Tag,
},
baseAutoCompleteIdKeysOrder,
),
},
] as BaseAutocompleteData[];
it('should return new object with updated fieldType when existing element has different fieldType', () => {
const result = chooseAutocompleteFromCustomValue(
fieldTypeMockSourceList,
'test_key',
false,
'string' as DataTypes,
MetricsType.Resource,
);
expect(result).toEqual({
...initialAutocompleteData,
key: 'test_key',
dataType: DataTypes.String,
isJSON: false,
type: MetricsType.Resource,
});
});
});
describe('when element not found in sourceList but fieldType is provided', () => {
it('should return new object with Tag fieldType', () => {
const result = chooseAutocompleteFromCustomValue(
mockSourceList,
'new_key_with_tag_type',
false,
'string' as DataTypes,
MetricsType.Tag,
);
expect(result).toEqual({
...initialAutocompleteData,
key: 'new_key_with_tag_type',
dataType: DataTypes.String,
isJSON: false,
type: MetricsType.Tag,
});
});
it('should return new object with Resource fieldType', () => {
const result = chooseAutocompleteFromCustomValue(
mockSourceList,
'new_key_with_resource_type',
false,
'number' as DataTypes,
MetricsType.Resource,
);
expect(result).toEqual({
...initialAutocompleteData,
key: 'new_key_with_resource_type',
dataType: DataTypes.Float64,
isJSON: false,
type: MetricsType.Resource,
});
});
it('should return new object with Scope fieldType', () => {
const result = chooseAutocompleteFromCustomValue(
mockSourceList,
'new_key_with_scope_type',
false,
'bool' as DataTypes,
MetricsType.Scope,
);
expect(result).toEqual({
...initialAutocompleteData,
key: 'new_key_with_scope_type',
dataType: DataTypes.bool,
isJSON: false,
type: MetricsType.Scope,
});
});
it('should return new object with empty fieldType when undefined is passed', () => {
const result = chooseAutocompleteFromCustomValue(
mockSourceList,
'new_key_with_undefined_type',
false,
'string' as DataTypes,
undefined,
);
expect(result).toEqual({
...initialAutocompleteData,
key: 'new_key_with_undefined_type',
dataType: DataTypes.String,
isJSON: false,
type: '',
});
});
it('should return new object with isJSON true and fieldType when not found', () => {
const result = chooseAutocompleteFromCustomValue(
mockSourceList,
'json_not_found_with_type',
true,
'string' as DataTypes,
MetricsType.Tag,
);
expect(result).toEqual({
...initialAutocompleteData,
key: 'json_not_found_with_type',
dataType: DataTypes.String,
isJSON: true,
type: MetricsType.Tag,
});
});
});
});

View File

@ -1,4 +1,5 @@
import { initialAutocompleteData } from 'constants/queryBuilder';
import { MetricsType } from 'container/MetricsApplication/constant';
import {
BaseAutocompleteData,
DataTypes,
@ -25,12 +26,15 @@ export const chooseAutocompleteFromCustomValue = (
value: string,
isJSON?: boolean,
dataType?: DataTypes | 'number',
fieldType?: MetricsType | undefined,
): BaseAutocompleteData => {
const dataTypeToUse = getDataTypeForCustomValue(dataType);
const firstBaseAutoCompleteValue = sourceList.find(
(sourceAutoComplete) =>
value === sourceAutoComplete.key &&
(dataType === undefined || dataTypeToUse === sourceAutoComplete.dataType),
(dataType === undefined || dataTypeToUse === sourceAutoComplete.dataType) &&
((fieldType === undefined && sourceAutoComplete.type === '') ||
(fieldType !== undefined && fieldType === sourceAutoComplete.type)),
);
if (!firstBaseAutoCompleteValue) {
@ -38,6 +42,7 @@ export const chooseAutocompleteFromCustomValue = (
...initialAutocompleteData,
key: value,
dataType: dataTypeToUse,
type: fieldType || '',
isJSON,
};
}