From 632b711ca1de77d113192649f56c5b18ae453f86 Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Fri, 11 Oct 2024 20:15:37 +0200 Subject: [PATCH] fix current field candidates list --- ...te_analysis_field_candidates_slice.test.ts | 13 ++++++-- ...og_rate_analysis_field_candidates_slice.ts | 31 +++++++++++++---- .../log_rate_analysis_options.tsx | 25 ++++---------- .../log_rate_analysis_results.tsx | 33 ++++++++++--------- 4 files changed, 59 insertions(+), 43 deletions(-) diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/state/log_rate_analysis_field_candidates_slice.test.ts b/x-pack/packages/ml/aiops_log_rate_analysis/state/log_rate_analysis_field_candidates_slice.test.ts index 4f829b0e0bf5a..5b4946dc2eb2b 100644 --- a/x-pack/packages/ml/aiops_log_rate_analysis/state/log_rate_analysis_field_candidates_slice.test.ts +++ b/x-pack/packages/ml/aiops_log_rate_analysis/state/log_rate_analysis_field_candidates_slice.test.ts @@ -9,14 +9,16 @@ import { httpServiceMock } from '@kbn/core/public/mocks'; import type { FetchFieldCandidatesResponse } from '../queries/fetch_field_candidates'; -import { fetchFieldCandidates } from './log_rate_analysis_field_candidates_slice'; +import { fetchFieldCandidates, getDefaultState } from './log_rate_analysis_field_candidates_slice'; const mockHttp = httpServiceMock.createStartContract(); describe('fetchFieldCandidates', () => { it('dispatches field candidates', async () => { const mockDispatch = jest.fn(); - const mockGetState = jest.fn(); + const mockGetState = jest.fn().mockReturnValue({ + logRateAnalysisFieldCandidates: getDefaultState(), + }); const mockResponse: FetchFieldCandidatesResponse = { isECS: false, @@ -60,7 +62,12 @@ describe('fetchFieldCandidates', () => { payload: { fieldSelectionMessage: '2 out of 5 fields were preselected for the analysis. Use the "Fields" dropdown to adjust the selection.', - fieldFilterSkippedItems: [ + initialFieldFilterSkippedItems: [ + 'another-keyword-field', + 'another-text-field', + 'yet-another-text-field', + ], + currentFieldFilterSkippedItems: [ 'another-keyword-field', 'another-text-field', 'yet-another-text-field', diff --git a/x-pack/packages/ml/aiops_log_rate_analysis/state/log_rate_analysis_field_candidates_slice.ts b/x-pack/packages/ml/aiops_log_rate_analysis/state/log_rate_analysis_field_candidates_slice.ts index aa5cb969e5401..a3ac63c5927e9 100644 --- a/x-pack/packages/ml/aiops_log_rate_analysis/state/log_rate_analysis_field_candidates_slice.ts +++ b/x-pack/packages/ml/aiops_log_rate_analysis/state/log_rate_analysis_field_candidates_slice.ts @@ -90,10 +90,14 @@ export const fetchFieldCandidates = createAsyncThunk( ...selectedKeywordFieldCandidates, ...selectedTextFieldCandidates, ]; - const fieldFilterSkippedItems = fieldFilterUniqueItems.filter( + const initialFieldFilterSkippedItems = fieldFilterUniqueItems.filter( (d) => !fieldFilterUniqueSelectedItems.includes(d) ); + const currentFieldFilterSkippedItems = ( + thunkApi.getState() as { logRateAnalysisFieldCandidates: FieldCandidatesState } + ).logRateAnalysisFieldCandidates.currentFieldFilterSkippedItems; + thunkApi.dispatch( setAllFieldCandidates({ fieldSelectionMessage: getFieldSelectionMessage( @@ -102,7 +106,13 @@ export const fetchFieldCandidates = createAsyncThunk( fieldFilterUniqueSelectedItems.length ), fieldFilterUniqueItems, - fieldFilterSkippedItems, + initialFieldFilterSkippedItems, + // If the currentFieldFilterSkippedItems is null, we're on the first load, + // only then we set the current skipped fields to the initial skipped fields. + currentFieldFilterSkippedItems: + currentFieldFilterSkippedItems === null && initialFieldFilterSkippedItems.length > 0 + ? initialFieldFilterSkippedItems + : currentFieldFilterSkippedItems, keywordFieldCandidates, textFieldCandidates, selectedKeywordFieldCandidates, @@ -116,18 +126,20 @@ export interface FieldCandidatesState { isLoading: boolean; fieldSelectionMessage?: string; fieldFilterUniqueItems: string[]; - fieldFilterSkippedItems: string[]; + initialFieldFilterSkippedItems: string[]; + currentFieldFilterSkippedItems: string[] | null; keywordFieldCandidates: string[]; textFieldCandidates: string[]; selectedKeywordFieldCandidates: string[]; selectedTextFieldCandidates: string[]; } -function getDefaultState(): FieldCandidatesState { +export function getDefaultState(): FieldCandidatesState { return { isLoading: false, fieldFilterUniqueItems: [], - fieldFilterSkippedItems: [], + initialFieldFilterSkippedItems: [], + currentFieldFilterSkippedItems: null, keywordFieldCandidates: [], textFieldCandidates: [], selectedKeywordFieldCandidates: [], @@ -145,6 +157,12 @@ export const logRateAnalysisFieldCandidatesSlice = createSlice({ ) => { return { ...state, ...action.payload }; }, + setCurrentFieldFilterSkippedItems: ( + state: FieldCandidatesState, + action: PayloadAction + ) => { + return { ...state, currentFieldFilterSkippedItems: action.payload }; + }, }, extraReducers: (builder) => { builder.addCase(fetchFieldCandidates.pending, (state) => { @@ -157,4 +175,5 @@ export const logRateAnalysisFieldCandidatesSlice = createSlice({ }); // Action creators are generated for each case reducer function -export const { setAllFieldCandidates } = logRateAnalysisFieldCandidatesSlice.actions; +export const { setAllFieldCandidates, setCurrentFieldFilterSkippedItems } = + logRateAnalysisFieldCandidatesSlice.actions; diff --git a/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_options.tsx b/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_options.tsx index 1e5e16e02420d..99a2829886aec 100644 --- a/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_options.tsx +++ b/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_options.tsx @@ -6,7 +6,7 @@ */ import type { FC } from 'react'; -import React, { useEffect, useState } from 'react'; +import React from 'react'; import { EuiButtonGroup, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; @@ -23,6 +23,7 @@ import { setSkippedColumns, type LogRateAnalysisResultsTableColumnName, } from '@kbn/aiops-log-rate-analysis/state/log_rate_analysis_table_slice'; +import { setCurrentFieldFilterSkippedItems } from '@kbn/aiops-log-rate-analysis/state/log_rate_analysis_field_candidates_slice'; import { ItemFilterPopover as FieldFilterPopover } from './item_filter_popover'; import { ItemFilterPopover as ColumnFilterPopover } from './item_filter_popover'; @@ -82,13 +83,11 @@ const resultsGroupedOnId = 'aiopsLogRateAnalysisGroupingOn'; export interface LogRateAnalysisOptionsProps { foundGroups: boolean; growFirstItem?: boolean; - onFieldsFilterChange: (skippedFieldsUpdate: string[]) => void; } export const LogRateAnalysisOptions: FC = ({ foundGroups, growFirstItem = false, - onFieldsFilterChange, }) => { const dispatch = useAppDispatch(); @@ -96,20 +95,11 @@ export const LogRateAnalysisOptions: FC = ({ const { isRunning } = useAppSelector((s) => s.logRateAnalysisStream); const fieldCandidates = useAppSelector((s) => s.logRateAnalysisFieldCandidates); const { skippedColumns } = useAppSelector((s) => s.logRateAnalysisTable); - const { fieldFilterUniqueItems, fieldFilterSkippedItems } = fieldCandidates; + const { fieldFilterUniqueItems, initialFieldFilterSkippedItems } = fieldCandidates; const fieldFilterButtonDisabled = isRunning || fieldCandidates.isLoading || fieldFilterUniqueItems.length === 0; const toggleIdSelected = groupResults ? resultsGroupedOnId : resultsGroupedOffId; - // null is used as the uninitialized state to identify the first load. - const [skippedFields, setSkippedFields] = useState(null); - - // Set skipped fields only on first load, otherwise we'd overwrite the user's selection. - useEffect(() => { - if (skippedFields === null && fieldFilterSkippedItems.length > 0) - setSkippedFields(fieldFilterSkippedItems); - }, [fieldFilterSkippedItems, skippedFields]); - const onGroupResultsToggle = (optionId: string) => { dispatch(setGroupResults(optionId === resultsGroupedOnId)); // When toggling the group switch, clear all row selections @@ -120,9 +110,8 @@ export const LogRateAnalysisOptions: FC = ({ dispatch(setSkippedColumns(columns)); }; - const onFieldsFilterChangeHandler = (skippedFieldsUpdate: string[]) => { - setSkippedFields(skippedFieldsUpdate); - onFieldsFilterChange(skippedFieldsUpdate); + const onFieldsFilterChange = (skippedFieldsUpdate: string[]) => { + dispatch(setCurrentFieldFilterSkippedItems(skippedFieldsUpdate)); }; // Disable the grouping switch toggle only if no groups were found, @@ -173,8 +162,8 @@ export const LogRateAnalysisOptions: FC = ({ popoverButtonTitle={fieldsButton} selectedItemLimit={1} uniqueItemNames={fieldFilterUniqueItems} - initialSkippedItems={fieldFilterSkippedItems} - onChange={onFieldsFilterChangeHandler} + initialSkippedItems={initialFieldFilterSkippedItems} + onChange={onFieldsFilterChange} /> diff --git a/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_results.tsx b/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_results.tsx index f1c6d0e60f1c8..6ab82bd16e00d 100644 --- a/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_results.tsx +++ b/x-pack/plugins/aiops/public/components/log_rate_analysis/log_rate_analysis_results.tsx @@ -135,21 +135,27 @@ export const LogRateAnalysisResults: FC = ({ setEmbeddableOptionsVisible((s) => !s); }; - const onFieldsFilterChange = (skippedFieldsUpdate: string[]) => { + const { currentFieldFilterSkippedItems, keywordFieldCandidates, textFieldCandidates } = + fieldCandidates; + + useEffect(() => { + if (currentFieldFilterSkippedItems === null) return; + dispatch(resetResults()); setOverrides({ loaded: 0, remainingKeywordFieldCandidates: keywordFieldCandidates.filter( - (d) => !skippedFieldsUpdate.includes(d) + (d) => !currentFieldFilterSkippedItems.includes(d) ), remainingTextFieldCandidates: textFieldCandidates.filter( - (d) => !skippedFieldsUpdate.includes(d) + (d) => !currentFieldFilterSkippedItems.includes(d) ), regroupOnly: false, }); startHandler(true, false); - }; - const { fieldFilterSkippedItems, keywordFieldCandidates, textFieldCandidates } = fieldCandidates; + // custom check to trigger on currentFieldFilterSkippedItems change + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [currentFieldFilterSkippedItems]); function cancelHandler() { abortCtrl.current.abort(); @@ -209,10 +215,12 @@ export const LogRateAnalysisResults: FC = ({ dispatch(resetResults()); setOverrides({ remainingKeywordFieldCandidates: keywordFieldCandidates.filter( - (d) => fieldFilterSkippedItems !== null && fieldFilterSkippedItems.includes(d) + (d) => + currentFieldFilterSkippedItems === null || !currentFieldFilterSkippedItems.includes(d) ), remainingTextFieldCandidates: textFieldCandidates.filter( - (d) => fieldFilterSkippedItems !== null && fieldFilterSkippedItems.includes(d) + (d) => + currentFieldFilterSkippedItems === null || !currentFieldFilterSkippedItems.includes(d) ), }); } @@ -325,10 +333,7 @@ export const LogRateAnalysisResults: FC = ({ > <> {embeddingOrigin !== AIOPS_EMBEDDABLE_ORIGIN.DASHBOARD && ( - + )} {embeddingOrigin === AIOPS_EMBEDDABLE_ORIGIN.DASHBOARD && ( @@ -354,11 +359,7 @@ export const LogRateAnalysisResults: FC = ({ <> - + )}