From 47e8a89dbe1eab7b4724a3c396747cd13a2eb3e7 Mon Sep 17 00:00:00 2001 From: Aditya Singh Date: Thu, 18 Sep 2025 12:51:54 +0530 Subject: [PATCH] Fix: No quick filter found screen on navigating back from a diff screen (#9121) * feat: minor refactor * feat: change contextlinks data structure * feat: context menu changes init * feat: context menu hook refactor * feat: context links processors * feat: context variables hook added * feat: add support for field variables * feat: minor refactor * feat: minor refactor * feat: minor refactor * feat: handle on save * feat: minor refactor * feat: snapshot update * feat: revert qbv5 * feat: aggregation header val * feat: fix header color * feat: minor refactor * feat: minor refactor * feat: fix breaking changes from qb v5 * feat: change api for breakout opitons * feat: minor refactor * feat: minor refactor * fix: added fix for extractquerypararms when value is string in multivalue operator * feat: minor refactor * feat: add back in breakout * feat: minor refactor * feat: add substitute var api call to decode vars * feat: minor fix * feat: optimize query value comparison in QueryBuilderV2 * feat: minor fix * feat: minor fix * feat: test fix * feat: added dynamic variables creation flow (#7541) * feat: added dynamic variables creation flow * feat: added keys and value apis and hooks * feat: added api and select component changes * feat: added keys fetching and preview values * feat: added dynamic variable to variable items * feat: handled value persistence and tab switches * feat: added default value and formed a schema for dyn-variables * feat: added client and server side searches * feat: corrected the initial load getfieldKey api * feat: removed fetch on mount restriction * feat: added dynamic variable to the dashboard details (#7755) * feat: added dynamic variable to the dashboard details * feat: added new component to existing variables * feat: added enhancement to multiselect and select for dyn-variables * feat: added refetch method between all dynamic-variables * feat: correct error handling * feat: correct error handling * feat: enforced non-empty selectedvalues and default value * feat: added client and server side searches * feat: retry on error * feat: correct error handling * feat: handle defautl value in existing variables * feat: lowercase the source for payload * feat: fixed the incorrect assignment of active indices * feat: improved handling of all option * feat: improved the ALL option visuals * feat: handled default value enforcement in existing variables * feat: added unix time to values call * feat: added incomplete data message and info to search * feat: changed dashboard panel call handling with existing variables * feat: adjusted the response type and data with the new API schema for values * feat: code refactor * feat: made dyn-variable option as the default * feat: added test cases for dyn variable creation and completion * feat: updated test cases * feat: added variable in url and made dashboard sync around that and sharable (#7944) * feat: added dynamic variable to the dashboard details * feat: added new component to existing variables * feat: added enhancement to multiselect and select for dyn-variables * feat: added refetch method between all dynamic-variables * feat: correct error handling * feat: correct error handling * feat: enforced non-empty selectedvalues and default value * feat: added client and server side searches * feat: retry on error * feat: correct error handling * feat: handle defautl value in existing variables * feat: lowercase the source for payload * feat: fixed the incorrect assignment of active indices * feat: improved handling of all option * feat: improved the ALL option visuals * feat: handled default value enforcement in existing variables * feat: added unix time to values call * feat: added incomplete data message and info to search * feat: changed dashboard panel call handling with existing variables * feat: adjusted the response type and data with the new API schema for values * feat: code refactor * feat: made dyn-variable option as the default * feat: added test cases for dyn variable creation and completion * feat: updated test cases * feat: added variable in url and made dashboard sync around that and sharable * feat: added test cases * feat: added safety check * feat: enabled url setting on first load itself * feat: code refactor * feat: cleared options query param when on dashboard list page * feat: resolved conflicts * feat: added dynamic variable suggestion in where clause * feat: added test cases for hooks and api call functions * feat: added test case for querybuildersearchv2 suggestion changes * feat: code refactor * feat: updated test case * feat: corrected the regex matcher for resolved titles * feat: added ability to add/remove variable filter to one or more existing panels * feat: added widgetselector on variable creation * feat: show labels in widget selector * feat: added apply to all and variable removal logical * feat: refectch only related and affected panels in case of dynamic variables * feat: added button loader for apply-all * feat: light-mode styles * feat: minor refactor * feat: added test cases * feat: refactor * feat: remove consoles * feat: pass panel types to substitutevars * feat: cross filtering init * fix: added fix for query builder filters * feat: cross filtering add set/unset/create functionality * feat: test update * fix: added migration to filter expression for crud operations of variable * feat: format legend name according to existing format * feat: breakout test init * feat: breakout test match query * feat: context links tests * feat: minor refactor * feat: show edit only if user has access * feat: added dynamic variables creation flow (#7541) * feat: added dynamic variables creation flow * feat: added keys and value apis and hooks * feat: added api and select component changes * feat: added keys fetching and preview values * feat: added dynamic variable to variable items * feat: handled value persistence and tab switches * feat: added default value and formed a schema for dyn-variables * feat: added client and server side searches * feat: corrected the initial load getfieldKey api * feat: removed fetch on mount restriction * feat: added dynamic variable to the dashboard details (#7755) * feat: added dynamic variable to the dashboard details * feat: added new component to existing variables * feat: added enhancement to multiselect and select for dyn-variables * feat: added refetch method between all dynamic-variables * feat: correct error handling * feat: correct error handling * feat: enforced non-empty selectedvalues and default value * feat: added client and server side searches * feat: retry on error * feat: correct error handling * feat: handle defautl value in existing variables * feat: lowercase the source for payload * feat: fixed the incorrect assignment of active indices * feat: improved handling of all option * feat: improved the ALL option visuals * feat: handled default value enforcement in existing variables * feat: added unix time to values call * feat: added incomplete data message and info to search * feat: changed dashboard panel call handling with existing variables * feat: adjusted the response type and data with the new API schema for values * feat: code refactor * feat: made dyn-variable option as the default * feat: added test cases for dyn variable creation and completion * feat: updated test cases * feat: added dynamic variable suggestion in where clause * feat: added test cases for hooks and api call functions * feat: added test case for querybuildersearchv2 suggestion changes * feat: code refactor * feat: updated test case * feat: corrected the regex matcher for resolved titles * feat: added ability to add/remove variable filter to one or more existing panels * feat: added widgetselector on variable creation * feat: show labels in widget selector * feat: added apply to all and variable removal logical * feat: refectch only related and affected panels in case of dynamic variables * feat: added button loader for apply-all * feat: light-mode styles * fix: added migration to filter expression for crud operations of variable * feat: reverted dynamic variable url config changes (#8877) * Revert "feat: changed query param name" This reverts commit 62bee5f003bf74b0da1c5951f1b5d0f2c250905d. * Revert "feat: added user-friendly format to dashboard variable url" This reverts commit 6de8b1c2e8c6a838941014ea4929e9f5c908d975. * feat: reverted url var changes * feat: reverted url changed from usedashboardvarupdate hook * feat: send empty array for widgetId * feat: added type in the variables in query_range payload for dynamic * feat: minor fixes * fix: added fix for multivalue operator without brackets * feat: minor fix * feat: fix failing test * feat: change revert * test: added tests for querycontextUtils + querybuilderv2 utils * fix: added fix for replacing filter with the new value * fix: added fix for replacing filters + datetimepicker composite query * test: fixed querybuilderv2 utils test * feat: handle number dataType in filters * feat: correct the variable addition to panel format for new qb expression * feat: remove other queries in breakout * feat: add metric to traces mapping * feat: pass proper time range * feat: update time range logic * feat: value panel drilldown init * feat: value panel drilldown init * feat: enable context links in value panel * feat: minor fix * feat: update snapshot * feat: hide breakout in value panel * feat: add panel type to view mode * feat: add support to change panel in breakouts * feat: panel change for breakout logic added * chore: fix style * chore: show variables suggestion while creating context links * chore: add timestamp to graphs * chore: add timestamp to table panel * chore: fix failing tests * chore: fix infinite re-rendering due to queryRange * chore: send appropriate time range when signal is metrics * chore: show variables suggestion while creating context links * chore: minor refactor * chore: show trace details link if filter has trace_id * chore: fix infinite render of table component * chore: added tests for v2 * fix: context links set from dropdown * chore: minor refactor * chore: minor refactor * chore: fix test * chore: fix timerange for apm metrics * fix: get correct timestamp for clicked data * chore: comment out change to histogram on breakout by number * chore: change panel type on panel type change in url * chore: remove consoles * feat: added dynamic variables creation flow (#7541) * feat: added dynamic variables creation flow * feat: added keys and value apis and hooks * feat: added api and select component changes * feat: added keys fetching and preview values * feat: added dynamic variable to variable items * feat: handled value persistence and tab switches * feat: added default value and formed a schema for dyn-variables * feat: added client and server side searches * feat: corrected the initial load getfieldKey api * feat: removed fetch on mount restriction * feat: added dynamic variable to the dashboard details (#7755) * feat: added dynamic variable to the dashboard details * feat: added new component to existing variables * feat: added enhancement to multiselect and select for dyn-variables * feat: added refetch method between all dynamic-variables * feat: correct error handling * feat: correct error handling * feat: enforced non-empty selectedvalues and default value * feat: added client and server side searches * feat: retry on error * feat: correct error handling * feat: handle defautl value in existing variables * feat: lowercase the source for payload * feat: fixed the incorrect assignment of active indices * feat: improved handling of all option * feat: improved the ALL option visuals * feat: handled default value enforcement in existing variables * feat: added unix time to values call * feat: added incomplete data message and info to search * feat: changed dashboard panel call handling with existing variables * feat: adjusted the response type and data with the new API schema for values * feat: code refactor * feat: made dyn-variable option as the default * feat: added test cases for dyn variable creation and completion * feat: updated test cases * feat: fix lint and test cases * feat: fix typo * feat: fixed test case * feat: added dynamic variable suggestion in where clause * feat: added test cases for hooks and api call functions * feat: added test case for querybuildersearchv2 suggestion changes * feat: code refactor * feat: corrected the regex matcher for resolved titles * feat: fixed test cases * feat: added ability to add/remove variable filter to one or more existing panels * feat: added widgetselector on variable creation * feat: show labels in widget selector * feat: added apply to all and variable removal logical * feat: refectch only related and affected panels in case of dynamic variables * feat: added button loader for apply-all * feat: light-mode styles * fix: added migration to filter expression for crud operations of variable * feat: added type in the variables in query_range payload for dynamic * feat: correct the variable addition to panel format for new qb expression * feat: added test cases for dynamic variable and add/remove panel feat * feat: implemented where clause suggestion in new qb v5 * feat: added retries for dyn variable and fixed on-enter selection issue * feat: added relatedValues and existing query in param related changes * feat: sanitized data storage and removed duplicates * fix: fixed typechecks * feat: updated panel wait and refetch logic and ALL option selection * feat: fixed variable tabel reordering issue * feat: added empty name validation in variable creation * feat: change value to searchtext in values API * feat: added option for regex in the component, disabled for now * feat: added beta and not rec. tag in variable tabs * feat: added check to prevent api and updates calls with same payload * feat: optimized localstorage for all selection in dynamic variable and updated __all__ case * feat: resolved variable tables infinite loop update error * feat: aded variable name auto-update based on attribute name entered for dynamic variables * feat: modified only/all click behaviour and set all selection always true for dynamic variable * feat: fix dropdown closing doesn't reset us back to our all available values when we have a search * feat: handled all state distinction and carry forward in existing variables * feat: trucate + n more tooltip content to 10 * feat: fixed infinite loop because of dependency of frequently changing object ref in var table * feat: fixed inconsist search implementations * feat: reverted only - all updated area implementation * feat: added more space for search in multiselect component * feat: checked for variable id instead of variable key for refetch * feat: improved performance around multiselect component and added confirm modal for apply to all * feat: rewrite functionality around add and remove panels * feat: changed color for apply to all modal * feat: added changes under flag to handle variable specific removal for removeKeysFromExpression func * feat: added validation in variable edit panel * chore: fix dynamic variable update in context menu to latest logic * chore: minor fix * chore: type fix * fix: remove unwanted code * fix: remove unwanted code * fix: resolved pr comments * fix: minor fix * fix: fix tests * fix: style fix * fix: hide drilldown options in view mode for non-builder panels * chore: add global uplot mock * chore: query builder context update to all provider * chore: add cursor rules init * chore: useSafeNavigate mock added * chore: more cleanups * chore: remove react-router-v5 mock from setup * chore: update cursorrules * chore: add tests readme init * chore: minor refactor * fix: refetch quick filters on revisit to page * fix: return expected response from queryFn and use as state --------- Co-authored-by: Aditya Singh Co-authored-by: Abhi Kumar Co-authored-by: SagarRajput-7 <162284829+SagarRajput-7@users.noreply.github.com> Co-authored-by: SagarRajput-7 --- .../components/QuickFilters/QuickFilters.tsx | 4 +- .../QuickFiltersSettings.tsx | 6 +- .../hooks/useQuickFilterSettings.tsx | 6 +- .../QuickFilters/hooks/useFilterConfig.tsx | 48 +++--- .../QuickFilters/tests/QuickFilters.test.tsx | 138 ++++++++++++++++++ 5 files changed, 166 insertions(+), 36 deletions(-) diff --git a/frontend/src/components/QuickFilters/QuickFilters.tsx b/frontend/src/components/QuickFilters/QuickFilters.tsx index c70ca59c2df6..efa1ee536e81 100644 --- a/frontend/src/components/QuickFilters/QuickFilters.tsx +++ b/frontend/src/components/QuickFilters/QuickFilters.tsx @@ -50,7 +50,7 @@ export default function QuickFilters(props: IQuickFiltersProps): JSX.Element { filterConfig, isDynamicFilters, customFilters, - setIsStale, + refetchCustomFilters, isCustomFiltersLoading, } = useFilterConfig({ signal, config }); @@ -263,7 +263,7 @@ export default function QuickFilters(props: IQuickFiltersProps): JSX.Element { signal={signal} setIsSettingsOpen={setIsSettingsOpen} customFilters={customFilters} - setIsStale={setIsStale} + refetchCustomFilters={refetchCustomFilters} /> )} diff --git a/frontend/src/components/QuickFilters/QuickFiltersSettings/QuickFiltersSettings.tsx b/frontend/src/components/QuickFilters/QuickFiltersSettings/QuickFiltersSettings.tsx index aa78d2610781..20e8e5b581a4 100644 --- a/frontend/src/components/QuickFilters/QuickFiltersSettings/QuickFiltersSettings.tsx +++ b/frontend/src/components/QuickFilters/QuickFiltersSettings/QuickFiltersSettings.tsx @@ -14,12 +14,12 @@ function QuickFiltersSettings({ signal, setIsSettingsOpen, customFilters, - setIsStale, + refetchCustomFilters, }: { signal: SignalType | undefined; setIsSettingsOpen: (isSettingsOpen: boolean) => void; customFilters: FilterType[]; - setIsStale: (isStale: boolean) => void; + refetchCustomFilters: () => void; }): JSX.Element { const { handleSettingsClose, @@ -34,7 +34,7 @@ function QuickFiltersSettings({ } = useQuickFilterSettings({ setIsSettingsOpen, customFilters, - setIsStale, + refetchCustomFilters, signal, }); diff --git a/frontend/src/components/QuickFilters/QuickFiltersSettings/hooks/useQuickFilterSettings.tsx b/frontend/src/components/QuickFilters/QuickFiltersSettings/hooks/useQuickFilterSettings.tsx index bf4406c3045a..42be1bece827 100644 --- a/frontend/src/components/QuickFilters/QuickFiltersSettings/hooks/useQuickFilterSettings.tsx +++ b/frontend/src/components/QuickFilters/QuickFiltersSettings/hooks/useQuickFilterSettings.tsx @@ -12,7 +12,7 @@ import { Filter as FilterType } from 'types/api/quickFilters/getCustomFilters'; interface UseQuickFilterSettingsProps { setIsSettingsOpen: (isSettingsOpen: boolean) => void; customFilters: FilterType[]; - setIsStale: (isStale: boolean) => void; + refetchCustomFilters: () => void; signal?: SignalType; } @@ -32,7 +32,7 @@ interface UseQuickFilterSettingsReturn { const useQuickFilterSettings = ({ customFilters, setIsSettingsOpen, - setIsStale, + refetchCustomFilters, signal, }: UseQuickFilterSettingsProps): UseQuickFilterSettingsReturn => { const [inputValue, setInputValue] = useState(''); @@ -46,7 +46,7 @@ const useQuickFilterSettings = ({ } = useMutation(updateCustomFiltersAPI, { onSuccess: () => { setIsSettingsOpen(false); - setIsStale(true); + refetchCustomFilters(); logEvent('Quick Filters Settings: changes saved', { addedFilters, }); diff --git a/frontend/src/components/QuickFilters/hooks/useFilterConfig.tsx b/frontend/src/components/QuickFilters/hooks/useFilterConfig.tsx index fb2659a6817b..2f7d0bc70ed1 100644 --- a/frontend/src/components/QuickFilters/hooks/useFilterConfig.tsx +++ b/frontend/src/components/QuickFilters/hooks/useFilterConfig.tsx @@ -1,12 +1,8 @@ import getCustomFilters from 'api/quickFilters/getCustomFilters'; import { REACT_QUERY_KEY } from 'constants/reactQueryKeys'; -import { useMemo, useState } from 'react'; +import { useMemo } from 'react'; import { useQuery } from 'react-query'; -import { ErrorResponse, SuccessResponse } from 'types/api'; -import { - Filter as FilterType, - PayloadProps, -} from 'types/api/quickFilters/getCustomFilters'; +import { Filter as FilterType } from 'types/api/quickFilters/getCustomFilters'; import { IQuickFiltersConfig, SignalType } from '../types'; import { getFilterConfig } from '../utils'; @@ -18,37 +14,34 @@ interface UseFilterConfigProps { interface UseFilterConfigReturn { filterConfig: IQuickFiltersConfig[]; customFilters: FilterType[]; - setCustomFilters: React.Dispatch>; isCustomFiltersLoading: boolean; isDynamicFilters: boolean; - setIsStale: React.Dispatch>; + refetchCustomFilters: () => void; } const useFilterConfig = ({ signal, config, }: UseFilterConfigProps): UseFilterConfigReturn => { - const [customFilters, setCustomFilters] = useState([]); - const [isStale, setIsStale] = useState(true); + const { + isFetching: isCustomFiltersLoading, + data: customFilters = [], + refetch, + } = useQuery( + [REACT_QUERY_KEY.GET_CUSTOM_FILTERS, signal], + async () => { + const res = await getCustomFilters({ signal: signal || '' }); + return 'payload' in res && res.payload?.filters ? res.payload.filters : []; + }, + { + enabled: !!signal, + }, + ); + const isDynamicFilters = useMemo(() => customFilters.length > 0, [ customFilters, ]); - const { isFetching: isCustomFiltersLoading } = useQuery< - SuccessResponse | ErrorResponse, - Error - >( - [REACT_QUERY_KEY.GET_CUSTOM_FILTERS, signal], - () => getCustomFilters({ signal: signal || '' }), - { - onSuccess: (data) => { - if ('payload' in data && data.payload?.filters) { - setCustomFilters(data.payload.filters || ([] as FilterType[])); - } - setIsStale(false); - }, - enabled: !!signal && isStale, - }, - ); + const filterConfig = useMemo( () => getFilterConfig(signal, customFilters, config), [config, customFilters, signal], @@ -57,10 +50,9 @@ const useFilterConfig = ({ return { filterConfig, customFilters, - setCustomFilters, isCustomFiltersLoading, isDynamicFilters, - setIsStale, + refetchCustomFilters: refetch, }; }; diff --git a/frontend/src/components/QuickFilters/tests/QuickFilters.test.tsx b/frontend/src/components/QuickFilters/tests/QuickFilters.test.tsx index a251095eb41e..fb3e745f9a42 100644 --- a/frontend/src/components/QuickFilters/tests/QuickFilters.test.tsx +++ b/frontend/src/components/QuickFilters/tests/QuickFilters.test.tsx @@ -364,3 +364,141 @@ describe('Quick Filters with custom filters', () => { jest.useRealTimers(); }); }); + +describe('Quick Filters refetch behavior', () => { + it('fetches custom filters on every mount when signal is provided', async () => { + let getCalls = 0; + + server.use( + rest.get(quickFiltersListURL, (_req, res, ctx) => { + getCalls += 1; + return res(ctx.status(200), ctx.json(quickFiltersListResponse)); + }), + ); + + const { unmount } = render(); + expect(await screen.findByText(FILTER_SERVICE_NAME)).toBeInTheDocument(); + + unmount(); + + render(); + expect(await screen.findByText(FILTER_SERVICE_NAME)).toBeInTheDocument(); + + expect(getCalls).toBe(2); + }); + + it('does not fetch custom filters when signal is undefined', async () => { + let getCalls = 0; + + server.use( + rest.get(quickFiltersListURL, (_req, res, ctx) => { + getCalls += 1; + return res(ctx.status(200), ctx.json(quickFiltersListResponse)); + }), + ); + + render(); + + await waitFor(() => expect(getCalls).toBe(0)); + }); + + it('refetches custom filters after saving settings', async () => { + let getCalls = 0; + putHandler.mockClear(); + + server.use( + rest.get(quickFiltersListURL, (_req, res, ctx) => { + getCalls += 1; + return res(ctx.status(200), ctx.json(quickFiltersListResponse)); + }), + rest.put(saveQuickFiltersURL, async (req, res, ctx) => { + putHandler(await req.json()); + return res(ctx.status(200), ctx.json({})); + }), + ); + + const user = userEvent.setup({ pointerEventsCheck: 0 }); + render(); + + expect(await screen.findByText(FILTER_SERVICE_NAME)).toBeInTheDocument(); + + const icon = await screen.findByTestId(SETTINGS_ICON_TEST_ID); + const settingsButton = icon.closest('button') ?? icon; + await user.click(settingsButton); + + const target = await screen.findByText(FILTER_OS_DESCRIPTION); + const removeBtn = target.parentElement?.querySelector( + 'button', + ) as HTMLButtonElement; + await user.click(removeBtn); + + await user.click(screen.getByText(SAVE_CHANGES_TEXT)); + + await waitFor(() => expect(putHandler).toHaveBeenCalled()); + await waitFor(() => expect(getCalls).toBeGreaterThanOrEqual(2)); + }); + + it('renders updated filters after refetch post-save', async () => { + const updatedResponse = { + ...quickFiltersListResponse, + data: { + ...quickFiltersListResponse.data, + filters: [ + ...(quickFiltersListResponse.data.filters ?? []), + { + key: 'new.custom.filter', + dataType: 'string', + type: 'resource', + } as const, + ], + }, + }; + + let getCount = 0; + server.use( + rest.get(quickFiltersListURL, (_req, res, ctx) => { + getCount += 1; + return getCount >= 2 + ? res(ctx.status(200), ctx.json(updatedResponse)) + : res(ctx.status(200), ctx.json(quickFiltersListResponse)); + }), + rest.put(saveQuickFiltersURL, async (_req, res, ctx) => + res(ctx.status(200), ctx.json({})), + ), + ); + + const user = userEvent.setup({ pointerEventsCheck: 0 }); + render(); + + expect(await screen.findByText(FILTER_SERVICE_NAME)).toBeInTheDocument(); + + const icon = await screen.findByTestId(SETTINGS_ICON_TEST_ID); + const settingsButton = icon.closest('button') ?? icon; + await user.click(settingsButton); + + // Make a minimal change so Save button appears + const target = await screen.findByText(FILTER_OS_DESCRIPTION); + const removeBtn = target.parentElement?.querySelector( + 'button', + ) as HTMLButtonElement; + await user.click(removeBtn); + + await user.click(screen.getByText(SAVE_CHANGES_TEXT)); + + await waitFor(() => { + expect(screen.getByText('New Custom Filter')).toBeInTheDocument(); + }); + }); + + it('shows empty state when GET fails', async () => { + server.use( + rest.get(quickFiltersListURL, (_req, res, ctx) => + res(ctx.status(500), ctx.json({})), + ), + ); + + render(); + + expect(await screen.findByText('No filters found')).toBeInTheDocument(); + }); +});