Apply URL overrides to saved searches (#25051)

* Apply URL overrides to saved searches.

* Adding comment

* Adding changelog

* Extend unit tests
This commit is contained in:
Linus Pahl
2026-03-03 14:34:14 +01:00
committed by GitHub
parent a38202f532
commit 5bc5da03d2
5 changed files with 41 additions and 3 deletions

View File

@@ -0,0 +1,5 @@
type = "fixed"
message = "Fix saved search URL query overrides to execute with URL values on initial search execution."
issues = ["25028"]
pulls = ["25051"]

View File

@@ -25,7 +25,7 @@ import { selectView } from 'views/logic/slices/viewSelectors';
import { selectSearchExecutionState } from 'views/logic/slices/searchExecutionSelectors';
import useLocation from 'routing/useLocation';
import useQuery from 'routing/useQuery';
import { updateView } from 'views/logic/slices/viewSlice';
import { updateView, executeActiveQuery } from 'views/logic/slices/viewSlice';
const bindSearchParamsFromQueryThunk =
(query: { [key: string]: unknown }) => async (dispatch: ViewsDispatch, getState: () => RootState) => {
@@ -41,7 +41,10 @@ const bindSearchParamsFromQueryThunk =
const [newView] = result;
if (newView !== view) {
return dispatch(updateView(newView, true));
// Update view, but don't create new search because this already happened in bindSearchParamsFromQuery
await dispatch(updateView(newView));
return dispatch(executeActiveQuery());
}
};

View File

@@ -73,6 +73,27 @@ describe('BindSearchParamsFromQuery should', () => {
const [newView] = await bindSearchParamsFromQuery(input);
expect(findMockQuery(newView).query.query_string).toBe('gl2_source_input:source-input-id');
expect(newView.search.id).not.toBe(view.search.id);
});
it('apply URL query override without mutating the original saved query', async () => {
const savedQuery = Query.builder()
.id(MOCK_VIEW_QUERY_ID)
.query(createElasticsearchQueryString('persisted:query'))
.build();
const savedSearch = Search.create().toBuilder().queries([savedQuery]).build();
const savedView = view.toBuilder().search(savedSearch).build();
const input = {
...defaultInput,
view: savedView,
query: { q: 'override:query' },
};
const [newView] = await bindSearchParamsFromQuery(input);
expect(findMockQuery(newView).query.query_string).toBe('override:query');
expect(findMockQuery(savedView).query.query_string).toBe('persisted:query');
expect(newView.search.id).not.toBe(savedView.search.id);
});
it('not update query string when no query param is provided', async () => {

View File

@@ -20,6 +20,7 @@ import isDeepEqual from 'stores/isDeepEqual';
import type { ViewHook, ViewHookArguments } from 'views/logic/hooks/ViewHook';
import View from 'views/logic/views/View';
import normalizeSearchURLQueryParams from 'views/logic/NormalizeSearchURLQueryParams';
import createSearch from 'views/logic/slices/createSearch';
const bindSearchParamsFromQuery: ViewHook = async ({ query, view, executionState }: ViewHookArguments) => {
if (view.type !== View.Type.Search) {
@@ -67,7 +68,7 @@ const bindSearchParamsFromQuery: ViewHook = async ({ query, view, executionState
return [view, executionState];
}
const newSearch = view.search.toBuilder().queries([newQuery]).build();
const newSearch = await createSearch(view.search.toBuilder().newId().queries([newQuery]).build());
const newView = view.toBuilder().search(newSearch).build();
return [newView, executionState];

View File

@@ -87,6 +87,14 @@ describe('SyncWithQueryParameters', () => {
);
});
it('preserves a saved-search URL query override and appends missing timerange params', () => {
asMock(useCurrentQuery).mockReturnValue(createQuery(lastFiveMinutes, [], 'new-query'));
renderHook(() => useSyncWithQueryParameters('/search/example-search-id?q=new-query'));
expect(history.replace).toHaveBeenCalledWith('/search/example-search-id?q=new-query&rangetype=relative&from=300');
expect(history.push).not.toHaveBeenCalled();
});
it('if time range is relative with from and to', () => {
asMock(useCurrentQuery).mockReturnValue(createQuery({ ...lastFiveMinutes, to: 240 }));
renderHook(() => useSyncWithQueryParameters('/search'));