From afa61125e69a02959f633711e1ef82710152656f Mon Sep 17 00:00:00 2001 From: Pablo Neves Machado Date: Thu, 14 Jul 2022 10:10:35 +0200 Subject: [PATCH] General improvements and refactor Move everything inside use_url_state and delete the folder --- .../public/app/home/index.test.tsx | 136 +++++++++-- .../components/events_viewer/index.test.tsx | 2 +- .../plugins/timeline/parser.ts | 3 +- .../components/navigation/helpers.test.tsx | 8 +- .../common/components/navigation/helpers.ts | 6 +- .../common/components/navigation/index.tsx | 37 +-- .../common/components/navigation/types.ts | 18 +- .../components/sessions_viewer/index.test.tsx | 2 +- .../common/components/sourcerer/index.tsx | 4 +- .../common/components/url_state/constants.ts | 45 ---- .../components/url_state/index.test.tsx | 196 ---------------- .../url_state/index_mocked.test.tsx | 217 ------------------ ...query_timeline_by_id_on_url_change.test.ts | 140 ----------- .../components/url_state/test_dependencies.ts | 213 ----------------- .../common/components/url_state/types.ts | 10 - .../common/containers/sourcerer/index.tsx | 6 +- .../use_init_search_bar_url_params.ts | 8 +- .../use_init_timerange_url_params.ts | 6 +- .../use_sync_search_bar_url_param.ts | 8 +- .../use_sync_timerange_url_param.ts | 8 +- .../use_update_timerange_on_page_change.ts | 2 +- .../timeline/use_init_timeline_url_param.ts | 6 +- .../timeline/use_sync_timeline_url_param.ts | 10 +- ...use_update_timeline_on_page_change.test.ts | 147 ++++++++++++ .../use_update_timeline_on_page_change.ts | 11 +- .../hooks/use_resolve_conflict.test.tsx | 2 +- .../common/hooks/use_resolve_conflict.tsx | 10 +- .../common/hooks/use_resolve_redirect.test.ts | 2 +- .../common/hooks/use_resolve_redirect.ts | 10 +- .../use_update_browser_title.test.ts} | 4 +- .../public/common/hooks/use_url_state.ts | 13 +- .../public/common/store/inputs/model.ts | 4 +- .../__mocks__/normalize_time_range.ts | 0 .../global_query_string}/helpers.ts | 0 .../common/utils/global_query_string/index.ts | 2 +- .../normalize_time_range.test.ts | 8 +- .../normalize_time_range.ts | 6 +- .../rule_preview/preview_histogram.test.tsx | 2 +- .../hosts/pages/details/details_tabs.test.tsx | 2 +- .../components/flyout/pane/index.test.tsx | 2 +- .../components/open_timeline/helpers.test.ts | 2 +- .../components/open_timeline/helpers.ts | 2 +- .../components/timeline/index.test.tsx | 2 +- .../timelines/store/timeline/defaults.ts | 2 +- .../timelines/store/timeline/helpers.ts | 2 +- .../timelines/store/timeline/reducer.test.ts | 2 +- 46 files changed, 383 insertions(+), 945 deletions(-) delete mode 100644 x-pack/plugins/security_solution/public/common/components/url_state/constants.ts delete mode 100644 x-pack/plugins/security_solution/public/common/components/url_state/index.test.tsx delete mode 100644 x-pack/plugins/security_solution/public/common/components/url_state/index_mocked.test.tsx delete mode 100644 x-pack/plugins/security_solution/public/common/components/url_state/query_timeline_by_id_on_url_change.test.ts delete mode 100644 x-pack/plugins/security_solution/public/common/components/url_state/test_dependencies.ts delete mode 100644 x-pack/plugins/security_solution/public/common/components/url_state/types.ts create mode 100644 x-pack/plugins/security_solution/public/common/hooks/timeline/use_update_timeline_on_page_change.test.ts rename x-pack/plugins/security_solution/public/common/{components/url_state/helpers.test.ts => hooks/use_update_browser_title.test.ts} (90%) rename x-pack/plugins/security_solution/public/common/{components/url_state => utils}/__mocks__/normalize_time_range.ts (100%) rename x-pack/plugins/security_solution/public/common/{components/url_state => utils/global_query_string}/helpers.ts (100%) rename x-pack/plugins/security_solution/public/common/{components/url_state => utils}/normalize_time_range.test.ts (97%) rename x-pack/plugins/security_solution/public/common/{components/url_state => utils}/normalize_time_range.ts (80%) diff --git a/x-pack/plugins/security_solution/public/app/home/index.test.tsx b/x-pack/plugins/security_solution/public/app/home/index.test.tsx index a81b6069381c24c..4796249b6dbb40a 100644 --- a/x-pack/plugins/security_solution/public/app/home/index.test.tsx +++ b/x-pack/plugins/security_solution/public/app/home/index.test.tsx @@ -10,7 +10,6 @@ import React from 'react'; import { HomePage } from '.'; import type { SavedQuery } from '@kbn/data-plugin/public'; import { FilterManager } from '@kbn/data-plugin/public'; -import { CONSTANTS } from '../../common/components/url_state/constants'; import { createSecuritySolutionStorageMock, @@ -30,6 +29,9 @@ import type { Filter } from '@kbn/es-query'; import { createStore } from '../../common/store'; import type { TimeRange, UrlInputsModel } from '../../common/store/inputs/model'; import { SecurityPageName } from '../types'; +import type { TimelineUrl } from '../../timelines/store/timeline/model'; +import { timelineDefaults } from '../../timelines/store/timeline/defaults'; +import { URL_PARAM_KEY } from '../../common/hooks/use_url_state'; jest.mock('../../common/store/inputs/actions'); @@ -51,12 +53,15 @@ const mockUseInitializeUrlParam = (urlParamKey: string, state: unknown) => { }); }; +const mockUpdateUrlParam = jest.fn(); + jest.mock('../../common/utils/global_query_string', () => { const original = jest.requireActual('../../common/utils/global_query_string'); return { ...original, useInitializeUrlParam: (...params: unknown[]) => mockedUseInitializeUrlParam(...params), useSyncGlobalQueryString: jest.fn(), + useUpdateUrlParam: () => mockUpdateUrlParam, }; }); @@ -80,6 +85,23 @@ jest.mock('react-router-dom', () => { }; }); +const mockQueryTimelineById = jest.fn(); +jest.mock('../../timelines/components/open_timeline/helpers', () => { + const original = jest.requireActual('../../timelines/components/open_timeline/helpers'); + return { + ...original, + queryTimelineById: (params: unknown) => mockQueryTimelineById(params), + }; +}); + +const mockGetTimiline = jest.fn(); + +jest.mock('../../timelines/store/timeline', () => ({ + timelineSelectors: { + getTimelineByIdSelector: () => mockGetTimiline, + }, +})); + const mockedFilterManager = new FilterManager(coreMock.createStart().uiSettings); const mockGetSavedQuery = jest.fn(); @@ -152,22 +174,22 @@ describe('HomePage', () => { ); expect(mockedUseInitializeUrlParam).toHaveBeenCalledWith( - CONSTANTS.appQuery, + URL_PARAM_KEY.appQuery, expect.any(Function) ); expect(mockedUseInitializeUrlParam).toHaveBeenCalledWith( - CONSTANTS.filters, + URL_PARAM_KEY.filters, expect.any(Function) ); expect(mockedUseInitializeUrlParam).toHaveBeenCalledWith( - CONSTANTS.savedQuery, + URL_PARAM_KEY.savedQuery, expect.any(Function) ); }); it('dispatches setFilterQuery when initializing appQuery', () => { const state = { query: 'testQuery', language: 'en' }; - mockUseInitializeUrlParam(CONSTANTS.appQuery, state); + mockUseInitializeUrlParam(URL_PARAM_KEY.appQuery, state); render( @@ -208,7 +230,7 @@ describe('HomePage', () => { }; mockGetSavedQuery.mockResolvedValue(savedQueryData); - mockUseInitializeUrlParam(CONSTANTS.savedQuery, state); + mockUseInitializeUrlParam(URL_PARAM_KEY.savedQuery, state); render( @@ -239,7 +261,7 @@ describe('HomePage', () => { describe('Filters', () => { it('sets filter initial value in the store and filterManager', () => { const state = [{ testFilter: 'test' }]; - mockUseInitializeUrlParam(CONSTANTS.filters, state); + mockUseInitializeUrlParam(URL_PARAM_KEY.filters, state); const spySetFilters = jest.spyOn(mockedFilterManager, 'setFilters'); render( @@ -260,7 +282,7 @@ describe('HomePage', () => { it('sets filter from store when URL param has no value', () => { const state = null; - mockUseInitializeUrlParam(CONSTANTS.filters, state); + mockUseInitializeUrlParam(URL_PARAM_KEY.filters, state); const spySetAppFilters = jest.spyOn(mockedFilterManager, 'setAppFilters'); const { storage } = createSecuritySolutionStorageMock(); @@ -290,7 +312,7 @@ describe('HomePage', () => { it('preserves pinned filters when URL param has no value', () => { const state = null; - mockUseInitializeUrlParam(CONSTANTS.filters, state); + mockUseInitializeUrlParam(URL_PARAM_KEY.filters, state); // pin filter mockedFilterManager.setGlobalFilters([dummyFilter]); @@ -325,16 +347,16 @@ describe('HomePage', () => { const state: UrlInputsModel = { global: { - [CONSTANTS.timerange]: timerange, + [URL_PARAM_KEY.timerange]: timerange, linkTo: ['timeline'], }, timeline: { - [CONSTANTS.timerange]: timerange, + [URL_PARAM_KEY.timerange]: timerange, linkTo: ['global'], }, }; - mockUseInitializeUrlParam(CONSTANTS.timerange, state); + mockUseInitializeUrlParam(URL_PARAM_KEY.timerange, state); render( @@ -370,16 +392,16 @@ describe('HomePage', () => { const state: UrlInputsModel = { global: { - [CONSTANTS.timerange]: timerange, + [URL_PARAM_KEY.timerange]: timerange, linkTo: ['timeline'], }, timeline: { - [CONSTANTS.timerange]: timerange, + [URL_PARAM_KEY.timerange]: timerange, linkTo: ['global'], }, }; - mockUseInitializeUrlParam(CONSTANTS.timerange, state); + mockUseInitializeUrlParam(URL_PARAM_KEY.timerange, state); render( @@ -496,7 +518,6 @@ describe('HomePage', () => { ); - // mockedUseInitializeUrlParam.mockImplementation(jest.fn()); const { rerender } = render(); jest.clearAllMocks(); @@ -519,4 +540,87 @@ describe('HomePage', () => { }); }); }); + + describe('Timeline', () => { + it('initializes Timeline store', () => { + const timeline: TimelineUrl = { + id: 'testSavedTimelineId', + isOpen: false, + }; + + mockUseInitializeUrlParam(URL_PARAM_KEY.timeline, timeline); + + render( + + + + + + ); + + expect(mockQueryTimelineById).toHaveBeenCalledWith( + expect.objectContaining({ + timelineId: timeline.id, + openTimeline: timeline.isOpen, + }) + ); + }); + + it('it removes empty timeline state from URL', async () => { + const { storage } = createSecuritySolutionStorageMock(); + const store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); + + mockUseInitializeUrlParam(URL_PARAM_KEY.timeline, { + id: 'testSavedTimelineId', + isOpen: false, + }); + + const TestComponent = () => ( + + + + + + ); + + const { rerender } = render(); + + jest.clearAllMocks(); + mockGetTimiline.mockReturnValue({ ...timelineDefaults, savedObjectId: null }); + + rerender(); + + expect(mockUpdateUrlParam).toHaveBeenCalledWith(null); + }); + + it('it updates URL when timeline store changes', async () => { + const { storage } = createSecuritySolutionStorageMock(); + const store = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, kibanaObservable, storage); + const savedObjectId = 'testTimelineId'; + + mockUseInitializeUrlParam(URL_PARAM_KEY.timeline, { + id: 'testSavedTimelineId', + isOpen: false, + }); + + const TestComponent = () => ( + + + + + + ); + + const { rerender } = render(); + + jest.clearAllMocks(); + mockGetTimiline.mockReturnValue({ ...timelineDefaults, savedObjectId }); + + rerender(); + + expect(mockUpdateUrlParam).toHaveBeenCalledWith( + expect.objectContaining({ id: savedObjectId }) + ); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx index c96fa89380e8c4a..0f5f69f7b08768a 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx @@ -39,7 +39,7 @@ jest.mock('../../../timelines/containers', () => ({ useTimelineEvents: jest.fn(), })); -jest.mock('../url_state/normalize_time_range'); +jest.mock('../../utils/normalize_time_range'); const mockUseFieldBrowserOptions = jest.fn(); jest.mock('../../../timelines/components/fields_browser', () => ({ diff --git a/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/timeline/parser.ts b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/timeline/parser.ts index b8c2825ea1c36b4..1d48cc4d56dd818 100644 --- a/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/timeline/parser.ts +++ b/x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/timeline/parser.ts @@ -8,7 +8,8 @@ import type { Plugin } from 'unified'; import type { RemarkTokenizer } from '@elastic/eui'; import { parse } from 'query-string'; -import { decodeRisonUrlState } from '../../../url_state/helpers'; +import { decodeRisonUrlState } from '../../../../utils/global_query_string/helpers'; + import { ID, PREFIX } from './constants'; import * as i18n from './translations'; diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/helpers.test.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/helpers.test.tsx index 3383b116cf6dbe3..26d708d43b39e2f 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/helpers.test.tsx @@ -10,16 +10,16 @@ import { getSearch } from './helpers'; describe('helpers', () => { it('returns the search string', () => { - const serachNavTab: SearchNavTab = { urlKey: 'host', isDetailPage: false }; + const searchNavTab: SearchNavTab = { urlKey: 'host', isDetailPage: false }; const globalQueryString = 'test=123'; - expect(getSearch(serachNavTab, globalQueryString)).toEqual('?&test=123'); + expect(getSearch(searchNavTab, globalQueryString)).toEqual('?test=123'); }); it('returns an empty string when globalQueryString is empty', () => { - const serachNavTab: SearchNavTab = { urlKey: 'host', isDetailPage: false }; + const searchNavTab: SearchNavTab = { urlKey: 'host', isDetailPage: false }; const globalQueryString = ''; - expect(getSearch(serachNavTab, globalQueryString)).toEqual(''); + expect(getSearch(searchNavTab, globalQueryString)).toEqual(''); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/helpers.ts b/x-pack/plugins/security_solution/public/common/components/navigation/helpers.ts index 18d55c2c0fe3021..e8fd2edbf99438b 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/helpers.ts +++ b/x-pack/plugins/security_solution/public/common/components/navigation/helpers.ts @@ -5,9 +5,7 @@ * 2.0. */ -import { isAdministration } from '../url_state/types'; - -import type { SearchNavTab } from './types'; +import type { SearchNavTab, UrlStateType } from './types'; import { useIsExperimentalFeatureEnabled } from '../../hooks/use_experimental_features'; import { useUiSetting$ } from '../../lib/kibana'; import { ENABLE_GROUPED_NAVIGATION } from '../../../../common/constants'; @@ -20,6 +18,8 @@ export const getSearch = (tab: SearchNavTab, globalQueryString: string): string return ''; }; +export const isAdministration = (urlKey: UrlStateType): boolean => 'administration' === urlKey; + /** * Hook to check if the new grouped navigation is enabled on both experimental flag and advanced settings * TODO: remove this function when flag and setting not needed diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/index.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/index.tsx index 41f1dd11caf49b3..26a5c4f6f3bece3 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/index.tsx @@ -6,14 +6,12 @@ */ import React, { useEffect } from 'react'; -import { connect } from 'react-redux'; -import { compose } from 'redux'; + import deepEqual from 'fast-deep-equal'; import { useKibana } from '../../lib/kibana'; import type { RouteSpyState } from '../../utils/route/types'; import { useRouteSpy } from '../../utils/route/use_route_spy'; -import { makeMapStateToProps } from '../url_state/helpers'; import { useSetBreadcrumbs } from './breadcrumbs'; import { TabNavigation } from './tab_navigation'; import type { TabNavigationComponentProps, SecuritySolutionTabNavigationProps } from './types'; @@ -25,18 +23,7 @@ import type { TabNavigationComponentProps, SecuritySolutionTabNavigationProps } export const TabNavigationComponent: React.FC< RouteSpyState & SecuritySolutionTabNavigationProps & TabNavigationComponentProps > = React.memo( - ({ - detailName, - display, - flowTarget, - navTabs, - pageName, - pathName, - search, - state, - tabName, - urlState, - }) => { + ({ detailName, display, flowTarget, navTabs, pageName, pathName, search, state, tabName }) => { const { chrome, application: { getUrlForApp, navigateToUrl }, @@ -83,25 +70,19 @@ export const TabNavigationComponent: React.FC< pageName={pageName} pathName={pathName} tabName={tabName} - timeline={urlState.timeline} /> ); } ); TabNavigationComponent.displayName = 'TabNavigationComponent'; -export const SecuritySolutionTabNavigationRedux = compose< - React.ComponentClass ->(connect(makeMapStateToProps))( - React.memo( - TabNavigationComponent, - (prevProps, nextProps) => - prevProps.pathName === nextProps.pathName && - prevProps.search === nextProps.search && - deepEqual(prevProps.navTabs, nextProps.navTabs) && - deepEqual(prevProps.urlState, nextProps.urlState) && - deepEqual(prevProps.state, nextProps.state) - ) +export const SecuritySolutionTabNavigationRedux = React.memo( + TabNavigationComponent, + (prevProps, nextProps) => + prevProps.pathName === nextProps.pathName && + prevProps.search === nextProps.search && + deepEqual(prevProps.navTabs, nextProps.navTabs) && + deepEqual(prevProps.state, nextProps.state) ); export const SecuritySolutionTabNavigation: React.FC = diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/types.ts b/x-pack/plugins/security_solution/public/common/components/navigation/types.ts index 8c7b4f9edea14c2..2df3122737c804e 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/navigation/types.ts @@ -6,7 +6,6 @@ */ import type { IconType } from '@elastic/eui'; -import type { UrlStateType } from '../url_state/constants'; import { SecurityPageName } from '../../../app/types'; import type { SiemRouteType } from '../../utils/route/types'; import type { LinkCategories } from '../../links'; @@ -31,6 +30,23 @@ export enum SecurityNavGroupKey { manage = 'manage', } +export type UrlStateType = + | 'administration' + | 'alerts' + | 'cases' + | 'detection_response' + | 'exceptions' + | 'get_started' + | 'host' + | 'users' + | 'network' + | 'kubernetes' + | 'overview' + | 'rules' + | 'timeline' + | 'explore' + | 'dashboards'; + export type SecurityNavGroup = Record; export interface NavTab { id: string; diff --git a/x-pack/plugins/security_solution/public/common/components/sessions_viewer/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/sessions_viewer/index.test.tsx index beacde94317cb67..a89a618972326fb 100644 --- a/x-pack/plugins/security_solution/public/common/components/sessions_viewer/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/sessions_viewer/index.test.tsx @@ -24,7 +24,7 @@ const originalKibanaLib = jest.requireActual('../../lib/kibana'); const mockUseGetUserCasesPermissions = useGetUserCasesPermissions as jest.Mock; mockUseGetUserCasesPermissions.mockImplementation(originalKibanaLib.useGetUserCasesPermissions); -jest.mock('../url_state/normalize_time_range'); +jest.mock('../../utils/normalize_time_range'); const startDate = '2022-03-22T22:10:56.794Z'; const endDate = '2022-03-21T22:10:56.791Z'; diff --git a/x-pack/plugins/security_solution/public/common/components/sourcerer/index.tsx b/x-pack/plugins/security_solution/public/common/components/sourcerer/index.tsx index 39ff50d361c9d17..a0bb1f3f27ff489 100644 --- a/x-pack/plugins/security_solution/public/common/components/sourcerer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/sourcerer/index.tsx @@ -33,7 +33,7 @@ import { Trigger } from './trigger'; import { AlertsCheckbox, SaveButtons, SourcererCallout } from './sub_components'; import { useSignalHelpers } from '../../containers/sourcerer/use_signal_helpers'; import { useUpdateUrlParam } from '../../utils/global_query_string'; -import { CONSTANTS } from '../url_state/constants'; +import { URL_PARAM_KEY } from '../../hooks/use_url_state'; export interface SourcererComponentProps { scope: sourcererModel.SourcererScopeName; @@ -44,7 +44,7 @@ export const Sourcerer = React.memo(({ scope: scopeId } const isDetectionsSourcerer = scopeId === SourcererScopeName.detections; const isTimelineSourcerer = scopeId === SourcererScopeName.timeline; const isDefaultSourcerer = scopeId === SourcererScopeName.default; - const updateUrlParam = useUpdateUrlParam(CONSTANTS.sourcerer); + const updateUrlParam = useUpdateUrlParam(URL_PARAM_KEY.sourcerer); const sourcererScopeSelector = useMemo(() => sourcererSelectors.getSourcererScopeSelector(), []); const { diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/constants.ts b/x-pack/plugins/security_solution/public/common/components/url_state/constants.ts deleted file mode 100644 index 6e421b57a90c2ea..000000000000000 --- a/x-pack/plugins/security_solution/public/common/components/url_state/constants.ts +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export enum CONSTANTS { - appQuery = 'query', - alertsPage = 'alerts.page', - caseDetails = 'case.details', - casePage = 'case.page', - filters = 'filters', - hostsDetails = 'hosts.details', - hostsPage = 'hosts.page', - kubernetesDetails = 'kubernetes.details', - kubernetesPage = 'kubernetes.page', - management = 'management', - networkDetails = 'network.details', - networkPage = 'network.page', - overviewPage = 'overview.page', - savedQuery = 'savedQuery', - sourcerer = 'sourcerer', - timeline = 'timeline', - timelinePage = 'timeline.page', - timerange = 'timerange', - unknown = 'unknown', -} - -export type UrlStateType = - | 'administration' - | 'alerts' - | 'cases' - | 'detection_response' - | 'exceptions' - | 'get_started' - | 'host' - | 'users' - | 'network' - | 'kubernetes' - | 'overview' - | 'rules' - | 'timeline' - | 'explore' - | 'dashboards'; diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/url_state/index.test.tsx deleted file mode 100644 index 8df37b42a18b03b..000000000000000 --- a/x-pack/plugins/security_solution/public/common/components/url_state/index.test.tsx +++ /dev/null @@ -1,196 +0,0 @@ -/* - * 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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -// /* -// * 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; you may not use this file except in compliance with the Elastic License -// * 2.0. -// */ - -// import { mount } from 'enzyme'; -// import React from 'react'; - -// import { HookWrapper } from '../../mock'; -// import { SecurityPageName } from '../../../app/types'; -// import type { RouteSpyState } from '../../utils/route/types'; -// import { CONSTANTS } from './constants'; -// import { getMockPropsObj, mockHistory, getMockProps } from './test_dependencies'; -// import type { UrlStateContainerPropTypes } from './types'; -// import { useUrlStateHooks } from './use_url_state'; -// import { useLocation } from 'react-router-dom'; -// import { updateAppLinks } from '../../links'; -// import { links } from '../../links/app_links'; -// import { allowedExperimentalValues } from '../../../../common/experimental_features'; - -// let mockProps: UrlStateContainerPropTypes; - -// const mockRouteSpy: RouteSpyState = { -// pageName: SecurityPageName.network, -// detailName: undefined, -// tabName: undefined, -// search: '', -// pathName: '/network', -// }; -// jest.mock('../../utils/route/use_route_spy', () => ({ -// useRouteSpy: () => [mockRouteSpy], -// })); - -// jest.mock('../super_date_picker', () => ({ -// formatDate: (date: string) => { -// return '2020-01-01T00:00:00.000Z'; -// }, -// })); - -// jest.mock('../../lib/kibana', () => ({ -// useKibana: () => ({ -// services: { -// data: { -// query: { -// filterManager: {}, -// savedQueries: {}, -// }, -// }, -// }, -// }), -// KibanaServices: { -// get: jest.fn(() => ({ uiSettings: { get: () => ({ from: 'now-24h', to: 'now' }) } })), -// }, -// })); - -// jest.mock('react-redux', () => { -// const original = jest.requireActual('react-redux'); -// return { -// ...original, -// useDispatch: () => jest.fn(), -// }; -// }); - -// jest.mock('react-router-dom', () => { -// const original = jest.requireActual('react-router-dom'); - -// return { -// ...original, -// useLocation: jest.fn(), -// }; -// }); - -// const mockedUseIsGroupedNavigationEnabled = jest.fn(); -// jest.mock('../navigation/helpers', () => ({ -// useIsGroupedNavigationEnabled: () => mockedUseIsGroupedNavigationEnabled(), -// })); - -// describe('UrlStateContainer', () => { -// beforeAll(() => { -// mockedUseIsGroupedNavigationEnabled.mockReturnValue(false); -// updateAppLinks(links, { -// experimentalFeatures: allowedExperimentalValues, -// capabilities: { -// navLinks: {}, -// management: {}, -// catalogue: {}, -// actions: { show: true, crud: true }, -// siem: { -// show: true, -// crud: true, -// }, -// }, -// }); -// }); - -// afterEach(() => { -// jest.clearAllMocks(); -// mockedUseIsGroupedNavigationEnabled.mockReturnValue(false); -// }); - -// describe('handleInitialize', () => { -// it("it doesn't update URL state when pathName and browserPAth are out of sync", () => { -// mockProps = getMockPropsObj({ -// page: CONSTANTS.networkPage, -// examplePath: '/network', -// namespaceLower: 'network', -// pageName: SecurityPageName.network, -// detailName: undefined, -// }).noSearch.undefinedQuery; - -// (useLocation as jest.Mock).mockReturnValue({ -// pathname: 'out of sync path', -// search: mockProps.search, -// }); - -// mount( useUrlStateHooks(args)} />); - -// expect(mockHistory.replace).not.toHaveBeenCalled(); -// }); - -// it("it doesn't update URL state when on admin page and grouped nav disabled", () => { -// mockedUseIsGroupedNavigationEnabled.mockReturnValue(false); -// mockProps = getMockPropsObj({ -// page: CONSTANTS.unknown, -// examplePath: '/administration', -// namespaceLower: 'administration', -// pageName: SecurityPageName.administration, -// detailName: undefined, -// }).noSearch.undefinedQuery; - -// (useLocation as jest.Mock).mockReturnValue({ -// pathname: mockProps.pathName, -// search: mockProps.search, -// }); - -// mount( useUrlStateHooks(args)} />); - -// expect(mockHistory.replace.mock.calls[0][0].search).toBe('?'); -// }); - -// it("it doesn't update URL state when on admin page and grouped nav enabled", () => { -// mockedUseIsGroupedNavigationEnabled.mockReturnValue(true); -// mockProps = getMockPropsObj({ -// page: CONSTANTS.unknown, -// examplePath: '/dashboards', -// namespaceLower: 'dashboards', -// pageName: SecurityPageName.dashboardsLanding, -// detailName: undefined, -// }).noSearch.undefinedQuery; - -// (useLocation as jest.Mock).mockReturnValue({ -// pathname: mockProps.pathName, -// search: mockProps.search, -// }); - -// mount( useUrlStateHooks(args)} />); - -// expect(mockHistory.replace.mock.calls[0][0].search).toBe('?'); -// }); - -// it('it removes empty timeline state from URL', () => { -// mockProps = { -// ...getMockProps( -// { -// hash: '', -// pathname: '/network', -// search: "?timeline=(id:'',isOpen:!t)", -// state: '', -// }, -// CONSTANTS.networkPage, -// null, -// SecurityPageName.network, -// undefined -// ), -// }; - -// (useLocation as jest.Mock).mockReturnValue({ -// pathname: mockProps.pathName, -// search: mockProps.search, -// }); - -// mount( useUrlStateHooks(args)} />); - -// expect(mockHistory.replace.mock.calls[0][0].search).not.toContain('timeline='); -// }); -// }); -// }); diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/index_mocked.test.tsx b/x-pack/plugins/security_solution/public/common/components/url_state/index_mocked.test.tsx deleted file mode 100644 index 02ecb50c77f6bfa..000000000000000 --- a/x-pack/plugins/security_solution/public/common/components/url_state/index_mocked.test.tsx +++ /dev/null @@ -1,217 +0,0 @@ -/* - * 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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { links } from '../../links/app_links'; -import { updateAppLinks } from '../../links'; -import { allowedExperimentalValues } from '../../../../common/experimental_features'; - -jest.mock('../../lib/kibana', () => ({ - useKibana: () => ({ - services: { - data: { - query: { - filterManager: {}, - savedQueries: {}, - }, - }, - }, - }), -})); - -jest.mock('react-router-dom', () => ({ - useLocation: jest.fn(), -})); - -jest.mock('react-redux', () => { - const original = jest.requireActual('react-redux'); - return { - ...original, - useDispatch: () => jest.fn(), - }; -}); - -const mockedUseIsGroupedNavigationEnabled = jest.fn(); -jest.mock('../navigation/helpers', () => ({ - useIsGroupedNavigationEnabled: () => mockedUseIsGroupedNavigationEnabled(), -})); - -describe('UrlStateContainer - lodash.throttle mocked to test update url', () => { - beforeAll(() => { - mockedUseIsGroupedNavigationEnabled.mockReturnValue(false); - updateAppLinks(links, { - experimentalFeatures: allowedExperimentalValues, - capabilities: { - navLinks: {}, - management: {}, - catalogue: {}, - actions: { show: true, crud: true }, - siem: { - show: true, - crud: true, - }, - }, - }); - }); - - afterEach(() => { - jest.clearAllMocks(); - jest.resetAllMocks(); - }); - - describe('componentDidUpdate', () => { - // test('timelineID redux state updates the url', () => { - // mockProps = getMockPropsObj({ - // page: CONSTANTS.networkPage, - // examplePath: '/network', - // namespaceLower: 'network', - // pageName: SecurityPageName.network, - // detailName: undefined, - // }).noSearch.undefinedQuery; - // (useLocation as jest.Mock).mockReturnValue({ - // pathname: mockProps.pathName, - // search: mockProps.search, - // }); - // const wrapper = mount( - // useUrlStateHooks(args)} /> - // ); - // const newUrlState = { - // ...mockProps.urlState, - // timeline: { id: 'hello_timeline_id', isOpen: true }, - // }; - // wrapper.setProps({ - // hookProps: { ...mockProps, urlState: newUrlState, isInitializing: false }, - // }); - // wrapper.update(); - // expect(mockHistory.replace.mock.calls[1][0]).toStrictEqual({ - // hash: '', - // pathname: '/network', - // search: '?timeline=(id:hello_timeline_id,isOpen:!t)', - // state: '', - // }); - // }); - // REWRITE REQUIRED - // test("administration page doesn't has query string when grouped nav disabled", () => { - // mockedUseIsGroupedNavigationEnabled.mockReturnValue(false); - // mockProps = getMockPropsObj({ - // page: CONSTANTS.networkPage, - // examplePath: '/network', - // namespaceLower: 'network', - // pageName: SecurityPageName.network, - // detailName: undefined, - // }).noSearch.definedQuery; - // const urlState = { - // }; - // const updatedMockProps = { - // ...getMockPropsObj({ - // ...mockProps, - // page: CONSTANTS.unknown, - // examplePath: MANAGEMENT_PATH, - // namespaceLower: 'administration', - // pageName: SecurityPageName.administration, - // detailName: undefined, - // }).noSearch.definedQuery, - // urlState, - // }; - // (useLocation as jest.Mock).mockReturnValue({ - // pathname: mockProps.pathName, - // search: mockProps.search, - // }); - // const wrapper = mount( - // useUrlStateHooks(args)} - // /> - // ); - // (useLocation as jest.Mock).mockReturnValue({ - // pathname: updatedMockProps.pathName, - // search: mockProps.search, - // }); - // wrapper.setProps({ - // hookProps: updatedMockProps, - // }); - // wrapper.update(); - // expect(mockHistory.replace.mock.calls[1][0]).toStrictEqual({ - // hash: '', - // pathname: MANAGEMENT_PATH, - // search: mockProps.search, - // state: '', - // }); - // }); - // // REWRITE REQUIRED - // test("dashboards page doesn't has query string when grouped nav enabled", () => { - // mockedUseIsGroupedNavigationEnabled.mockReturnValue(true); - // mockProps = getMockPropsObj({ - // page: CONSTANTS.networkPage, - // examplePath: '/network', - // namespaceLower: 'network', - // pageName: SecurityPageName.network, - // detailName: undefined, - // }).noSearch.definedQuery; - // const urlState = { - // ...mockProps.urlState, - // [CONSTANTS.appQuery]: getFilterQuery(), - // [CONSTANTS.timerange]: { - // global: { - // [CONSTANTS.timerange]: { - // from: '2020-07-07T08:20:18.966Z', - // fromStr: 'now-24h', - // kind: 'relative', - // to: '2020-07-08T08:20:18.966Z', - // toStr: 'now', - // }, - // linkTo: ['timeline'], - // }, - // timeline: { - // [CONSTANTS.timerange]: { - // from: '2020-07-07T08:20:18.966Z', - // fromStr: 'now-24h', - // kind: 'relative', - // to: '2020-07-08T08:20:18.966Z', - // toStr: 'now', - // }, - // linkTo: ['global'], - // }, - // }, - // }; - // const updatedMockProps = { - // ...getMockPropsObj({ - // ...mockProps, - // page: CONSTANTS.unknown, - // examplePath: DASHBOARDS_PATH, - // namespaceLower: 'dashboards', - // pageName: SecurityPageName.dashboardsLanding, - // detailName: undefined, - // }).noSearch.definedQuery, - // urlState, - // }; - // (useLocation as jest.Mock).mockReturnValue({ - // pathname: mockProps.pathName, - // search: mockProps.search, - // }); - // const wrapper = mount( - // useUrlStateHooks(args)} - // /> - // ); - // (useLocation as jest.Mock).mockReturnValue({ - // pathname: updatedMockProps.pathName, - // search: mockProps.search, - // }); - // wrapper.setProps({ - // hookProps: updatedMockProps, - // }); - // wrapper.update(); - // expect(mockHistory.replace.mock.calls[1][0]).toStrictEqual({ - // hash: '', - // pathname: DASHBOARDS_PATH, - // search: mockProps.search, - // state: '', - // }); - // }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/query_timeline_by_id_on_url_change.test.ts b/x-pack/plugins/security_solution/public/common/components/url_state/query_timeline_by_id_on_url_change.test.ts deleted file mode 100644 index 5cc4f8e8b80f931..000000000000000 --- a/x-pack/plugins/security_solution/public/common/components/url_state/query_timeline_by_id_on_url_change.test.ts +++ /dev/null @@ -1,140 +0,0 @@ -/* - * 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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { queryTimelineById } from '../../../timelines/components/open_timeline/helpers'; -import { queryTimelineByIdOnUrlChange } from './query_timeline_by_id_on_url_change'; -import * as urlHelpers from './helpers'; - -jest.mock('../../../timelines/components/open_timeline/helpers'); - -describe('queryTimelineByIdOnUrlChange', () => { - const oldTestTimelineId = '04e8ffb0-2c2a-11ec-949c-39005af91f70'; - const newTestTimelineId = `${oldTestTimelineId}-newId`; - const oldTimelineRisonSearchString = `?timeline=(activeTab:query,graphEventId:%27%27,id:%27${oldTestTimelineId}%27,isOpen:!t)`; - const newTimelineRisonSearchString = `?timeline=(activeTab:query,graphEventId:%27%27,id:%27${newTestTimelineId}%27,isOpen:!t)`; - const mockUpdateTimeline = jest.fn(); - const mockUpdateTimelineIsLoading = jest.fn(); - const mockQueryTimelineById = jest.fn(); - beforeEach(() => { - (queryTimelineById as jest.Mock).mockImplementation(mockQueryTimelineById); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - describe('when search strings are empty', () => { - it('should not call queryTimelineById', () => { - queryTimelineByIdOnUrlChange({ - oldSearch: '', - search: '', - timelineIdFromReduxStore: 'current-timeline-id', - updateTimeline: mockUpdateTimeline, - updateTimelineIsLoading: mockUpdateTimelineIsLoading, - }); - - expect(queryTimelineById).not.toBeCalled(); - }); - }); - - describe('when search string has not changed', () => { - it('should not call queryTimelineById', () => { - queryTimelineByIdOnUrlChange({ - oldSearch: oldTimelineRisonSearchString, - search: oldTimelineRisonSearchString, - timelineIdFromReduxStore: 'timeline-id', - updateTimeline: mockUpdateTimeline, - updateTimelineIsLoading: mockUpdateTimelineIsLoading, - }); - - expect(queryTimelineById).not.toBeCalled(); - }); - }); - - describe('when decode rison fails', () => { - it('should not call queryTimelineById', () => { - jest.spyOn(urlHelpers, 'decodeRisonUrlState').mockImplementationOnce(() => { - throw new Error('Unable to decode'); - }); - - queryTimelineByIdOnUrlChange({ - oldSearch: oldTimelineRisonSearchString, - search: newTimelineRisonSearchString, - timelineIdFromReduxStore: '', - updateTimeline: mockUpdateTimeline, - updateTimelineIsLoading: mockUpdateTimelineIsLoading, - }); - - expect(queryTimelineById).not.toBeCalled(); - }); - }); - - describe('when new id is not provided', () => { - it('should not call queryTimelineById', () => { - queryTimelineByIdOnUrlChange({ - oldSearch: oldTimelineRisonSearchString, - search: '?timeline=(activeTab:query)', // no id - timelineIdFromReduxStore: newTestTimelineId, - updateTimeline: mockUpdateTimeline, - updateTimelineIsLoading: mockUpdateTimelineIsLoading, - }); - - expect(queryTimelineById).not.toBeCalled(); - }); - }); - - describe('when new id matches the data in redux', () => { - it('should not call queryTimelineById', () => { - queryTimelineByIdOnUrlChange({ - oldSearch: oldTimelineRisonSearchString, - search: newTimelineRisonSearchString, - timelineIdFromReduxStore: newTestTimelineId, - updateTimeline: mockUpdateTimeline, - updateTimelineIsLoading: mockUpdateTimelineIsLoading, - }); - - expect(queryTimelineById).not.toBeCalled(); - }); - }); - - // You can only redirect or run into conflict scenarios when already viewing a timeline - describe('when not actively on a page with timeline in the search field', () => { - it('should not call queryTimelineById', () => { - queryTimelineByIdOnUrlChange({ - oldSearch: '?random=foo', - search: newTimelineRisonSearchString, - timelineIdFromReduxStore: oldTestTimelineId, - updateTimeline: mockUpdateTimeline, - updateTimelineIsLoading: mockUpdateTimelineIsLoading, - }); - - expect(queryTimelineById).not.toBeCalled(); - }); - }); - - describe('when an old timeline id exists, but a new id is given', () => { - it('should call queryTimelineById', () => { - queryTimelineByIdOnUrlChange({ - oldSearch: oldTimelineRisonSearchString, - search: newTimelineRisonSearchString, - timelineIdFromReduxStore: oldTestTimelineId, - updateTimeline: mockUpdateTimeline, - updateTimelineIsLoading: mockUpdateTimelineIsLoading, - }); - - expect(queryTimelineById).toBeCalledWith({ - activeTimelineTab: 'query', - duplicate: false, - graphEventId: '', - timelineId: newTestTimelineId, - openTimeline: true, - updateIsLoading: mockUpdateTimelineIsLoading, - updateTimeline: mockUpdateTimeline, - }); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/test_dependencies.ts b/x-pack/plugins/security_solution/public/common/components/url_state/test_dependencies.ts deleted file mode 100644 index 5b259e5a663363b..000000000000000 --- a/x-pack/plugins/security_solution/public/common/components/url_state/test_dependencies.ts +++ /dev/null @@ -1,213 +0,0 @@ -/* - * 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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -// type Action = 'PUSH' | 'POP' | 'REPLACE'; -// const pop: Action = 'POP'; - -// export const getFilterQuery = (): Query => ({ -// query: 'host.name:"siem-es"', -// language: 'kuery', -// }); - -// export const mockSetFilterQuery: jest.Mock = inputsActions.setFilterQuery as unknown as jest.Mock; -// export const mockAddGlobalLinkTo: jest.Mock = inputsActions.addGlobalLinkTo as unknown as jest.Mock; -// export const mockAddTimelineLinkTo: jest.Mock = -// inputsActions.addTimelineLinkTo as unknown as jest.Mock; -// export const mockRemoveGlobalLinkTo: jest.Mock = -// inputsActions.removeGlobalLinkTo as unknown as jest.Mock; -// export const mockRemoveTimelineLinkTo: jest.Mock = -// inputsActions.removeTimelineLinkTo as unknown as jest.Mock; -// export const mockSetAbsoluteRangeDatePicker: jest.Mock = -// inputsActions.setAbsoluteRangeDatePicker as unknown as jest.Mock; -// export const mockSetRelativeRangeDatePicker: jest.Mock = -// inputsActions.setRelativeRangeDatePicker as unknown as jest.Mock; - -// jest.mock('../../store/actions', () => ({ -// inputsActions: { -// addGlobalLinkTo: jest.fn(), -// addTimelineLinkTo: jest.fn(), -// removeGlobalLinkTo: jest.fn(), -// removeTimelineLinkTo: jest.fn(), -// setAbsoluteRangeDatePicker: jest.fn(), -// setRelativeRangeDatePicker: jest.fn(), -// setFilterQuery: jest.fn(), -// }, -// })); - -// const mockDispatch = jest.fn(); -// mockDispatch.mockImplementation((fn) => fn); - -// export const mockHistory = { -// action: pop, -// block: jest.fn(), -// createHref: jest.fn(), -// go: jest.fn(), -// goBack: jest.fn(), -// goForward: jest.fn(), -// length: 2, -// listen: jest.fn(), -// location: defaultLocation, -// push: jest.fn(), -// replace: jest.fn(), -// }; - -// export const defaultProps: UrlStateContainerPropTypes = { -// pageName: SecurityPageName.network, -// detailName: undefined, -// tabName: HostsTableType.authentications, -// search: '', -// pathName: '/network', -// navTabs, -// indexPattern: { -// fields: [ -// { -// name: '@timestamp', -// type: 'date', -// }, -// ], -// title: 'filebeat-*,packetbeat-*', -// }, -// history: { -// ...mockHistory, -// location: defaultLocation, -// }, -// }; - -// export const getMockProps = ( -// location = defaultLocation, -// kqlQueryKey = CONSTANTS.networkPage, -// kqlQueryValue: Query | null, -// pageName: SecurityPageName, -// detailName: string | undefined -// ): UrlStateContainerPropTypes => ({ -// ...defaultProps, -// history: { -// ...mockHistory, -// location, -// }, -// detailName, -// pageName, -// pathName: location.pathname, -// search: location.search, -// }); - -// interface GetMockPropsObj { -// examplePath: string; -// namespaceLower: string; -// page: LocationTypes; -// pageName: SecurityPageName; -// detailName: string | undefined; -// } - -// export const getMockPropsObj = ({ page, examplePath, pageName, detailName }: GetMockPropsObj) => ({ -// noSearch: { -// undefinedQuery: getMockProps( -// { -// hash: '', -// pathname: examplePath, -// search: '?', -// state: '', -// }, -// page, -// null, -// pageName, -// detailName -// ), -// definedQuery: getMockProps( -// { -// hash: '', -// pathname: examplePath, -// search: '?', -// state: '', -// }, -// page, -// getFilterQuery(), -// pageName, -// detailName -// ), -// }, -// relativeTimeSearch: { -// undefinedQuery: getMockProps( -// { -// hash: '', -// pathname: examplePath, -// search: `?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(),timerange:(from:1558591200000,fromStr:now-1d%2Fd,kind:relative,to:1558677599999,toStr:now-1d%2Fd)),timeline:(linkTo:!(),timerange:(from:1558732849370,fromStr:now-15m,kind:relative,to:1558733749370,toStr:now)))`, -// state: '', -// }, -// page, -// null, -// pageName, -// detailName -// ), -// definedQuery: getMockProps( -// { -// hash: '', -// pathname: examplePath, -// search: `?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(),timerange:(from:1558591200000,fromStr:now-1d%2Fd,kind:relative,to:1558677599999,toStr:now-1d%2Fd)),timeline:(linkTo:!(),timerange:(from:1558732849370,fromStr:now-15m,kind:relative,to:1558733749370,toStr:now)))`, -// state: '', -// }, -// page, -// getFilterQuery(), -// pageName, -// detailName -// ), -// undefinedLinkQuery: getMockProps( -// { -// hash: '', -// pathname: examplePath, -// search: `?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:1558591200000,fromStr:now-1d%2Fd,kind:relative,to:1558677599999,toStr:now-1d%2Fd)),timeline:(linkTo:!(global),timerange:(from:1558732849370,fromStr:now-15m,kind:relative,to:1558733749370,toStr:now)))`, -// state: '', -// }, -// page, -// null, -// pageName, -// detailName -// ), -// }, -// absoluteTimeSearch: { -// undefinedQuery: getMockProps( -// { -// hash: '', -// pathname: examplePath, -// search: -// '?timerange=(global:(linkTo:!(timeline),timerange:(from:1556736012685,kind:absolute,to:1556822416082)),timeline:(linkTo:!(global),timerange:(from:1556736012685,kind:absolute,to:1556822416082)))', -// state: '', -// }, -// page, -// null, -// pageName, -// detailName -// ), -// definedQuery: getMockProps( -// { -// hash: '', -// pathname: examplePath, -// search: -// '?timerange=(global:(linkTo:!(timeline),timerange:(from:1556736012685,kind:absolute,to:1556822416082)),timeline:(linkTo:!(global),timerange:(from:1556736012685,kind:absolute,to:1556822416082)))', -// state: '', -// }, -// page, -// getFilterQuery(), -// pageName, -// detailName -// ), -// }, -// oppositeQueryLocationSearch: { -// undefinedQuery: getMockProps( -// { -// hash: '', -// pathname: examplePath, -// search: `?query=(query:'host.name:%22siem-es%22',language:kuery)&timerange=(global:(linkTo:!(),timerange:(from:1558591200000,fromStr:now-1d%2Fd,kind:relative,to:1558677599999,toStr:now-1d%2Fd)),timeline:(linkTo:!(),timerange:(from:1558732849370,fromStr:now-15m,kind:relative,to:1558733749370,toStr:now)))`, -// state: '', -// }, -// page, -// null, -// pageName, -// detailName -// ), -// }, -// }); diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/types.ts b/x-pack/plugins/security_solution/public/common/components/url_state/types.ts deleted file mode 100644 index a73d80447b78a5c..000000000000000 --- a/x-pack/plugins/security_solution/public/common/components/url_state/types.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* - * 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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { UrlStateType } from './constants'; - -export const isAdministration = (urlKey: UrlStateType): boolean => 'administration' === urlKey; diff --git a/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.tsx b/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.tsx index ceb9c209f30a1b8..e799e9d608cd1a4 100644 --- a/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.tsx @@ -39,7 +39,7 @@ import { postSourcererDataView } from './api'; import { useDataView } from '../source/use_data_view'; import { useFetchIndex } from '../source'; import { useInitializeUrlParam, useUpdateUrlParam } from '../../utils/global_query_string'; -import { CONSTANTS } from '../../components/url_state/constants'; +import { URL_PARAM_KEY } from '../../hooks/use_url_state'; export const useInitSourcerer = ( scopeId: SourcererScopeName.default | SourcererScopeName.detections = SourcererScopeName.default @@ -49,7 +49,7 @@ export const useInitSourcerer = ( const initialTimelineSourcerer = useRef(true); const initialDetectionSourcerer = useRef(true); const { loading: loadingSignalIndex, isSignalIndexExists, signalIndexName } = useUserInfo(); - const updateUrlParam = useUpdateUrlParam(CONSTANTS.sourcerer); + const updateUrlParam = useUpdateUrlParam(URL_PARAM_KEY.sourcerer); const getDataViewsSelector = useMemo( () => sourcererSelectors.getSourcererDataViewsSelector(), @@ -125,7 +125,7 @@ export const useInitSourcerer = ( [dispatch, scopeDataViewId, scopeId, selectedPatterns, updateUrlParam] ); - useInitializeUrlParam(CONSTANTS.sourcerer, onInitializeUrlParam); + useInitializeUrlParam(URL_PARAM_KEY.sourcerer, onInitializeUrlParam); /* * Note for future engineer: diff --git a/x-pack/plugins/security_solution/public/common/hooks/search_bar/use_init_search_bar_url_params.ts b/x-pack/plugins/security_solution/public/common/hooks/search_bar/use_init_search_bar_url_params.ts index 608f38b7428a95c..bed541fdbd8daf0 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/search_bar/use_init_search_bar_url_params.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/search_bar/use_init_search_bar_url_params.ts @@ -12,7 +12,7 @@ import { useKibana } from '../../lib/kibana'; import { inputsSelectors } from '../../store'; import { inputsActions } from '../../store/inputs'; import { useInitializeUrlParam } from '../../utils/global_query_string'; -import { CONSTANTS } from '../../components/url_state/constants'; +import { URL_PARAM_KEY } from '../use_url_state'; export const useInitSearchBarFromUrlParams = () => { const dispatch = useDispatch(); @@ -91,7 +91,7 @@ export const useInitSearchBarFromUrlParams = () => { [dispatch, filterManager, savedQueries] ); - useInitializeUrlParam(CONSTANTS.appQuery, onInitializeAppQueryFromUrlParam); - useInitializeUrlParam(CONSTANTS.filters, onInitializeFiltersFromUrlParam); - useInitializeUrlParam(CONSTANTS.savedQuery, onInitializeSavedQueryFromUrlParam); + useInitializeUrlParam(URL_PARAM_KEY.appQuery, onInitializeAppQueryFromUrlParam); + useInitializeUrlParam(URL_PARAM_KEY.filters, onInitializeFiltersFromUrlParam); + useInitializeUrlParam(URL_PARAM_KEY.savedQuery, onInitializeSavedQueryFromUrlParam); }; diff --git a/x-pack/plugins/security_solution/public/common/hooks/search_bar/use_init_timerange_url_params.ts b/x-pack/plugins/security_solution/public/common/hooks/search_bar/use_init_timerange_url_params.ts index 2d5c00aa600f5ce..1992582c0816b10 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/search_bar/use_init_timerange_url_params.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/search_bar/use_init_timerange_url_params.ts @@ -16,11 +16,11 @@ import type { RelativeTimeRange, UrlInputsModel, } from '../../store/inputs/model'; -import { normalizeTimeRange } from '../../components/url_state/normalize_time_range'; +import { normalizeTimeRange } from '../../utils/normalize_time_range'; import { inputsActions } from '../../store/inputs'; import { formatDate } from '../../components/super_date_picker'; import { useInitializeUrlParam } from '../../utils/global_query_string'; -import { CONSTANTS } from '../../components/url_state/constants'; +import { URL_PARAM_KEY } from '../use_url_state'; export const useInitTimerangeFromUrlParam = () => { const dispatch = useDispatch(); @@ -31,7 +31,7 @@ export const useInitTimerangeFromUrlParam = () => { [dispatch] ); - useInitializeUrlParam(CONSTANTS.timerange, onInitialize); + useInitializeUrlParam(URL_PARAM_KEY.timerange, onInitialize); }; const initializeTimerangeFromUrlParam = ( diff --git a/x-pack/plugins/security_solution/public/common/hooks/search_bar/use_sync_search_bar_url_param.ts b/x-pack/plugins/security_solution/public/common/hooks/search_bar/use_sync_search_bar_url_param.ts index 945bb5b00d4cc0f..2b6c97ae8eacb4d 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/search_bar/use_sync_search_bar_url_param.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/search_bar/use_sync_search_bar_url_param.ts @@ -10,13 +10,13 @@ import { useMemo, useEffect } from 'react'; import type { Filter, Query } from '@kbn/es-query'; import { isEmpty } from 'lodash/fp'; import { inputsSelectors } from '../../store'; -import { CONSTANTS } from '../../components/url_state/constants'; import { useUpdateUrlParam } from '../../utils/global_query_string'; +import { URL_PARAM_KEY } from '../use_url_state'; export const useSyncSearchBarUrlParams = () => { - const updateSavedQueryUrlParam = useUpdateUrlParam(CONSTANTS.savedQuery); - const updateAppQueryUrlParam = useUpdateUrlParam(CONSTANTS.appQuery); - const updateFilterUrlParam = useUpdateUrlParam(CONSTANTS.filters); + const updateSavedQueryUrlParam = useUpdateUrlParam(URL_PARAM_KEY.savedQuery); + const updateAppQueryUrlParam = useUpdateUrlParam(URL_PARAM_KEY.appQuery); + const updateFilterUrlParam = useUpdateUrlParam(URL_PARAM_KEY.filters); const getGlobalQuerySelector = useMemo(() => inputsSelectors.globalQuerySelector(), []); const getGlobalFiltersQuerySelector = useMemo( diff --git a/x-pack/plugins/security_solution/public/common/hooks/search_bar/use_sync_timerange_url_param.ts b/x-pack/plugins/security_solution/public/common/hooks/search_bar/use_sync_timerange_url_param.ts index a14299b7f226742..ebeae3d8a7f5d71 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/search_bar/use_sync_timerange_url_param.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/search_bar/use_sync_timerange_url_param.ts @@ -9,10 +9,10 @@ import { useSelector } from 'react-redux'; import type { UrlInputsModel } from '../../store/inputs/model'; import { inputsSelectors } from '../../store/inputs'; import { useUpdateUrlParam } from '../../utils/global_query_string'; -import { CONSTANTS } from '../../components/url_state/constants'; +import { URL_PARAM_KEY } from '../use_url_state'; export const useSyncTimerangeUrlParam = () => { - const updateTimerangeUrlParam = useUpdateUrlParam(CONSTANTS.timerange); + const updateTimerangeUrlParam = useUpdateUrlParam(URL_PARAM_KEY.timerange); const getInputSelector = useMemo(() => inputsSelectors.inputsSelector(), []); const inputState = useSelector(getInputSelector); @@ -22,11 +22,11 @@ export const useSyncTimerangeUrlParam = () => { useEffect(() => { updateTimerangeUrlParam({ global: { - [CONSTANTS.timerange]: globalTimerange, + [URL_PARAM_KEY.timerange]: globalTimerange, linkTo: globalLinkTo, }, timeline: { - [CONSTANTS.timerange]: timelineTimerange, + [URL_PARAM_KEY.timerange]: timelineTimerange, linkTo: timelineLinkTo, }, }); diff --git a/x-pack/plugins/security_solution/public/common/hooks/search_bar/use_update_timerange_on_page_change.ts b/x-pack/plugins/security_solution/public/common/hooks/search_bar/use_update_timerange_on_page_change.ts index 79fdc5ddd2c1234..edd527be0a1328f 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/search_bar/use_update_timerange_on_page_change.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/search_bar/use_update_timerange_on_page_change.ts @@ -10,7 +10,7 @@ import { useDispatch, useSelector } from 'react-redux'; import usePrevious from 'react-use/lib/usePrevious'; import type { SecurityPageName } from '../../../app/types'; import { formatDate } from '../../components/super_date_picker'; -import { isDetectionsPages } from '../../components/url_state/helpers'; +import { isDetectionsPages } from '../../utils/global_query_string/helpers'; import { inputsSelectors } from '../../store'; import { inputsActions } from '../../store/inputs'; import type { InputsModelId } from '../../store/inputs/constants'; diff --git a/x-pack/plugins/security_solution/public/common/hooks/timeline/use_init_timeline_url_param.ts b/x-pack/plugins/security_solution/public/common/hooks/timeline/use_init_timeline_url_param.ts index a2c376e21742ade..8368023627908be 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/timeline/use_init_timeline_url_param.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/timeline/use_init_timeline_url_param.ts @@ -10,15 +10,15 @@ import { useCallback } from 'react'; import { useDispatch } from 'react-redux'; import { useInitializeUrlParam } from '../../utils/global_query_string'; -import { CONSTANTS } from '../../components/url_state/constants'; import { dispatchUpdateTimeline, queryTimelineById, } from '../../../timelines/components/open_timeline/helpers'; import type { TimelineUrl } from '../../../timelines/store/timeline/model'; import { timelineActions } from '../../../timelines/store/timeline'; +import { URL_PARAM_KEY } from '../use_url_state'; -export const useInitTimelineUrlParam = () => { +export const useInitTimelineFromUrlParam = () => { const dispatch = useDispatch(); const onInitialize = useCallback( @@ -39,5 +39,5 @@ export const useInitTimelineUrlParam = () => { [dispatch] ); - useInitializeUrlParam(CONSTANTS.timeline, onInitialize); + useInitializeUrlParam(URL_PARAM_KEY.timeline, onInitialize); }; diff --git a/x-pack/plugins/security_solution/public/common/hooks/timeline/use_sync_timeline_url_param.ts b/x-pack/plugins/security_solution/public/common/hooks/timeline/use_sync_timeline_url_param.ts index 5b196e43f4c8d8c..5f2ca43dec51cd1 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/timeline/use_sync_timeline_url_param.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/timeline/use_sync_timeline_url_param.ts @@ -8,27 +8,27 @@ import { useEffect, useMemo } from 'react'; import { useUpdateUrlParam } from '../../utils/global_query_string'; -import { CONSTANTS } from '../../components/url_state/constants'; import type { TimelineUrl } from '../../../timelines/store/timeline/model'; import { timelineSelectors } from '../../../timelines/store/timeline'; -import { TimelineId, TimelineTabs } from '../../../../common/types'; +import { TimelineId } from '../../../../common/types'; import { useShallowEqualSelector } from '../use_selector'; +import { URL_PARAM_KEY } from '../use_url_state'; export const useSyncTimelineUrlParam = () => { - const updateUrlParam = useUpdateUrlParam(CONSTANTS.timeline); + const updateUrlParam = useUpdateUrlParam(URL_PARAM_KEY.timeline); const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); const flyoutTimeline = useShallowEqualSelector((state) => getTimeline(state, TimelineId.active)); useEffect(() => { updateUrlParam( - flyoutTimeline != null + flyoutTimeline && flyoutTimeline.savedObjectId != null ? { id: flyoutTimeline.savedObjectId != null ? flyoutTimeline.savedObjectId : '', isOpen: flyoutTimeline.show, activeTab: flyoutTimeline.activeTab, graphEventId: flyoutTimeline.graphEventId ?? '', } - : { id: '', isOpen: false, activeTab: TimelineTabs.query, graphEventId: '' } + : null ); }, [flyoutTimeline, updateUrlParam]); }; diff --git a/x-pack/plugins/security_solution/public/common/hooks/timeline/use_update_timeline_on_page_change.test.ts b/x-pack/plugins/security_solution/public/common/hooks/timeline/use_update_timeline_on_page_change.test.ts new file mode 100644 index 000000000000000..26ca2a24b0b8902 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/hooks/timeline/use_update_timeline_on_page_change.test.ts @@ -0,0 +1,147 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +// /* +// * 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; you may not use this file except in compliance with the Elastic License +// * 2.0. +// */ + +// import { queryTimelineById } from '../../../timelines/components/open_timeline/helpers'; +// import { queryTimelineByIdOnUrlChange } from './query_timeline_by_id_on_url_change'; +// import * as urlHelpers from './helpers'; + +// jest.mock('../../../timelines/components/open_timeline/helpers'); + +// describe('queryTimelineByIdOnUrlChange', () => { +// const oldTestTimelineId = '04e8ffb0-2c2a-11ec-949c-39005af91f70'; +// const newTestTimelineId = `${oldTestTimelineId}-newId`; +// const oldTimelineRisonSearchString = `?timeline=(activeTab:query,graphEventId:%27%27,id:%27${oldTestTimelineId}%27,isOpen:!t)`; +// const newTimelineRisonSearchString = `?timeline=(activeTab:query,graphEventId:%27%27,id:%27${newTestTimelineId}%27,isOpen:!t)`; +// const mockUpdateTimeline = jest.fn(); +// const mockUpdateTimelineIsLoading = jest.fn(); +// const mockQueryTimelineById = jest.fn(); +// beforeEach(() => { +// (queryTimelineById as jest.Mock).mockImplementation(mockQueryTimelineById); +// }); + +// afterEach(() => { +// jest.clearAllMocks(); +// }); + +// describe('when search strings are empty', () => { +// it('should not call queryTimelineById', () => { +// queryTimelineByIdOnUrlChange({ +// oldSearch: '', +// search: '', +// timelineIdFromReduxStore: 'current-timeline-id', +// updateTimeline: mockUpdateTimeline, +// updateTimelineIsLoading: mockUpdateTimelineIsLoading, +// }); + +// expect(queryTimelineById).not.toBeCalled(); +// }); +// }); + +// describe('when search string has not changed', () => { +// it('should not call queryTimelineById', () => { +// queryTimelineByIdOnUrlChange({ +// oldSearch: oldTimelineRisonSearchString, +// search: oldTimelineRisonSearchString, +// timelineIdFromReduxStore: 'timeline-id', +// updateTimeline: mockUpdateTimeline, +// updateTimelineIsLoading: mockUpdateTimelineIsLoading, +// }); + +// expect(queryTimelineById).not.toBeCalled(); +// }); +// }); + +// describe('when decode rison fails', () => { +// it('should not call queryTimelineById', () => { +// jest.spyOn(urlHelpers, 'decodeRisonUrlState').mockImplementationOnce(() => { +// throw new Error('Unable to decode'); +// }); + +// queryTimelineByIdOnUrlChange({ +// oldSearch: oldTimelineRisonSearchString, +// search: newTimelineRisonSearchString, +// timelineIdFromReduxStore: '', +// updateTimeline: mockUpdateTimeline, +// updateTimelineIsLoading: mockUpdateTimelineIsLoading, +// }); + +// expect(queryTimelineById).not.toBeCalled(); +// }); +// }); + +// describe('when new id is not provided', () => { +// it('should not call queryTimelineById', () => { +// queryTimelineByIdOnUrlChange({ +// oldSearch: oldTimelineRisonSearchString, +// search: '?timeline=(activeTab:query)', // no id +// timelineIdFromReduxStore: newTestTimelineId, +// updateTimeline: mockUpdateTimeline, +// updateTimelineIsLoading: mockUpdateTimelineIsLoading, +// }); + +// expect(queryTimelineById).not.toBeCalled(); +// }); +// }); + +// describe('when new id matches the data in redux', () => { +// it('should not call queryTimelineById', () => { +// queryTimelineByIdOnUrlChange({ +// oldSearch: oldTimelineRisonSearchString, +// search: newTimelineRisonSearchString, +// timelineIdFromReduxStore: newTestTimelineId, +// updateTimeline: mockUpdateTimeline, +// updateTimelineIsLoading: mockUpdateTimelineIsLoading, +// }); + +// expect(queryTimelineById).not.toBeCalled(); +// }); +// }); + +// // You can only redirect or run into conflict scenarios when already viewing a timeline +// describe('when not actively on a page with timeline in the search field', () => { +// it('should not call queryTimelineById', () => { +// queryTimelineByIdOnUrlChange({ +// oldSearch: '?random=foo', +// search: newTimelineRisonSearchString, +// timelineIdFromReduxStore: oldTestTimelineId, +// updateTimeline: mockUpdateTimeline, +// updateTimelineIsLoading: mockUpdateTimelineIsLoading, +// }); + +// expect(queryTimelineById).not.toBeCalled(); +// }); +// }); + +// describe('when an old timeline id exists, but a new id is given', () => { +// it('should call queryTimelineById', () => { +// queryTimelineByIdOnUrlChange({ +// oldSearch: oldTimelineRisonSearchString, +// search: newTimelineRisonSearchString, +// timelineIdFromReduxStore: oldTestTimelineId, +// updateTimeline: mockUpdateTimeline, +// updateTimelineIsLoading: mockUpdateTimelineIsLoading, +// }); + +// expect(queryTimelineById).toBeCalledWith({ +// activeTimelineTab: 'query', +// duplicate: false, +// graphEventId: '', +// timelineId: newTestTimelineId, +// openTimeline: true, +// updateIsLoading: mockUpdateTimelineIsLoading, +// updateTimeline: mockUpdateTimeline, +// }); +// }); +// }); +// }); diff --git a/x-pack/plugins/security_solution/public/common/hooks/timeline/use_update_timeline_on_page_change.ts b/x-pack/plugins/security_solution/public/common/hooks/timeline/use_update_timeline_on_page_change.ts index d42521fc3ce8467..987bf21478830ad 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/timeline/use_update_timeline_on_page_change.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/timeline/use_update_timeline_on_page_change.ts @@ -10,7 +10,6 @@ import { useEffect, useMemo } from 'react'; import { useLocation } from 'react-router-dom'; import usePrevious from 'react-use/lib/usePrevious'; import { useDispatch } from 'react-redux'; -import { CONSTANTS } from '../../components/url_state/constants'; import type { TimelineUrl } from '../../../timelines/store/timeline/model'; import { timelineActions, timelineSelectors } from '../../../timelines/store/timeline'; import { TimelineId, TimelineTabs } from '../../../../common/types'; @@ -24,7 +23,8 @@ import { decodeRisonUrlState, getParamFromQueryString, getQueryStringFromLocation, -} from '../../components/url_state/helpers'; +} from '../../utils/global_query_string/helpers'; +import { URL_PARAM_KEY } from '../use_url_state'; /** * After the initial load of the security solution, timeline is not updated when the timeline url search value is changed @@ -35,21 +35,22 @@ import { */ // TODO change the name. OnUrlChange +// TODO TEST THIS SCENARIO https://github.com/elastic/kibana/pull/107099#issuecomment-891147792 export const useQueryTimelineByIdOnUrlChange = () => { const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); const flyoutTimeline = useShallowEqualSelector((state) => getTimeline(state, TimelineId.active)); const { search } = useLocation(); const oldSearch = usePrevious(search); - const timelineIdFromReduxStore = flyoutTimeline.savedObjectId ?? ''; + const timelineIdFromReduxStore = flyoutTimeline?.savedObjectId ?? ''; const dispatch = useDispatch(); useEffect(() => { const oldUrlStateString = getQueryStringKeyValue({ - urlKey: CONSTANTS.timeline, + urlKey: URL_PARAM_KEY.timeline, search: oldSearch ?? '', }); - const newUrlStateString = getQueryStringKeyValue({ urlKey: CONSTANTS.timeline, search }); + const newUrlStateString = getQueryStringKeyValue({ urlKey: URL_PARAM_KEY.timeline, search }); if (oldUrlStateString != null && newUrlStateString != null) { let newTimeline = null; diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.test.tsx b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.test.tsx index b7bec2b49030045..ef4a26e52e968ed 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.test.tsx +++ b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.test.tsx @@ -9,7 +9,7 @@ import { renderHook } from '@testing-library/react-hooks'; import { useDeepEqualSelector } from './use_selector'; import { useKibana } from '../lib/kibana'; import { useResolveConflict } from './use_resolve_conflict'; -import * as urlHelpers from '../components/url_state/helpers'; +import * as urlHelpers from '../utils/global_query_string/helpers'; jest.mock('react-router-dom', () => { const original = jest.requireActual('react-router-dom'); diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.tsx b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.tsx index 757492677266eb5..6bd55a9b70b369b 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.tsx +++ b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.tsx @@ -13,9 +13,9 @@ import { TimelineId } from '../../../common/types/timeline'; import { timelineSelectors } from '../../timelines/store/timeline'; import type { TimelineUrl } from '../../timelines/store/timeline/model'; import { timelineDefaults } from '../../timelines/store/timeline/defaults'; -import { decodeRisonUrlState, encodeRisonUrlState } from '../components/url_state/helpers'; +import { decodeRisonUrlState, encodeRisonUrlState } from '../utils/global_query_string/helpers'; import { useKibana } from '../lib/kibana'; -import { CONSTANTS } from '../components/url_state/constants'; +import { URL_PARAM_KEY } from './use_url_state'; /** * Unfortunately the url change initiated when clicking the button to otherObjectPath doesn't seem to be @@ -45,7 +45,7 @@ export const useResolveConflict = () => { } const searchQuery = new URLSearchParams(search); - const timelineRison = searchQuery.get(CONSTANTS.timeline) ?? undefined; + const timelineRison = searchQuery.get(URL_PARAM_KEY.timeline) ?? undefined; // Try to get state on URL, but default to what's in Redux in case of decodeRisonFailure const currentTimelineState = { id: savedObjectId ?? '', @@ -69,14 +69,14 @@ export const useResolveConflict = () => { id: newSavedObjectId, }; const newTimelineRison = encodeRisonUrlState(newTimelineSearch); - searchQuery.set(CONSTANTS.timeline, newTimelineRison); + searchQuery.set(URL_PARAM_KEY.timeline, newTimelineRison); const newPath = `${pathname}?${searchQuery.toString()}${window.location.hash}`; return ( <> {spaces.ui.components.getLegacyUrlConflict({ - objectNoun: CONSTANTS.timeline, + objectNoun: URL_PARAM_KEY.timeline, currentObjectId, otherObjectId: newSavedObjectId, otherObjectPath: newPath, diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.test.ts b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.test.ts index 204900f7469665c..7e3b0f88780c42d 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.test.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.test.ts @@ -10,7 +10,7 @@ import { renderHook } from '@testing-library/react-hooks'; import { useDeepEqualSelector } from './use_selector'; import { useKibana } from '../lib/kibana'; import { useResolveRedirect } from './use_resolve_redirect'; -import * as urlHelpers from '../components/url_state/helpers'; +import * as urlHelpers from '../utils/global_query_string/helpers'; jest.mock('react-router-dom', () => { const original = jest.requireActual('react-router-dom'); diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.ts b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.ts index 7166b755a1ab38c..2f5bf211d890610 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.ts @@ -11,10 +11,10 @@ import { useDeepEqualSelector } from './use_selector'; import { TimelineId } from '../../../common/types/timeline'; import { timelineSelectors } from '../../timelines/store/timeline'; import { timelineDefaults } from '../../timelines/store/timeline/defaults'; -import { decodeRisonUrlState, encodeRisonUrlState } from '../components/url_state/helpers'; +import { decodeRisonUrlState, encodeRisonUrlState } from '../utils/global_query_string/helpers'; import { useKibana } from '../lib/kibana'; import type { TimelineUrl } from '../../timelines/store/timeline/model'; -import { CONSTANTS } from '../components/url_state/constants'; +import { URL_PARAM_KEY } from './use_url_state'; /** * This hooks is specifically for use with the resolve api that was introduced as part of 7.16 @@ -33,7 +33,7 @@ export const useResolveRedirect = () => { const redirect = useCallback(() => { const searchQuery = new URLSearchParams(search); - const timelineRison = searchQuery.get(CONSTANTS.timeline) ?? undefined; + const timelineRison = searchQuery.get(URL_PARAM_KEY.timeline) ?? undefined; // Try to get state on URL, but default to what's in Redux in case of decodeRisonFailure const currentTimelineState = { @@ -65,12 +65,12 @@ export const useResolveRedirect = () => { id: newObjectId, }; const newTimelineRison = encodeRisonUrlState(newTimelineSearch); - searchQuery.set(CONSTANTS.timeline, newTimelineRison); + searchQuery.set(URL_PARAM_KEY.timeline, newTimelineRison); const newPath = `${pathname}?${searchQuery.toString()}`; spaces.ui.redirectLegacyUrl({ path: newPath, aliasPurpose: resolveTimelineConfig.alias_purpose, - objectNoun: CONSTANTS.timeline, + objectNoun: URL_PARAM_KEY.timeline, }); // Prevent the effect from being called again as the url change takes place in location rather than a true redirect updateHasRedirected(true); diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/helpers.test.ts b/x-pack/plugins/security_solution/public/common/hooks/use_update_browser_title.test.ts similarity index 90% rename from x-pack/plugins/security_solution/public/common/components/url_state/helpers.test.ts rename to x-pack/plugins/security_solution/public/common/hooks/use_update_browser_title.test.ts index 8a678be0616b999..38a0b7e25baaea9 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/use_update_browser_title.test.ts @@ -5,8 +5,8 @@ * 2.0. */ -import { navTabs } from '../../../app/home/home_navigations'; -import { getTitle } from './helpers'; +import { navTabs } from '../../app/home/home_navigations'; +import { getTitle } from './use_update_browser_title'; describe('Helpers Url_State', () => { describe('getTitle', () => { diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_url_state.ts b/x-pack/plugins/security_solution/public/common/hooks/use_url_state.ts index 60b356f81cdbc4b..c6caf665009ce3a 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_url_state.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/use_url_state.ts @@ -9,7 +9,7 @@ import { useSyncGlobalQueryString } from '../utils/global_query_string'; import { useInitSearchBarFromUrlParams } from './search_bar/use_init_search_bar_url_params'; import { useInitTimerangeFromUrlParam } from './search_bar/use_init_timerange_url_params'; import { useUpdateTimerangeOnPageChange } from './search_bar/use_update_timerange_on_page_change'; -import { useInitTimelineUrlParam } from './timeline/use_init_timeline_url_param'; +import { useInitTimelineFromUrlParam } from './timeline/use_init_timeline_url_param'; import { useSyncTimelineUrlParam } from './timeline/use_sync_timeline_url_param'; import { useQueryTimelineByIdOnUrlChange } from './timeline/use_update_timeline_on_page_change'; @@ -18,7 +18,16 @@ export const useUrlState = () => { useInitSearchBarFromUrlParams(); useInitTimerangeFromUrlParam(); useUpdateTimerangeOnPageChange(); - useInitTimelineUrlParam(); + useInitTimelineFromUrlParam(); useSyncTimelineUrlParam(); useQueryTimelineByIdOnUrlChange(); }; + +export enum URL_PARAM_KEY { + appQuery = 'query', + filters = 'filters', + savedQuery = 'savedQuery', + sourcerer = 'sourcerer', + timeline = 'timeline', + timerange = 'timerange', +} diff --git a/x-pack/plugins/security_solution/public/common/store/inputs/model.ts b/x-pack/plugins/security_solution/public/common/store/inputs/model.ts index 6d4e0d51df556bb..fcf72ba6f7d55fc 100644 --- a/x-pack/plugins/security_solution/public/common/store/inputs/model.ts +++ b/x-pack/plugins/security_solution/public/common/store/inputs/model.ts @@ -9,7 +9,7 @@ import type { Dispatch } from 'redux'; import type { Filter, Query } from '@kbn/es-query'; import type { SavedQuery } from '@kbn/data-plugin/public'; import type { InputsModelId } from './constants'; -import type { CONSTANTS } from '../../components/url_state/constants'; +import type { URL_PARAM_KEY } from '../../hooks/use_url_state'; export interface AbsoluteTimeRange { kind: 'absolute'; @@ -96,7 +96,7 @@ export interface InputsModel { } export interface UrlInputsModelInputs { linkTo: InputsModelId[]; - [CONSTANTS.timerange]: TimeRange; + [URL_PARAM_KEY.timerange]: TimeRange; } export interface UrlInputsModel { global: UrlInputsModelInputs; diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/__mocks__/normalize_time_range.ts b/x-pack/plugins/security_solution/public/common/utils/__mocks__/normalize_time_range.ts similarity index 100% rename from x-pack/plugins/security_solution/public/common/components/url_state/__mocks__/normalize_time_range.ts rename to x-pack/plugins/security_solution/public/common/utils/__mocks__/normalize_time_range.ts diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts b/x-pack/plugins/security_solution/public/common/utils/global_query_string/helpers.ts similarity index 100% rename from x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts rename to x-pack/plugins/security_solution/public/common/utils/global_query_string/helpers.ts diff --git a/x-pack/plugins/security_solution/public/common/utils/global_query_string/index.ts b/x-pack/plugins/security_solution/public/common/utils/global_query_string/index.ts index 3b5b7dc9da7991c..09588c7298c094f 100644 --- a/x-pack/plugins/security_solution/public/common/utils/global_query_string/index.ts +++ b/x-pack/plugins/security_solution/public/common/utils/global_query_string/index.ts @@ -19,7 +19,7 @@ import { encodeRisonUrlState, getParamFromQueryString, getQueryStringFromLocation, -} from '../../components/url_state/helpers'; +} from './helpers'; import { useShallowEqualSelector } from '../../hooks/use_selector'; import { globalUrlParamActions, globalUrlParamSelectors } from '../../store/global_url_param'; import { useRouteSpy } from '../route/use_route_spy'; diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/normalize_time_range.test.ts b/x-pack/plugins/security_solution/public/common/utils/normalize_time_range.test.ts similarity index 97% rename from x-pack/plugins/security_solution/public/common/components/url_state/normalize_time_range.test.ts rename to x-pack/plugins/security_solution/public/common/utils/normalize_time_range.test.ts index 7154c69c425e487..1e7a99c813a86f2 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/normalize_time_range.test.ts +++ b/x-pack/plugins/security_solution/public/common/utils/normalize_time_range.test.ts @@ -6,14 +6,14 @@ */ import { normalizeTimeRange } from './normalize_time_range'; -import type { URLTimeRange, AbsoluteTimeRange, RelativeTimeRange } from '../../store/inputs/model'; -import { isAbsoluteTimeRange, isRelativeTimeRange } from '../../store/inputs/model'; +import type { URLTimeRange, AbsoluteTimeRange, RelativeTimeRange } from '../store/inputs/model'; +import { isAbsoluteTimeRange, isRelativeTimeRange } from '../store/inputs/model'; import DateMath from '@kbn/datemath'; -import { getTimeRangeSettings } from '../../utils/default_date_settings'; +import { getTimeRangeSettings } from './default_date_settings'; const getTimeRangeSettingsMock = getTimeRangeSettings as jest.Mock; -jest.mock('../../utils/default_date_settings'); +jest.mock('./default_date_settings'); getTimeRangeSettingsMock.mockImplementation(() => ({ from: '2020-07-04T08:20:18.966Z', diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/normalize_time_range.ts b/x-pack/plugins/security_solution/public/common/utils/normalize_time_range.ts similarity index 80% rename from x-pack/plugins/security_solution/public/common/components/url_state/normalize_time_range.ts rename to x-pack/plugins/security_solution/public/common/utils/normalize_time_range.ts index 17f49a8622c3795..e248bf99bfd8f9c 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/normalize_time_range.ts +++ b/x-pack/plugins/security_solution/public/common/utils/normalize_time_range.ts @@ -5,9 +5,9 @@ * 2.0. */ -import type { URLTimeRange } from '../../store/inputs/model'; -import { getTimeRangeSettings } from '../../utils/default_date_settings'; -import { getMaybeDate } from '../formatted_date/maybe_date'; +import type { URLTimeRange } from '../store/inputs/model'; +import { getTimeRangeSettings } from './default_date_settings'; +import { getMaybeDate } from '../components/formatted_date/maybe_date'; export const normalizeTimeRange = < T extends URLTimeRange | { to: string | number; from: string | number } diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/preview_histogram.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/preview_histogram.test.tsx index 59c0af79391bd0e..127380108031fdc 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/preview_histogram.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/preview_histogram.test.tsx @@ -18,7 +18,7 @@ import { ALL_VALUES_ZEROS_TITLE } from '../../../../common/components/charts/tra jest.mock('../../../../common/lib/kibana'); jest.mock('../../../../common/containers/use_global_time'); jest.mock('./use_preview_histogram'); -jest.mock('../../../../common/components/url_state/normalize_time_range'); +jest.mock('../../../../common/utils/normalize_time_range'); describe('PreviewHistogram', () => { const mockSetQuery = jest.fn(); diff --git a/x-pack/plugins/security_solution/public/hosts/pages/details/details_tabs.test.tsx b/x-pack/plugins/security_solution/public/hosts/pages/details/details_tabs.test.tsx index 4c68761dbe1b69e..978b6eb5d270493 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/details/details_tabs.test.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/details/details_tabs.test.tsx @@ -36,7 +36,7 @@ jest.mock('../../../common/lib/kibana', () => { }; }); -jest.mock('../../../common/components/url_state/normalize_time_range'); +jest.mock('../../../common/utils/normalize_time_range'); jest.mock('../../../common/containers/source', () => ({ useFetchIndex: () => [false, { indicesExist: true, indexPatterns: mockIndexPattern }], diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/pane/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/pane/index.test.tsx index ba965cd7ad3d408..ffc4ab5304e1737 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/pane/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/pane/index.test.tsx @@ -13,7 +13,7 @@ import { TimelineId } from '../../../../../common/types/timeline'; import { Pane } from '.'; jest.mock('../../../../common/lib/kibana'); -jest.mock('../../../../common/components/url_state/normalize_time_range'); +jest.mock('../../../../common/utils/normalize_time_range'); jest.mock('../../../../common/hooks/use_resolve_conflict', () => { return { diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts index a5a313c93670143..0b8d6152ac8f451 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts @@ -48,7 +48,7 @@ import { import { resolveTimeline } from '../../containers/api'; jest.mock('../../../common/store/inputs/actions'); -jest.mock('../../../common/components/url_state/normalize_time_range'); +jest.mock('../../../common/utils/normalize_time_range'); jest.mock('../../store/timeline/actions'); jest.mock('../../../common/store/app/actions'); jest.mock('uuid', () => { diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts index 39df7ecee41f1a3..8c69ae2df30daa0 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts @@ -61,7 +61,7 @@ import type { } from './types'; import { createNote } from '../notes/helpers'; import { IS_OPERATOR } from '../timeline/data_providers/data_provider'; -import { normalizeTimeRange } from '../../../common/components/url_state/normalize_time_range'; +import { normalizeTimeRange } from '../../../common/utils/normalize_time_range'; import { sourcererActions } from '../../../common/store/sourcerer'; import { SourcererScopeName } from '../../../common/store/sourcerer/model'; import { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx index a9d3f8c31b0a077..c07a8308522c4ce 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx @@ -40,7 +40,7 @@ jest.mock('../../containers', () => ({ jest.mock('./tabs_content'); jest.mock('../../../common/lib/kibana'); -jest.mock('../../../common/components/url_state/normalize_time_range'); +jest.mock('../../../common/utils/normalize_time_range'); jest.mock('@kbn/i18n-react', () => { const originalModule = jest.requireActual('@kbn/i18n-react'); const FormattedRelative = jest.fn().mockImplementation(() => '20 hours ago'); diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts index 4b6a895c32ad5b8..107fbf182ee9f6b 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts @@ -8,7 +8,7 @@ import { TimelineType, TimelineStatus, TimelineTabs } from '../../../../common/types/timeline'; import { defaultHeaders } from '../../components/timeline/body/column_headers/default_headers'; -import { normalizeTimeRange } from '../../../common/components/url_state/normalize_time_range'; +import { normalizeTimeRange } from '../../../common/utils/normalize_time_range'; import type { SubsetTimelineModel, TimelineModel } from './model'; // normalizeTimeRange uses getTimeRangeSettings which cannot be used outside Kibana context if the uiSettings is not false diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts index 0e4e64c1971b24f..08dc6ecfd928f42 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts @@ -38,7 +38,7 @@ import { TimelineId, TimelineTabs, } from '../../../../common/types/timeline'; -import { normalizeTimeRange } from '../../../common/components/url_state/normalize_time_range'; +import { normalizeTimeRange } from '../../../common/utils/normalize_time_range'; import { timelineDefaults } from './defaults'; import type { KqlMode, TimelineModel } from './model'; diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts index c2464e411bd396c..7bb667b4eca1fe9 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.test.ts @@ -56,7 +56,7 @@ import type { TimelineById } from './types'; import { Direction } from '../../../../common/search_strategy'; import type { FilterManager } from '@kbn/data-plugin/public'; -jest.mock('../../../common/components/url_state/normalize_time_range'); +jest.mock('../../../common/utils/normalize_time_range'); jest.mock('../../../common/utils/default_date_settings', () => { const actual = jest.requireActual('../../../common/utils/default_date_settings'); return {