diff --git a/frontend/src/container/LogDetailedView/ActionItem.tsx b/frontend/src/container/LogDetailedView/ActionItem.tsx index 2860f90f0abd..a736750a66a7 100644 --- a/frontend/src/container/LogDetailedView/ActionItem.tsx +++ b/frontend/src/container/LogDetailedView/ActionItem.tsx @@ -58,6 +58,7 @@ export interface ActionItemProps { operator: string, isJSON?: boolean, dataType?: DataTypes, + fieldType?: string, ) => void; } diff --git a/frontend/src/container/LogDetailedView/TableView.tsx b/frontend/src/container/LogDetailedView/TableView.tsx index 05a578f60af0..d7fb84065ea4 100644 --- a/frontend/src/container/LogDetailedView/TableView.tsx +++ b/frontend/src/container/LogDetailedView/TableView.tsx @@ -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); } diff --git a/frontend/src/container/LogDetailedView/TableView/TableViewActions.tsx b/frontend/src/container/LogDetailedView/TableView/TableViewActions.tsx index af0a75e78517..3c4a102d8868 100644 --- a/frontend/src/container/LogDetailedView/TableView/TableViewActions.tsx +++ b/frontend/src/container/LogDetailedView/TableView/TableViewActions.tsx @@ -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, )} /> @@ -252,6 +255,7 @@ export default function TableViewActions( fieldFilterKey, parseFieldValue(fieldData.value), dataType, + fieldType, )} /> @@ -312,6 +316,7 @@ export default function TableViewActions( fieldFilterKey, parseFieldValue(fieldData.value), dataType, + fieldType, )} /> @@ -330,6 +335,7 @@ export default function TableViewActions( fieldFilterKey, parseFieldValue(fieldData.value), dataType, + fieldType, )} /> diff --git a/frontend/src/hooks/logs/useActiveLog.ts b/frontend/src/hooks/logs/useActiveLog.ts index d7cc498f9fb1..be5d882f31d6 100644 --- a/frontend/src/hooks/logs/useActiveLog.ts +++ b/frontend/src/hooks/logs/useActiveLog.ts @@ -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 => { try { const keysAutocompleteResponse = await queryClient.fetchQuery( @@ -104,6 +106,7 @@ export const useActiveLog = (): UseActiveLog => { fieldKey, isJSON, dataType, + fieldType, ); const currentOperator = getOperatorValue(operator); diff --git a/frontend/src/lib/__tests__/chooseAutocompleteFromCustomValue.test.ts b/frontend/src/lib/__tests__/chooseAutocompleteFromCustomValue.test.ts index 8ce0798f4e9b..375d8c980420 100644 --- a/frontend/src/lib/__tests__/chooseAutocompleteFromCustomValue.test.ts +++ b/frontend/src/lib/__tests__/chooseAutocompleteFromCustomValue.test.ts @@ -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, + }); + }); + }); }); diff --git a/frontend/src/lib/newQueryBuilder/chooseAutocompleteFromCustomValue.ts b/frontend/src/lib/newQueryBuilder/chooseAutocompleteFromCustomValue.ts index 4449359e4c48..69087ad044c3 100644 --- a/frontend/src/lib/newQueryBuilder/chooseAutocompleteFromCustomValue.ts +++ b/frontend/src/lib/newQueryBuilder/chooseAutocompleteFromCustomValue.ts @@ -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, }; }