diff --git a/src/plugins/discover/public/application/main/services/discover_state.ts b/src/plugins/discover/public/application/main/services/discover_state.ts index bc33b2f1ff09d..86d279ec09c20 100644 --- a/src/plugins/discover/public/application/main/services/discover_state.ts +++ b/src/plugins/discover/public/application/main/services/discover_state.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { isEqual, cloneDeep } from 'lodash'; +import { cloneDeep, isEqual } from 'lodash'; import { i18n } from '@kbn/i18n'; import { History } from 'history'; import { NotificationsStart, IUiSettingsClient } from '@kbn/core/public'; @@ -34,12 +34,12 @@ import { syncQueryStateWithUrl, } from '@kbn/data-plugin/public'; import { DataView } from '@kbn/data-views-plugin/public'; -import { migrateLegacyQuery } from '../../../utils/migrate_legacy_query'; import { DiscoverGridSettings } from '../../../components/discover_grid/types'; import { SavedSearch } from '../../../services/saved_searches'; import { handleSourceColumnState } from '../../../utils/state_helpers'; import { DISCOVER_APP_LOCATOR, DiscoverAppLocatorParams } from '../../../locator'; import { VIEW_MODE } from '../../../components/view_mode_toggle'; +import { cleanupUrlState } from '../utils/cleanup_url_state'; export interface AppState { /** @@ -92,6 +92,13 @@ export interface AppState { rowHeight?: number; } +export interface AppStateUrl extends Omit { + /** + * Necessary to take care of legacy links [fieldName,direction] + */ + sort?: string[][] | [string, string]; +} + interface GetStateParams { /** * Default state used for merging with with URL state to get the initial state @@ -193,17 +200,7 @@ export function getState({ ...(toasts && withNotifyOnErrors(toasts)), }); - const appStateFromUrl = stateStorage.get(APP_STATE_URL_KEY) as AppState; - - if (appStateFromUrl && appStateFromUrl.query && !appStateFromUrl.query.language) { - appStateFromUrl.query = migrateLegacyQuery(appStateFromUrl.query); - } - - if (appStateFromUrl?.sort && !appStateFromUrl.sort.length) { - // If there's an empty array given in the URL, the sort prop should be removed - // This allows the sort prop to be overwritten with the default sorting - delete appStateFromUrl.sort; - } + const appStateFromUrl = cleanupUrlState(stateStorage.get(APP_STATE_URL_KEY) as AppStateUrl); let initialAppState = handleSourceColumnState( { diff --git a/src/plugins/discover/public/application/main/utils/cleanup_url_state.test.ts b/src/plugins/discover/public/application/main/utils/cleanup_url_state.test.ts new file mode 100644 index 0000000000000..e76e40a45a316 --- /dev/null +++ b/src/plugins/discover/public/application/main/utils/cleanup_url_state.test.ts @@ -0,0 +1,58 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { cleanupUrlState } from './cleanup_url_state'; +import { AppStateUrl } from '../services/discover_state'; + +describe('cleanupUrlState', () => { + test('cleaning up legacy sort', async () => { + const state = { sort: ['batman', 'desc'] } as AppStateUrl; + expect(cleanupUrlState(state)).toMatchInlineSnapshot(` + Object { + "sort": Array [ + Array [ + "batman", + "desc", + ], + ], + } + `); + }); + test('not cleaning up broken legacy sort', async () => { + const state = { sort: ['batman'] } as unknown as AppStateUrl; + expect(cleanupUrlState(state)).toMatchInlineSnapshot(`Object {}`); + }); + test('not cleaning up regular sort', async () => { + const state = { + sort: [ + ['batman', 'desc'], + ['robin', 'asc'], + ], + } as AppStateUrl; + expect(cleanupUrlState(state)).toMatchInlineSnapshot(` + Object { + "sort": Array [ + Array [ + "batman", + "desc", + ], + Array [ + "robin", + "asc", + ], + ], + } + `); + }); + test('removing empty sort', async () => { + const state = { + sort: [], + } as AppStateUrl; + expect(cleanupUrlState(state)).toMatchInlineSnapshot(`Object {}`); + }); +}); diff --git a/src/plugins/discover/public/application/main/utils/cleanup_url_state.ts b/src/plugins/discover/public/application/main/utils/cleanup_url_state.ts new file mode 100644 index 0000000000000..0148931f73c58 --- /dev/null +++ b/src/plugins/discover/public/application/main/utils/cleanup_url_state.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { migrateLegacyQuery } from '../../../utils/migrate_legacy_query'; +import { AppState, AppStateUrl } from '../services/discover_state'; + +/** + * Takes care of the given url state, migrates legacy props and cleans up empty props + * @param appStateFromUrl + */ +export function cleanupUrlState(appStateFromUrl: AppStateUrl): AppState { + if (appStateFromUrl && appStateFromUrl.query && !appStateFromUrl.query.language) { + appStateFromUrl.query = migrateLegacyQuery(appStateFromUrl.query); + } + + if (typeof appStateFromUrl?.sort?.[0] === 'string') { + if (appStateFromUrl?.sort?.[1] === 'asc' || appStateFromUrl.sort[1] === 'desc') { + // handling sort props like this[fieldName,direction] + appStateFromUrl.sort = [[appStateFromUrl.sort[0], appStateFromUrl.sort[1]]]; + } else { + delete appStateFromUrl.sort; + } + } + + if (appStateFromUrl?.sort && !appStateFromUrl.sort.length) { + // If there's an empty array given in the URL, the sort prop should be removed + // This allows the sort prop to be overwritten with the default sorting + delete appStateFromUrl.sort; + } + return appStateFromUrl as AppState; +}