From 1d80af159176c5a96bab4749fe56bd80deaa6e2a Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Fri, 5 Jun 2020 14:14:25 -0400 Subject: [PATCH 01/43] Change the bootstrap of the app --- .../security_solution/public/app/app.tsx | 117 +++--- .../public/app/home/index.tsx | 6 +- .../security_solution/public/app/index.tsx | 30 +- .../security_solution/public/app/routes.tsx | 6 +- .../security_solution/public/app/types.ts | 6 +- .../security_solution/public/cases/index.ts | 4 +- .../security_solution/public/cases/routes.tsx | 14 +- .../common/lib/compose/kibana_compose.tsx | 2 +- .../public/endpoint_alerts/index.ts | 4 +- .../public/endpoint_alerts/routes.tsx | 14 +- .../public/endpoint_hosts/index.ts | 4 +- .../public/endpoint_hosts/routes.tsx | 14 +- .../security_solution/public/hosts/index.ts | 4 +- .../security_solution/public/hosts/routes.tsx | 16 +- .../public/management/index.ts | 4 +- .../public/management/routes.tsx | 11 +- .../security_solution/public/network/index.ts | 4 +- .../public/network/routes.tsx | 16 +- .../public/overview/index.ts | 4 +- .../public/overview/routes.tsx | 10 +- .../security_solution/public/plugin.tsx | 354 ++++++++++++++---- .../public/timelines/index.ts | 4 +- .../public/timelines/routes.tsx | 10 +- 23 files changed, 443 insertions(+), 215 deletions(-) diff --git a/x-pack/plugins/security_solution/public/app/app.tsx b/x-pack/plugins/security_solution/public/app/app.tsx index 50a24ef012b8b..2f7bd9d498ca6 100644 --- a/x-pack/plugins/security_solution/public/app/app.tsx +++ b/x-pack/plugins/security_solution/public/app/app.tsx @@ -4,88 +4,39 @@ * you may not use this file except in compliance with the Elastic License. */ -import { createHashHistory, History } from 'history'; +import { History } from 'history'; import React, { memo, useMemo, FC } from 'react'; import { ApolloProvider } from 'react-apollo'; -import { Store } from 'redux'; +import { Store, Action } from 'redux'; import { Provider as ReduxStoreProvider } from 'react-redux'; import { ThemeProvider } from 'styled-components'; import { EuiErrorBoundary } from '@elastic/eui'; import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json'; import euiLightVars from '@elastic/eui/dist/eui_theme_light.json'; -import { BehaviorSubject } from 'rxjs'; -import { pluck } from 'rxjs/operators'; -import { KibanaContextProvider, useKibana, useUiSetting$ } from '../common/lib/kibana'; import { Storage } from '../../../../../src/plugins/kibana_utils/public'; - import { DEFAULT_DARK_MODE } from '../../common/constants'; import { ErrorToastDispatcher } from '../common/components/error_toast_dispatcher'; -import { compose } from '../common/lib/compose/kibana_compose'; -import { AppFrontendLibs, AppApolloClient } from '../common/lib/lib'; -import { StartServices } from '../types'; -import { PageRouter } from './routes'; -import { createStore, createInitialState } from '../common/store'; -import { GlobalToaster, ManageGlobalToaster } from '../common/components/toasters'; import { MlCapabilitiesProvider } from '../common/components/ml/permissions/ml_capabilities_provider'; -import { ManageGlobalTimeline } from '../timelines/components/manage_timeline'; +import { GlobalToaster, ManageGlobalToaster } from '../common/components/toasters'; +import { AppFrontendLibs } from '../common/lib/lib'; +import { KibanaContextProvider, useKibana, useUiSetting$ } from '../common/lib/kibana'; +import { State } from '../common/store'; import { ApolloClientContext } from '../common/utils/apollo_context'; -import { SecuritySubPlugins } from './types'; - -interface AppPluginRootComponentProps { - apolloClient: AppApolloClient; - history: History; - store: Store; - subPluginRoutes: React.ReactElement[]; - theme: any; // eslint-disable-line @typescript-eslint/no-explicit-any -} - -const AppPluginRootComponent: React.FC = ({ - apolloClient, - theme, - store, - subPluginRoutes, - history, -}) => ( - - - - - - - - - - - - - - - - - -); - -const AppPluginRoot = memo(AppPluginRootComponent); +import { ManageGlobalTimeline } from '../timelines/components/manage_timeline'; +import { StartServices } from '../types'; +import { PageRouter } from './routes'; interface StartAppComponent extends AppFrontendLibs { - subPlugins: SecuritySubPlugins; + children: React.ReactNode; + history: History; + store: Store; } -const StartAppComponent: FC = ({ subPlugins, ...libs }) => { - const { routes: subPluginRoutes, store: subPluginsStore } = subPlugins; +const StartAppComponent: FC = ({ children, apolloClient, history, store }) => { const { i18n } = useKibana().services; - const history = createHashHistory(); - const libs$ = new BehaviorSubject(libs); - - const store = createStore( - createInitialState(subPluginsStore.initialState), - subPluginsStore.reducer, - libs$.pipe(pluck('apolloClient')), - subPluginsStore.middlewares - ); const [darkMode] = useUiSetting$(DEFAULT_DARK_MODE); const theme = useMemo( @@ -99,13 +50,23 @@ const StartAppComponent: FC = ({ subPlugins, ...libs }) => { return ( - + + + + + + + + {children} + + + + + + + + + ); @@ -113,12 +74,20 @@ const StartAppComponent: FC = ({ subPlugins, ...libs }) => { const StartApp = memo(StartAppComponent); -interface SiemAppComponentProps { +interface SiemAppComponentProps extends AppFrontendLibs { + children: React.ReactNode; + history: History; services: StartServices; - subPlugins: SecuritySubPlugins; + store: Store; } -const SiemAppComponent: React.FC = ({ services, subPlugins }) => ( +const SiemAppComponent: React.FC = ({ + children, + apolloClient, + history, + services, + store, +}) => ( = ({ services, subPlugin ...services, }} > - + + {children} + ); diff --git a/x-pack/plugins/security_solution/public/app/home/index.tsx b/x-pack/plugins/security_solution/public/app/home/index.tsx index b5aae0b28c871..ce3d59b3adf5c 100644 --- a/x-pack/plugins/security_solution/public/app/home/index.tsx +++ b/x-pack/plugins/security_solution/public/app/home/index.tsx @@ -52,10 +52,10 @@ const calculateFlyoutHeight = ({ }): number => Math.max(0, windowHeight - globalHeaderSize); interface HomePageProps { - subPlugins: JSX.Element[]; + children: React.ReactNode; } -export const HomePage: React.FC = ({ subPlugins }) => { +export const HomePage: React.FC = ({ children }) => { const { ref: measureRef, height: windowHeight = 0 } = useThrottledResizeObserver(); const flyoutHeight = useMemo( () => @@ -90,7 +90,7 @@ export const HomePage: React.FC = ({ subPlugins }) => { - {subPlugins} + {children} } /> { - render(, element); +interface RenderAppProps extends AppFrontendLibs, AppMountParameters { + services: StartServices; + store: Store; + SubPluginRoutes: React.FC; +} + +export const renderApp = ({ + apolloClient, + element, + history, + services, + store, + SubPluginRoutes, +}: RenderAppProps) => { + render( + + + , + element + ); return () => unmountComponentAtNode(element); }; diff --git a/x-pack/plugins/security_solution/public/app/routes.tsx b/x-pack/plugins/security_solution/public/app/routes.tsx index d1395813d39f4..fc0d4e1f4fa62 100644 --- a/x-pack/plugins/security_solution/public/app/routes.tsx +++ b/x-pack/plugins/security_solution/public/app/routes.tsx @@ -14,17 +14,17 @@ import { ManageRoutesSpy } from '../common/utils/route/manage_spy_routes'; import { RouteCapture } from '../common/components/endpoint/route_capture'; interface RouterProps { + children: React.ReactNode; history: History; - subPluginRoutes: JSX.Element[]; } -const PageRouterComponent: FC = ({ history, subPluginRoutes }) => ( +const PageRouterComponent: FC = ({ history, children }) => ( - + {children} diff --git a/x-pack/plugins/security_solution/public/app/types.ts b/x-pack/plugins/security_solution/public/app/types.ts index 4b00dee3b7510..5cf60f58a1b92 100644 --- a/x-pack/plugins/security_solution/public/app/types.ts +++ b/x-pack/plugins/security_solution/public/app/types.ts @@ -20,10 +20,10 @@ import { Immutable } from '../../common/endpoint/types'; import { AppAction } from '../common/store/actions'; export enum SiemPageName { + alerts = 'alerts', overview = 'overview', hosts = 'hosts', network = 'network', - detections = 'detections', timelines = 'timelines', case = 'case', management = 'management', @@ -33,7 +33,7 @@ export type SiemNavTabKey = | SiemPageName.overview | SiemPageName.hosts | SiemPageName.network - | SiemPageName.detections + | SiemPageName.alerts | SiemPageName.timelines | SiemPageName.case | SiemPageName.management; @@ -47,7 +47,7 @@ export interface SecuritySubPluginStore } export interface SecuritySubPlugin { - routes: React.ReactElement[]; + SubPluginRoutes: React.FC; } type SecuritySubPluginKeyStore = diff --git a/x-pack/plugins/security_solution/public/cases/index.ts b/x-pack/plugins/security_solution/public/cases/index.ts index 1eb8c82532e21..3b3130152f383 100644 --- a/x-pack/plugins/security_solution/public/cases/index.ts +++ b/x-pack/plugins/security_solution/public/cases/index.ts @@ -5,14 +5,14 @@ */ import { SecuritySubPlugin } from '../app/types'; -import { getCasesRoutes } from './routes'; +import { CasesRoutes } from './routes'; export class Cases { public setup() {} public start(): SecuritySubPlugin { return { - routes: getCasesRoutes(), + SubPluginRoutes: CasesRoutes, }; } } diff --git a/x-pack/plugins/security_solution/public/cases/routes.tsx b/x-pack/plugins/security_solution/public/cases/routes.tsx index 698350e49bc3e..4bd7aa9742ee4 100644 --- a/x-pack/plugins/security_solution/public/cases/routes.tsx +++ b/x-pack/plugins/security_solution/public/cases/routes.tsx @@ -5,13 +5,15 @@ */ import React from 'react'; -import { Route } from 'react-router-dom'; +import { Route, Switch } from 'react-router-dom'; import { Case } from './pages'; import { SiemPageName } from '../app/types'; -export const getCasesRoutes = () => [ - - - , -]; +export const CasesRoutes: React.FC = () => ( + + + + + +); diff --git a/x-pack/plugins/security_solution/public/common/lib/compose/kibana_compose.tsx b/x-pack/plugins/security_solution/public/common/lib/compose/kibana_compose.tsx index f7c7c65318482..47834f148c910 100644 --- a/x-pack/plugins/security_solution/public/common/lib/compose/kibana_compose.tsx +++ b/x-pack/plugins/security_solution/public/common/lib/compose/kibana_compose.tsx @@ -14,7 +14,7 @@ import introspectionQueryResultData from '../../../graphql/introspection.json'; import { AppFrontendLibs } from '../lib'; import { getLinks } from './helpers'; -export function compose(core: CoreStart): AppFrontendLibs { +export function composeLibs(core: CoreStart): AppFrontendLibs { const cache = new InMemoryCache({ dataIdFromObject: () => null, fragmentMatcher: new IntrospectionFragmentMatcher({ diff --git a/x-pack/plugins/security_solution/public/endpoint_alerts/index.ts b/x-pack/plugins/security_solution/public/endpoint_alerts/index.ts index e7e45b95ceb91..a755b53728e1a 100644 --- a/x-pack/plugins/security_solution/public/endpoint_alerts/index.ts +++ b/x-pack/plugins/security_solution/public/endpoint_alerts/index.ts @@ -6,7 +6,7 @@ import { Reducer } from 'redux'; import { SecuritySubPluginWithStore } from '../app/types'; -import { endpointAlertsRoutes } from './routes'; +import { EndpointAlertsRoutes } from './routes'; import { alertListReducer } from './store/reducer'; import { AlertListState } from '../../common/endpoint_alerts/types'; import { alertMiddlewareFactory } from './store/middleware'; @@ -47,7 +47,7 @@ export class EndpointAlerts { ]; return { - routes: endpointAlertsRoutes(), + SubPluginRoutes: EndpointAlertsRoutes, store: { initialState: { alertList: undefined }, /** diff --git a/x-pack/plugins/security_solution/public/endpoint_alerts/routes.tsx b/x-pack/plugins/security_solution/public/endpoint_alerts/routes.tsx index d62ef20c384dc..acc6a82e29a2c 100644 --- a/x-pack/plugins/security_solution/public/endpoint_alerts/routes.tsx +++ b/x-pack/plugins/security_solution/public/endpoint_alerts/routes.tsx @@ -5,12 +5,14 @@ */ import React from 'react'; -import { Route } from 'react-router-dom'; +import { Route, Switch } from 'react-router-dom'; import { AlertIndex } from './view'; -export const endpointAlertsRoutes = () => [ - - - , -]; +export const EndpointAlertsRoutes: React.FC = () => ( + + + + + +); diff --git a/x-pack/plugins/security_solution/public/endpoint_hosts/index.ts b/x-pack/plugins/security_solution/public/endpoint_hosts/index.ts index bd1c5f96f8cda..0319847b5f219 100644 --- a/x-pack/plugins/security_solution/public/endpoint_hosts/index.ts +++ b/x-pack/plugins/security_solution/public/endpoint_hosts/index.ts @@ -6,7 +6,7 @@ import { Reducer } from 'redux'; import { SecuritySubPluginWithStore } from '../app/types'; -import { endpointHostsRoutes } from './routes'; +import { EndpointHostsRoutes } from './routes'; import { hostListReducer } from './store/reducer'; import { HostState } from './types'; import { hostMiddlewareFactory } from './store/middleware'; @@ -46,7 +46,7 @@ export class EndpointHosts { ), ]; return { - routes: endpointHostsRoutes(), + SubPluginRoutes: EndpointHostsRoutes, store: { initialState: { hostList: undefined }, /** diff --git a/x-pack/plugins/security_solution/public/endpoint_hosts/routes.tsx b/x-pack/plugins/security_solution/public/endpoint_hosts/routes.tsx index 4ff9ecfaeab0e..3f92358b93d56 100644 --- a/x-pack/plugins/security_solution/public/endpoint_hosts/routes.tsx +++ b/x-pack/plugins/security_solution/public/endpoint_hosts/routes.tsx @@ -5,12 +5,14 @@ */ import React from 'react'; -import { Route } from 'react-router-dom'; +import { Route, Switch } from 'react-router-dom'; import { HostList } from './view'; -export const endpointHostsRoutes = () => [ - - - , -]; +export const EndpointHostsRoutes: React.FC = () => ( + + + + + +); diff --git a/x-pack/plugins/security_solution/public/hosts/index.ts b/x-pack/plugins/security_solution/public/hosts/index.ts index 6f27428e71c27..39b9174b839c6 100644 --- a/x-pack/plugins/security_solution/public/hosts/index.ts +++ b/x-pack/plugins/security_solution/public/hosts/index.ts @@ -5,7 +5,7 @@ */ import { SecuritySubPluginWithStore } from '../app/types'; -import { getHostsRoutes } from './routes'; +import { HostsRoutes } from './routes'; import { initialHostsState, hostsReducer, HostsState } from './store'; export class Hosts { @@ -13,7 +13,7 @@ export class Hosts { public start(): SecuritySubPluginWithStore<'hosts', HostsState> { return { - routes: getHostsRoutes(), + SubPluginRoutes: HostsRoutes, store: { initialState: { hosts: initialHostsState }, reducer: { hosts: hostsReducer }, diff --git a/x-pack/plugins/security_solution/public/hosts/routes.tsx b/x-pack/plugins/security_solution/public/hosts/routes.tsx index 93585fa0f8394..bf44d8f6f1ee2 100644 --- a/x-pack/plugins/security_solution/public/hosts/routes.tsx +++ b/x-pack/plugins/security_solution/public/hosts/routes.tsx @@ -5,14 +5,16 @@ */ import React from 'react'; -import { Route } from 'react-router-dom'; +import { Route, Switch } from 'react-router-dom'; import { HostsContainer } from './pages'; import { SiemPageName } from '../app/types'; -export const getHostsRoutes = () => [ - } - />, -]; +export const HostsRoutes = () => ( + + } + /> + +); diff --git a/x-pack/plugins/security_solution/public/management/index.ts b/x-pack/plugins/security_solution/public/management/index.ts index d6a723e5340bf..0fcba81821c4a 100644 --- a/x-pack/plugins/security_solution/public/management/index.ts +++ b/x-pack/plugins/security_solution/public/management/index.ts @@ -6,7 +6,7 @@ import { CoreStart } from 'kibana/public'; import { Reducer, CombinedState } from 'redux'; -import { managementRoutes } from './routes'; +import { ManagementRoutes } from './routes'; import { StartPlugins } from '../types'; import { SecuritySubPluginWithStore } from '../app/types'; import { managementReducer } from './store/reducer'; @@ -40,7 +40,7 @@ export class Management { plugins: StartPlugins ): SecuritySubPluginWithStore<'management', ManagementState> { return { - routes: managementRoutes(), + SubPluginRoutes: ManagementRoutes, store: { initialState: { management: undefined, diff --git a/x-pack/plugins/security_solution/public/management/routes.tsx b/x-pack/plugins/security_solution/public/management/routes.tsx index 12727ea97458e..fc74490515fd7 100644 --- a/x-pack/plugins/security_solution/public/management/routes.tsx +++ b/x-pack/plugins/security_solution/public/management/routes.tsx @@ -5,14 +5,15 @@ */ import React from 'react'; -import { Route } from 'react-router-dom'; +import { Route, Switch } from 'react-router-dom'; import { ManagementContainer } from './pages'; import { MANAGEMENT_ROUTING_ROOT_PATH } from './common/constants'; /** * Returns the React Router Routes for the management area */ -export const managementRoutes = () => [ - // Mounts the Management interface on `/management` - , -]; +export const ManagementRoutes = () => ( + + + +); diff --git a/x-pack/plugins/security_solution/public/network/index.ts b/x-pack/plugins/security_solution/public/network/index.ts index 6590e5ee5161c..781931ed80a60 100644 --- a/x-pack/plugins/security_solution/public/network/index.ts +++ b/x-pack/plugins/security_solution/public/network/index.ts @@ -5,7 +5,7 @@ */ import { SecuritySubPluginWithStore } from '../app/types'; -import { getNetworkRoutes } from './routes'; +import { NetworkRoutes } from './routes'; import { initialNetworkState, networkReducer, NetworkState } from './store'; export class Network { @@ -13,7 +13,7 @@ export class Network { public start(): SecuritySubPluginWithStore<'network', NetworkState> { return { - routes: getNetworkRoutes(), + SubPluginRoutes: NetworkRoutes, store: { initialState: { network: initialNetworkState }, reducer: { network: networkReducer }, diff --git a/x-pack/plugins/security_solution/public/network/routes.tsx b/x-pack/plugins/security_solution/public/network/routes.tsx index 6f3fd28ec53b7..4fc39e54a8607 100644 --- a/x-pack/plugins/security_solution/public/network/routes.tsx +++ b/x-pack/plugins/security_solution/public/network/routes.tsx @@ -5,14 +5,16 @@ */ import React from 'react'; -import { Route } from 'react-router-dom'; +import { Route, Switch } from 'react-router-dom'; import { NetworkContainer } from './pages'; import { SiemPageName } from '../app/types'; -export const getNetworkRoutes = () => [ - } - />, -]; +export const NetworkRoutes = () => ( + + } + /> + +); diff --git a/x-pack/plugins/security_solution/public/overview/index.ts b/x-pack/plugins/security_solution/public/overview/index.ts index bdf855b3851c8..dad9d0c1dc1f6 100644 --- a/x-pack/plugins/security_solution/public/overview/index.ts +++ b/x-pack/plugins/security_solution/public/overview/index.ts @@ -5,14 +5,14 @@ */ import { SecuritySubPlugin } from '../app/types'; -import { getOverviewRoutes } from './routes'; +import { OverviewRoutes } from './routes'; export class Overview { public setup() {} public start(): SecuritySubPlugin { return { - routes: getOverviewRoutes(), + SubPluginRoutes: OverviewRoutes, }; } } diff --git a/x-pack/plugins/security_solution/public/overview/routes.tsx b/x-pack/plugins/security_solution/public/overview/routes.tsx index fc41227b27c04..5ece275a84ed2 100644 --- a/x-pack/plugins/security_solution/public/overview/routes.tsx +++ b/x-pack/plugins/security_solution/public/overview/routes.tsx @@ -5,11 +5,13 @@ */ import React from 'react'; -import { Route } from 'react-router-dom'; +import { Route, Switch } from 'react-router-dom'; import { Overview } from './pages'; import { SiemPageName } from '../app/types'; -export const getOverviewRoutes = () => [ - } />, -]; +export const OverviewRoutes = () => ( + + } /> + +); diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index 676e3c705347b..439cf222a5394 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -5,6 +5,9 @@ */ import { i18n } from '@kbn/i18n'; +import { Store, Action } from 'redux'; +import { BehaviorSubject } from 'rxjs'; +import { pluck } from 'rxjs/operators'; import { AppMountParameters, @@ -19,14 +22,18 @@ import { initTelemetry } from './common/lib/telemetry'; import { KibanaServices } from './common/lib/kibana/services'; import { serviceNowActionType, jiraActionType } from './common/lib/connectors'; import { PluginSetup, PluginStart, SetupPlugins, StartPlugins, StartServices } from './types'; -import { APP_ID, APP_NAME, APP_ICON, APP_PATH } from '../common/constants'; +import { APP_ID, APP_ICON, APP_PATH } from '../common/constants'; import { ConfigureEndpointDatasource } from './management/pages/policy/view/ingest_manager_integration/configure_datasource'; +import { State, createStore, createInitialState } from './common/store'; + export class Plugin implements IPlugin { private kibanaVersion: string; + private store: Store | null; constructor(initializerContext: PluginInitializerContext) { this.kibanaVersion = initializerContext.env.packageInfo.version; + this.store = null; } public setup(core: CoreSetup, plugins: SetupPlugins) { @@ -49,82 +56,206 @@ export class Plugin implements IPlugin { + const mountSecurityFactory = async () => { const [coreStart, startPlugins] = await core.getStartServices(); - const { renderApp } = await import('./app'); + if (this.store == null) { + await this.buildStore(coreStart, startPlugins); + } + const services = { ...coreStart, ...startPlugins, security: plugins.security, } as StartServices; - - const alertsSubPlugin = new (await import('./alerts')).Alerts(); - const casesSubPlugin = new (await import('./cases')).Cases(); - const hostsSubPlugin = new (await import('./hosts')).Hosts(); - const networkSubPlugin = new (await import('./network')).Network(); - const overviewSubPlugin = new (await import('./overview')).Overview(); - const timelinesSubPlugin = new (await import('./timelines')).Timelines(); - const endpointAlertsSubPlugin = new (await import('./endpoint_alerts')).EndpointAlerts(); - const endpointHostsSubPlugin = new (await import('./endpoint_hosts')).EndpointHosts(); - const managementSubPlugin = new (await import('./management')).Management(); - - const alertsStart = alertsSubPlugin.start(); - const casesStart = casesSubPlugin.start(); - const hostsStart = hostsSubPlugin.start(); - const networkStart = networkSubPlugin.start(); - const overviewStart = overviewSubPlugin.start(); - const timelinesStart = timelinesSubPlugin.start(); - const endpointAlertsStart = endpointAlertsSubPlugin.start(coreStart, startPlugins); - const endpointHostsStart = endpointHostsSubPlugin.start(coreStart, startPlugins); - const managementSubPluginStart = managementSubPlugin.start(coreStart, startPlugins); - - return renderApp(services, params, { - routes: [ - ...alertsStart.routes, - ...casesStart.routes, - ...hostsStart.routes, - ...networkStart.routes, - ...overviewStart.routes, - ...timelinesStart.routes, - ...endpointAlertsStart.routes, - ...endpointHostsStart.routes, - ...managementSubPluginStart.routes, - ], - store: { - initialState: { - ...hostsStart.store.initialState, - ...networkStart.store.initialState, - ...timelinesStart.store.initialState, - ...endpointAlertsStart.store.initialState, - ...endpointHostsStart.store.initialState, - ...managementSubPluginStart.store.initialState, - }, - reducer: { - ...hostsStart.store.reducer, - ...networkStart.store.reducer, - ...timelinesStart.store.reducer, - ...endpointAlertsStart.store.reducer, - ...endpointHostsStart.store.reducer, - ...managementSubPluginStart.store.reducer, - }, - middlewares: [ - ...(endpointAlertsStart.store.middleware ?? []), - ...(endpointHostsStart.store.middleware ?? []), - ...(managementSubPluginStart.store.middleware ?? []), - ], - }, - }); + return { coreStart, startPlugins, services, store: this.store }; }; core.application.register({ - id: APP_ID, - title: APP_NAME, + id: `${APP_ID}:overview`, + title: 'Overview', order: 9000, euiIconType: APP_ICON, category: DEFAULT_APP_CATEGORIES.security, - appRoute: APP_PATH, + appRoute: `${APP_PATH}/overview`, + async mount(params: AppMountParameters) { + const [ + { coreStart, startPlugins, services }, + { renderApp, composeLibs }, + { overviewSubPlugin }, + ] = await Promise.all([ + mountSecurityFactory(), + this.downloadAssets(), + this.downloadSubPlugins(), + ]); + return renderApp({ + ...composeLibs(coreStart), + ...params, + services, + store: this.getStore(coreStart, startPlugins), + SubPluginRoutes: overviewSubPlugin.start().SubPluginRoutes, + }); + }, + }); + + core.application.register({ + id: `${APP_ID}:alerts`, + title: 'Alerts', + order: 9001, + euiIconType: APP_ICON, + category: DEFAULT_APP_CATEGORIES.security, + appRoute: `${APP_PATH}/alerts`, + async mount(params: AppMountParameters) { + const [ + { coreStart, store, services }, + { renderApp, composeLibs }, + { alertsSubPlugin }, + ] = await Promise.all([ + mountSecurityFactory(), + this.downloadAssets(), + this.downloadSubPlugins(), + ]); + return renderApp({ + ...composeLibs(coreStart), + ...params, + services, + store, + SubPluginRoutes: alertsSubPlugin.start().SubPluginRoutes, + }); + }, + }); + + core.application.register({ + id: `${APP_ID}:hosts`, + title: 'Hosts', + order: 9002, + euiIconType: APP_ICON, + category: DEFAULT_APP_CATEGORIES.security, + appRoute: `${APP_PATH}/hosts`, + async mount(params: AppMountParameters) { + const [ + { coreStart, store, services }, + { renderApp, composeLibs }, + { hostsSubPlugin }, + ] = await Promise.all([ + mountSecurityFactory(), + this.downloadAssets(), + this.downloadSubPlugins(), + ]); + return renderApp({ + ...composeLibs(coreStart), + ...params, + services, + store, + SubPluginRoutes: hostsSubPlugin.start().SubPluginRoutes, + }); + }, + }); + + core.application.register({ + id: `${APP_ID}:network`, + title: 'Network', + order: 9002, + euiIconType: APP_ICON, + category: DEFAULT_APP_CATEGORIES.security, + appRoute: `${APP_PATH}/network`, + async mount(params: AppMountParameters) { + const [ + { coreStart, store, services }, + { renderApp, composeLibs }, + { networkSubPlugin }, + ] = await Promise.all([ + mountSecurityFactory(), + this.downloadAssets(), + this.downloadSubPlugins(), + ]); + return renderApp({ + ...composeLibs(coreStart), + ...params, + services, + store, + SubPluginRoutes: networkSubPlugin.start().SubPluginRoutes, + }); + }, + }); + + core.application.register({ + id: `${APP_ID}:timelines`, + title: 'Timelines', + order: 9002, + euiIconType: APP_ICON, + category: DEFAULT_APP_CATEGORIES.security, + appRoute: `${APP_PATH}/timelines`, + async mount(params: AppMountParameters) { + const [ + { coreStart, store, services }, + { renderApp, composeLibs }, + { networkSubPlugin }, + ] = await Promise.all([ + mountSecurityFactory(), + this.downloadAssets(), + this.downloadSubPlugins(), + ]); + return renderApp({ + ...composeLibs(coreStart), + ...params, + services, + store, + SubPluginRoutes: networkSubPlugin.start().SubPluginRoutes, + }); + }, + }); + + core.application.register({ + id: `${APP_ID}:cases`, + title: 'Cases', + order: 9002, + euiIconType: APP_ICON, + category: DEFAULT_APP_CATEGORIES.security, + appRoute: `${APP_PATH}/cases`, async mount(params: AppMountParameters) { - return mountSecurityApp(params); + const [ + { coreStart, store, services }, + { renderApp, composeLibs }, + { networkSubPlugin }, + ] = await Promise.all([ + mountSecurityFactory(), + this.downloadAssets(), + this.downloadSubPlugins(), + ]); + return renderApp({ + ...composeLibs(coreStart), + ...params, + services, + store, + SubPluginRoutes: networkSubPlugin.start().SubPluginRoutes, + }); + }, + }); + + core.application.register({ + id: `${APP_ID}:management`, + title: 'Management', + order: 9002, + euiIconType: APP_ICON, + category: DEFAULT_APP_CATEGORIES.security, + appRoute: `${APP_PATH}/management`, + async mount(params: AppMountParameters) { + const [ + { coreStart, store, services }, + { renderApp, composeLibs }, + { managementSubPlugin }, + ] = await Promise.all([ + mountSecurityFactory(), + this.downloadAssets(), + this.downloadSubPlugins(), + ]); + return renderApp({ + ...composeLibs(coreStart), + ...params, + services, + store, + SubPluginRoutes: managementSubPlugin.start().SubPluginRoutes, + }); }, }); @@ -141,4 +272,99 @@ export class Plugin implements IPlugin { return { - routes: getTimelinesRoutes(), + SubPluginRoutes: TimelinesRoutes, store: { initialState: { timeline: initialTimelineState }, reducer: { timeline: timelineReducer }, diff --git a/x-pack/plugins/security_solution/public/timelines/routes.tsx b/x-pack/plugins/security_solution/public/timelines/routes.tsx index 50b8e1b8a7118..322a7e704b490 100644 --- a/x-pack/plugins/security_solution/public/timelines/routes.tsx +++ b/x-pack/plugins/security_solution/public/timelines/routes.tsx @@ -5,11 +5,13 @@ */ import React from 'react'; -import { Route } from 'react-router-dom'; +import { Route, Switch } from 'react-router-dom'; import { Timelines } from './pages'; import { SiemPageName } from '../app/types'; -export const getTimelinesRoutes = () => [ - } />, -]; +export const TimelinesRoutes = () => ( + + } /> + +); From 61046d7f6b0cd5ef42a6f3ee22ffeb322931ea27 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Fri, 5 Jun 2020 22:31:17 -0400 Subject: [PATCH 02/43] rename SiemPageName to SecurityPageName --- .../security_solution/public/app/app.tsx | 10 ++--- .../public/app/home/home_navigations.tsx | 32 +++++++-------- .../public/app/home/index.tsx | 10 ++--- .../security_solution/public/app/index.tsx | 6 +-- .../security_solution/public/app/types.ts | 16 ++++---- .../cases/components/case_view/actions.tsx | 4 +- .../cases/components/create/index.test.tsx | 6 +-- .../public/cases/components/create/index.tsx | 6 +-- .../user_action_tree/user_action_title.tsx | 4 +- .../public/cases/pages/index.tsx | 4 +- .../security_solution/public/cases/routes.tsx | 4 +- .../common/components/header_global/index.tsx | 8 ++-- .../common/components/link_to/link_to.tsx | 40 +++++++++---------- .../components/link_to/redirect_to_case.tsx | 12 +++--- .../components/link_to/redirect_to_hosts.tsx | 8 ++-- .../link_to/redirect_to_management.tsx | 4 +- .../link_to/redirect_to_network.tsx | 8 ++-- .../link_to/redirect_to_overview.tsx | 6 +-- .../link_to/redirect_to_timelines.tsx | 10 ++--- .../ml_host_conditional_container.tsx | 10 ++--- .../ml_network_conditional_container.tsx | 10 ++--- .../navigation/breadcrumbs/index.ts | 12 +++--- .../navigation/tab_navigation/index.test.tsx | 8 ++-- .../common/components/url_state/helpers.ts | 14 +++---- .../components/url_state/index.test.tsx | 6 +-- .../url_state/index_mocked.test.tsx | 12 +++--- .../components/url_state/test_dependencies.ts | 16 ++++---- .../components/url_state/use_url_state.tsx | 4 +- .../public/common/utils/route/spy_routes.tsx | 7 +++- .../public/common/utils/route/types.ts | 1 - .../utils/timeline/use_show_timeline.tsx | 4 +- .../public/hosts/pages/details/nav_tabs.tsx | 4 +- .../public/hosts/pages/index.tsx | 4 +- .../public/hosts/pages/nav_tabs.tsx | 4 +- .../public/hosts/pages/types.ts | 4 +- .../security_solution/public/hosts/routes.tsx | 4 +- .../public/management/common/constants.ts | 4 +- .../public/management/common/routing.ts | 10 ++--- .../public/management/types.ts | 4 +- .../public/network/pages/index.tsx | 10 ++--- .../public/network/routes.tsx | 4 +- .../public/overview/routes.tsx | 4 +- .../security_solution/public/plugin.tsx | 37 +++++++++-------- .../timeline/properties/helpers.tsx | 4 +- .../public/timelines/pages/index.tsx | 8 ++-- .../public/timelines/routes.tsx | 4 +- 46 files changed, 209 insertions(+), 202 deletions(-) diff --git a/x-pack/plugins/security_solution/public/app/app.tsx b/x-pack/plugins/security_solution/public/app/app.tsx index 2f7bd9d498ca6..2fabc12f6ff90 100644 --- a/x-pack/plugins/security_solution/public/app/app.tsx +++ b/x-pack/plugins/security_solution/public/app/app.tsx @@ -16,7 +16,7 @@ import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json'; import euiLightVars from '@elastic/eui/dist/eui_theme_light.json'; import { Storage } from '../../../../../src/plugins/kibana_utils/public'; -import { DEFAULT_DARK_MODE } from '../../common/constants'; +import { DEFAULT_DARK_MODE, APP_NAME } from '../../common/constants'; import { ErrorToastDispatcher } from '../common/components/error_toast_dispatcher'; import { MlCapabilitiesProvider } from '../common/components/ml/permissions/ml_capabilities_provider'; import { GlobalToaster, ManageGlobalToaster } from '../common/components/toasters'; @@ -74,14 +74,14 @@ const StartAppComponent: FC = ({ children, apolloClient, hist const StartApp = memo(StartAppComponent); -interface SiemAppComponentProps extends AppFrontendLibs { +interface SecurityAppComponentProps extends AppFrontendLibs { children: React.ReactNode; history: History; services: StartServices; store: Store; } -const SiemAppComponent: React.FC = ({ +const SecurityAppComponent: React.FC = ({ children, apolloClient, history, @@ -90,7 +90,7 @@ const SiemAppComponent: React.FC = ({ }) => ( = ({ ); -export const SiemApp = memo(SiemAppComponent); +export const SecurityApp = memo(SecurityAppComponent); diff --git a/x-pack/plugins/security_solution/public/app/home/home_navigations.tsx b/x-pack/plugins/security_solution/public/app/home/home_navigations.tsx index bb9e99326182f..03bb2c4d90418 100644 --- a/x-pack/plugins/security_solution/public/app/home/home_navigations.tsx +++ b/x-pack/plugins/security_solution/public/app/home/home_navigations.tsx @@ -13,57 +13,57 @@ import { getCaseUrl, } from '../../common/components/link_to'; import * as i18n from './translations'; -import { SiemPageName, SiemNavTab } from '../types'; +import { SecurityPageName, SiemNavTab } from '../types'; import { getManagementUrl } from '../../management'; export const navTabs: SiemNavTab = { - [SiemPageName.overview]: { - id: SiemPageName.overview, + [SecurityPageName.overview]: { + id: SecurityPageName.overview, name: i18n.OVERVIEW, href: getOverviewUrl(), disabled: false, urlKey: 'overview', }, - [SiemPageName.hosts]: { - id: SiemPageName.hosts, + [SecurityPageName.hosts]: { + id: SecurityPageName.hosts, name: i18n.HOSTS, href: getHostsUrl(), disabled: false, urlKey: 'host', }, - [SiemPageName.network]: { - id: SiemPageName.network, + [SecurityPageName.network]: { + id: SecurityPageName.network, name: i18n.NETWORK, href: getNetworkUrl(), disabled: false, urlKey: 'network', }, - [SiemPageName.detections]: { - id: SiemPageName.detections, + [SecurityPageName.detections]: { + id: SecurityPageName.detections, name: i18n.DETECTION_ENGINE, href: getDetectionEngineUrl(), disabled: false, urlKey: 'detections', }, - [SiemPageName.timelines]: { - id: SiemPageName.timelines, + [SecurityPageName.timelines]: { + id: SecurityPageName.timelines, name: i18n.TIMELINES, href: getTimelinesUrl(), disabled: false, urlKey: 'timeline', }, - [SiemPageName.case]: { - id: SiemPageName.case, + [SecurityPageName.case]: { + id: SecurityPageName.case, name: i18n.CASE, href: getCaseUrl(null), disabled: false, urlKey: 'case', }, - [SiemPageName.management]: { - id: SiemPageName.management, + [SecurityPageName.management]: { + id: SecurityPageName.management, name: i18n.MANAGEMENT, href: getManagementUrl({ name: 'default' }), disabled: false, - urlKey: SiemPageName.management, + urlKey: SecurityPageName.management, }, }; diff --git a/x-pack/plugins/security_solution/public/app/home/index.tsx b/x-pack/plugins/security_solution/public/app/home/index.tsx index ce3d59b3adf5c..40e0ef7ab328a 100644 --- a/x-pack/plugins/security_solution/public/app/home/index.tsx +++ b/x-pack/plugins/security_solution/public/app/home/index.tsx @@ -26,7 +26,7 @@ import { SpyRoute } from '../../common/utils/route/spy_routes'; import { useShowTimeline } from '../../common/utils/timeline/use_show_timeline'; import { NotFoundPage } from '../404'; import { navTabs } from './home_navigations'; -import { SiemPageName } from '../types'; +import { SecurityPageName } from '../types'; const WrappedByAutoSizer = styled.div` height: 100%; @@ -88,9 +88,9 @@ export const HomePage: React.FC = ({ children }) => { )} - - - {children} + {children} + {/* + } /> = ({ children }) => { )} /> } /> - + */} )} diff --git a/x-pack/plugins/security_solution/public/app/index.tsx b/x-pack/plugins/security_solution/public/app/index.tsx index 0f69a39afaa48..0afd945af8597 100644 --- a/x-pack/plugins/security_solution/public/app/index.tsx +++ b/x-pack/plugins/security_solution/public/app/index.tsx @@ -12,7 +12,7 @@ import { render, unmountComponentAtNode } from 'react-dom'; import { AppMountParameters } from '../../../../../src/core/public'; import { State } from '../common/store'; import { StartServices } from '../types'; -import { SiemApp } from './app'; +import { SecurityApp } from './app'; import { AppFrontendLibs } from '../common/lib/lib'; interface RenderAppProps extends AppFrontendLibs, AppMountParameters { @@ -30,9 +30,9 @@ export const renderApp = ({ SubPluginRoutes, }: RenderAppProps) => { render( - + - , + , element ); return () => unmountComponentAtNode(element); diff --git a/x-pack/plugins/security_solution/public/app/types.ts b/x-pack/plugins/security_solution/public/app/types.ts index 5cf60f58a1b92..860cd121b9795 100644 --- a/x-pack/plugins/security_solution/public/app/types.ts +++ b/x-pack/plugins/security_solution/public/app/types.ts @@ -19,7 +19,7 @@ import { State, SubPluginsInitReducer } from '../common/store'; import { Immutable } from '../../common/endpoint/types'; import { AppAction } from '../common/store/actions'; -export enum SiemPageName { +export enum SecurityPageName { alerts = 'alerts', overview = 'overview', hosts = 'hosts', @@ -30,13 +30,13 @@ export enum SiemPageName { } export type SiemNavTabKey = - | SiemPageName.overview - | SiemPageName.hosts - | SiemPageName.network - | SiemPageName.alerts - | SiemPageName.timelines - | SiemPageName.case - | SiemPageName.management; + | SecurityPageName.overview + | SecurityPageName.hosts + | SecurityPageName.network + | SecurityPageName.alerts + | SecurityPageName.timelines + | SecurityPageName.case + | SecurityPageName.management; export type SiemNavTab = Record; diff --git a/x-pack/plugins/security_solution/public/cases/components/case_view/actions.tsx b/x-pack/plugins/security_solution/public/cases/components/case_view/actions.tsx index cd9318a355e3c..c345f69960bcd 100644 --- a/x-pack/plugins/security_solution/public/cases/components/case_view/actions.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/case_view/actions.tsx @@ -10,7 +10,7 @@ import { Redirect } from 'react-router-dom'; import * as i18n from './translations'; import { useDeleteCases } from '../../containers/use_delete_cases'; import { ConfirmDeleteCaseModal } from '../confirm_delete_case'; -import { SiemPageName } from '../../../app/types'; +import { SecurityPageName } from '../../../app/types'; import { PropertyActions } from '../property_actions'; import { Case } from '../../containers/types'; import { CaseService } from '../../containers/use_get_case_user_actions'; @@ -68,7 +68,7 @@ const CaseViewActionsComponent: React.FC = ({ ); if (isDeleted) { - return ; + return ; } return ( <> diff --git a/x-pack/plugins/security_solution/public/cases/components/create/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/create/index.test.tsx index 78108f7d74658..0336f97b135d7 100644 --- a/x-pack/plugins/security_solution/public/cases/components/create/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/create/index.test.tsx @@ -20,7 +20,7 @@ jest.mock('../../../timelines/components/timeline/insert_timeline_popover/use_in jest.mock('../../containers/use_post_case'); import { useForm } from '../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form'; import { wait } from '../../../common/lib/helpers'; -import { SiemPageName } from '../../../app/types'; +import { SecurityPageName } from '../../../app/types'; jest.mock( '../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form' ); @@ -110,7 +110,7 @@ describe('Create case', () => { ); wrapper.find(`[data-test-subj="create-case-cancel"]`).first().simulate('click'); - expect(mockHistory.replace.mock.calls[0][0].pathname).toEqual(`/${SiemPageName.case}`); + expect(mockHistory.replace.mock.calls[0][0].pathname).toEqual(`/${SecurityPageName.case}`); }); it('should redirect to new case when caseData is there', () => { const sampleId = '777777'; @@ -123,7 +123,7 @@ describe('Create case', () => { ); expect(mockHistory.replace.mock.calls[0][0].pathname).toEqual( - `/${SiemPageName.case}/${sampleId}` + `/${SecurityPageName.case}/${sampleId}` ); }); diff --git a/x-pack/plugins/security_solution/public/cases/components/create/index.tsx b/x-pack/plugins/security_solution/public/cases/components/create/index.tsx index ae0ffe498c391..01511abf642d3 100644 --- a/x-pack/plugins/security_solution/public/cases/components/create/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/create/index.tsx @@ -30,7 +30,7 @@ import { schema } from './schema'; import { InsertTimelinePopover } from '../../../timelines/components/timeline/insert_timeline_popover'; import { useInsertTimeline } from '../../../timelines/components/timeline/insert_timeline_popover/use_insert_timeline'; import * as i18n from '../../translations'; -import { SiemPageName } from '../../../app/types'; +import { SecurityPageName } from '../../../app/types'; import { MarkdownEditorForm } from '../../../common/components//markdown_editor/form'; import { useGetTags } from '../../containers/use_get_tags'; @@ -100,11 +100,11 @@ export const Create = React.memo(() => { }, []); if (caseData != null && caseData.id) { - return ; + return ; } if (isCancel) { - return ; + return ; } return ( diff --git a/x-pack/plugins/security_solution/public/cases/components/user_action_tree/user_action_title.tsx b/x-pack/plugins/security_solution/public/cases/components/user_action_tree/user_action_title.tsx index 307790194421d..0bd6100b05df6 100644 --- a/x-pack/plugins/security_solution/public/cases/components/user_action_tree/user_action_title.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/user_action_tree/user_action_title.tsx @@ -23,7 +23,7 @@ import { LocalizedDateTooltip } from '../../../common/components/localized_date_ import { useGetUrlSearch } from '../../../common/components/navigation/use_get_url_search'; import { navTabs } from '../../../app/home/home_navigations'; import { PropertyActions } from '../property_actions'; -import { SiemPageName } from '../../../app/types'; +import { SecurityPageName } from '../../../app/types'; import * as i18n from './translations'; const MySpinner = styled(EuiLoadingSpinner)` @@ -94,7 +94,7 @@ export const UserActionTitle = ({ const handleAnchorLink = useCallback(() => { copy( - `${window.location.origin}${window.location.pathname}#${SiemPageName.case}/${caseId}/${id}${urlSearch}` + `${window.location.origin}${window.location.pathname}#${SecurityPageName.case}/${caseId}/${id}${urlSearch}` ); }, [caseId, id, urlSearch]); diff --git a/x-pack/plugins/security_solution/public/cases/pages/index.tsx b/x-pack/plugins/security_solution/public/cases/pages/index.tsx index 32f64d2690cba..a5ef4a0e7a110 100644 --- a/x-pack/plugins/security_solution/public/cases/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/pages/index.tsx @@ -7,13 +7,13 @@ import React from 'react'; import { Route, Switch } from 'react-router-dom'; -import { SiemPageName } from '../../app/types'; +import { SecurityPageName } from '../../app/types'; import { CaseDetailsPage } from './case_details'; import { CasesPage } from './case'; import { CreateCasePage } from './create_case'; import { ConfigureCasesPage } from './configure_cases'; -const casesPagePath = `/:pageName(${SiemPageName.case})`; +const casesPagePath = `/:pageName(${SecurityPageName.case})`; const caseDetailsPagePath = `${casesPagePath}/:detailName`; const caseDetailsPagePathWithCommentId = `${casesPagePath}/:detailName/:commentId`; const createCasePagePath = `${casesPagePath}/create`; diff --git a/x-pack/plugins/security_solution/public/cases/routes.tsx b/x-pack/plugins/security_solution/public/cases/routes.tsx index 4bd7aa9742ee4..8d0533b4826e7 100644 --- a/x-pack/plugins/security_solution/public/cases/routes.tsx +++ b/x-pack/plugins/security_solution/public/cases/routes.tsx @@ -8,11 +8,11 @@ import React from 'react'; import { Route, Switch } from 'react-router-dom'; import { Case } from './pages'; -import { SiemPageName } from '../app/types'; +import { SecurityPageName } from '../app/types'; export const CasesRoutes: React.FC = () => ( - + diff --git a/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx b/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx index 06a69d0612ee8..b28bb8ec6456f 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx @@ -12,7 +12,7 @@ import styled, { css } from 'styled-components'; import { useLocation } from 'react-router-dom'; import { gutterTimeline } from '../../lib/helpers'; import { navTabs } from '../../../app/home/home_navigations'; -import { SiemPageName } from '../../../app/types'; +import { SecurityPageName } from '../../../app/types'; import { getOverviewUrl } from '../link_to'; import { MlPopover } from '../ml_popover/ml_popover'; import { SiemNavigation } from '../navigation'; @@ -60,14 +60,14 @@ export const HeaderGlobal = React.memo(({ hideDetectionEngine display="condensed" navTabs={ hideDetectionEngine - ? pickBy((_, key) => key !== SiemPageName.detections, navTabs) + ? pickBy((_, key) => key !== SecurityPageName.detections, navTabs) : navTabs } /> ) : ( key === SiemPageName.overview, navTabs)} + navTabs={pickBy((_, key) => key === SecurityPageName.overview, navTabs)} /> )} @@ -77,7 +77,7 @@ export const HeaderGlobal = React.memo(({ hideDetectionEngine {indicesExistOrDataTemporarilyUnavailable(indicesExist) && - currentLocation.pathname.includes(`/${SiemPageName.detections}/`) && ( + currentLocation.pathname.includes(`/${SecurityPageName.detections}/`) && ( diff --git a/x-pack/plugins/security_solution/public/common/components/link_to/link_to.tsx b/x-pack/plugins/security_solution/public/common/components/link_to/link_to.tsx index 0294d175aef19..33198ef4aac4b 100644 --- a/x-pack/plugins/security_solution/public/common/components/link_to/link_to.tsx +++ b/x-pack/plugins/security_solution/public/common/components/link_to/link_to.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { match as RouteMatch, Redirect, Route, Switch } from 'react-router-dom'; -import { SiemPageName } from '../../../app/types'; +import { SecurityPageName } from '../../../app/types'; import { HostsTableType } from '../../../hosts/store/model'; import { RedirectToCreateRulePage, @@ -36,88 +36,88 @@ export const LinkToPage = React.memo(({ match }) => ( diff --git a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_case.tsx b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_case.tsx index e0c03519c6cbe..54541de4fc25b 100644 --- a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_case.tsx +++ b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_case.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { RouteComponentProps } from 'react-router-dom'; import { appendSearch } from './helpers'; import { RedirectWrapper } from './redirect_wrapper'; -import { SiemPageName } from '../../../app/types'; +import { SecurityPageName } from '../../../app/types'; export type CaseComponentProps = RouteComponentProps<{ detailName: string; @@ -20,16 +20,18 @@ export const RedirectToCasePage = ({ }, }: CaseComponentProps) => ( ); -export const RedirectToCreatePage = () => ; +export const RedirectToCreatePage = () => ( + +); export const RedirectToConfigureCasesPage = () => ( - + ); -const baseCaseUrl = `#/link-to/${SiemPageName.case}`; +const baseCaseUrl = `#/link-to/${SecurityPageName.case}`; export const getCaseUrl = (search: string | null) => `${baseCaseUrl}${appendSearch(search ?? undefined)}`; diff --git a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_hosts.tsx b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_hosts.tsx index 0cfe8e655e255..13d627bbfaa49 100644 --- a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_hosts.tsx +++ b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_hosts.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { RouteComponentProps } from 'react-router-dom'; import { HostsTableType } from '../../../hosts/store/model'; -import { SiemPageName } from '../../../app/types'; +import { SecurityPageName } from '../../../app/types'; import { appendSearch } from './helpers'; import { RedirectWrapper } from './redirect_wrapper'; @@ -27,7 +27,7 @@ export const RedirectToHostsPage = ({ }: HostComponentProps) => { const defaultSelectedTab = HostsTableType.hosts; const selectedTab = tabName ? tabName : defaultSelectedTab; - const to = `/${SiemPageName.hosts}/${selectedTab}${search}`; + const to = `/${SecurityPageName.hosts}/${selectedTab}${search}`; return ; }; @@ -40,11 +40,11 @@ export const RedirectToHostDetailsPage = ({ }: HostComponentProps) => { const defaultSelectedTab = HostsTableType.authentications; const selectedTab = tabName ? tabName : defaultSelectedTab; - const to = `/${SiemPageName.hosts}/${detailName}/${selectedTab}${search}`; + const to = `/${SecurityPageName.hosts}/${detailName}/${selectedTab}${search}`; return ; }; -const baseHostsUrl = `#/link-to/${SiemPageName.hosts}`; +const baseHostsUrl = `#/link-to/${SecurityPageName.hosts}`; export const getHostsUrl = (search?: string) => `${baseHostsUrl}${appendSearch(search)}`; diff --git a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_management.tsx b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_management.tsx index 595c203993bb7..f10add0802508 100644 --- a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_management.tsx +++ b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_management.tsx @@ -6,10 +6,10 @@ import React, { memo } from 'react'; import { RedirectWrapper } from './redirect_wrapper'; -import { SiemPageName } from '../../../app/types'; +import { SecurityPageName } from '../../../app/types'; export const RedirectToManagementPage = memo(() => { - return ; + return ; }); RedirectToManagementPage.displayName = 'RedirectToManagementPage'; diff --git a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_network.tsx b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_network.tsx index d72bacf511faa..9449606a3459e 100644 --- a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_network.tsx +++ b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_network.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { RouteComponentProps } from 'react-router-dom'; -import { SiemPageName } from '../../../app/types'; +import { SecurityPageName } from '../../../app/types'; import { FlowTarget, FlowTargetSourceDest } from '../../../graphql/types'; import { appendSearch } from './helpers'; @@ -28,13 +28,13 @@ export const RedirectToNetworkPage = ({ ); -const baseNetworkUrl = `#/link-to/${SiemPageName.network}`; +const baseNetworkUrl = `#/link-to/${SecurityPageName.network}`; export const getNetworkUrl = (search?: string) => `${baseNetworkUrl}${appendSearch(search)}`; export const getIPDetailsUrl = ( detailName: string, diff --git a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_overview.tsx b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_overview.tsx index 2043b820e6966..4d3c500a4e24a 100644 --- a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_overview.tsx +++ b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_overview.tsx @@ -7,14 +7,14 @@ import React from 'react'; import { RouteComponentProps } from 'react-router-dom'; import { RedirectWrapper } from './redirect_wrapper'; -import { SiemPageName } from '../../../app/types'; +import { SecurityPageName } from '../../../app/types'; export type OverviewComponentProps = RouteComponentProps<{ search: string; }>; export const RedirectToOverviewPage = ({ location: { search } }: OverviewComponentProps) => ( - + ); -export const getOverviewUrl = () => `#/link-to/${SiemPageName.overview}`; +export const getOverviewUrl = () => `#/link-to/${SecurityPageName.overview}`; diff --git a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_timelines.tsx b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_timelines.tsx index 3562153bea646..bfcaddcf92c5d 100644 --- a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_timelines.tsx +++ b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_timelines.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { RouteComponentProps } from 'react-router-dom'; -import { SiemPageName } from '../../../app/types'; +import { SecurityPageName } from '../../../app/types'; import { appendSearch } from './helpers'; import { RedirectWrapper } from './redirect_wrapper'; @@ -27,14 +27,14 @@ export const RedirectToTimelinesPage = ({ ); export const getTimelinesUrl = (search?: string) => - `#/link-to/${SiemPageName.timelines}${appendSearch(search)}`; + `#/link-to/${SecurityPageName.timelines}${appendSearch(search)}`; export const getTimelineTabsUrl = (tabName: TimelineTypeLiteral, search?: string) => - `#/link-to/${SiemPageName.timelines}/${tabName}${appendSearch(search)}`; + `#/link-to/${SecurityPageName.timelines}/${tabName}${appendSearch(search)}`; diff --git a/x-pack/plugins/security_solution/public/common/components/ml/conditional_links/ml_host_conditional_container.tsx b/x-pack/plugins/security_solution/public/common/components/ml/conditional_links/ml_host_conditional_container.tsx index 6ca723c50c681..e6f0f3b48072c 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/conditional_links/ml_host_conditional_container.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/conditional_links/ml_host_conditional_container.tsx @@ -11,7 +11,7 @@ import { Redirect, Route, Switch, RouteComponentProps } from 'react-router-dom'; import { addEntitiesToKql } from './add_entities_to_kql'; import { replaceKQLParts } from './replace_kql_parts'; import { emptyEntity, multipleEntities, getMultipleEntities } from './entity_helpers'; -import { SiemPageName } from '../../../../app/types'; +import { SecurityPageName } from '../../../../app/types'; import { HostsTableType } from '../../../../hosts/store/model'; import { url as urlUtils } from '../../../../../../../../src/plugins/kibana_utils/public'; @@ -42,7 +42,7 @@ export const MlHostConditionalContainer = React.memo(({ sort: false, encode: false, }); - return ; + return ; }} /> (({ }); return ( - + ); } else if (multipleEntities(hostName)) { const hosts: string[] = getMultipleEntities(hostName); @@ -82,7 +82,7 @@ export const MlHostConditionalContainer = React.memo(({ }); return ( - + ); } else { const reEncoded = stringify(urlUtils.encodeQuery(queryStringDecoded), { @@ -92,7 +92,7 @@ export const MlHostConditionalContainer = React.memo(({ return ( ); } diff --git a/x-pack/plugins/security_solution/public/common/components/ml/conditional_links/ml_network_conditional_container.tsx b/x-pack/plugins/security_solution/public/common/components/ml/conditional_links/ml_network_conditional_container.tsx index 05049cd9b4ea5..889ea0bd7dca9 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/conditional_links/ml_network_conditional_container.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/conditional_links/ml_network_conditional_container.tsx @@ -11,7 +11,7 @@ import { Redirect, Route, Switch, RouteComponentProps } from 'react-router-dom'; import { addEntitiesToKql } from './add_entities_to_kql'; import { replaceKQLParts } from './replace_kql_parts'; import { emptyEntity, getMultipleEntities, multipleEntities } from './entity_helpers'; -import { SiemPageName } from '../../../../app/types'; +import { SecurityPageName } from '../../../../app/types'; import { url as urlUtils } from '../../../../../../../../src/plugins/kibana_utils/public'; @@ -43,7 +43,7 @@ export const MlNetworkConditionalContainer = React.memo; + return ; }} /> ; + return ; } else if (multipleEntities(ip)) { const ips: string[] = getMultipleEntities(ip); queryStringDecoded.query = addEntitiesToKql( @@ -80,13 +80,13 @@ export const MlNetworkConditionalContainer = React.memo; + return ; } else { const reEncoded = stringify(urlUtils.encodeQuery(queryStringDecoded), { sort: false, encode: false, }); - return ; + return ; } }} /> diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.ts b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.ts index a202407b1270c..50ce984e55525 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.ts +++ b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.ts @@ -15,7 +15,7 @@ import { getBreadcrumbs as getIPDetailsBreadcrumbs } from '../../../../network/p import { getBreadcrumbs as getCaseDetailsBreadcrumbs } from '../../../../cases/pages/utils'; import { getBreadcrumbs as getDetectionRulesBreadcrumbs } from '../../../../alerts/pages/detection_engine/rules/utils'; import { getBreadcrumbs as getTimelinesBreadcrumbs } from '../../../../timelines/pages'; -import { SiemPageName } from '../../../../app/types'; +import { SecurityPageName } from '../../../../app/types'; import { RouteSpyState, HostRouteSpyState, @@ -46,19 +46,19 @@ export const siemRootBreadcrumb: ChromeBreadcrumb[] = [ ]; const isNetworkRoutes = (spyState: RouteSpyState): spyState is NetworkRouteSpyState => - spyState != null && spyState.pageName === SiemPageName.network; + spyState != null && spyState.pageName === SecurityPageName.network; const isHostsRoutes = (spyState: RouteSpyState): spyState is HostRouteSpyState => - spyState != null && spyState.pageName === SiemPageName.hosts; + spyState != null && spyState.pageName === SecurityPageName.hosts; const isTimelinesRoutes = (spyState: RouteSpyState): spyState is TimelineRouteSpyState => - spyState != null && spyState.pageName === SiemPageName.timelines; + spyState != null && spyState.pageName === SecurityPageName.timelines; const isCaseRoutes = (spyState: RouteSpyState): spyState is RouteSpyState => - spyState != null && spyState.pageName === SiemPageName.case; + spyState != null && spyState.pageName === SecurityPageName.case; const isDetectionsRoutes = (spyState: RouteSpyState) => - spyState != null && spyState.pageName === SiemPageName.detections; + spyState != null && spyState.pageName === SecurityPageName.detections; export const getBreadcrumbsForRoute = ( object: RouteSpyState & TabNavigationProps diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx index b9572caece94f..a8dde7d2c41c9 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx @@ -8,7 +8,7 @@ import { mount } from 'enzyme'; import React from 'react'; import { navTabs } from '../../../../app/home/home_navigations'; -import { SiemPageName } from '../../../../app/types'; +import { SecurityPageName } from '../../../../app/types'; import { navTabsHostDetails } from '../../../../hosts/pages/details/nav_tabs'; import { HostsTableType } from '../../../../hosts/store/model'; import { RouteSpyState } from '../../../utils/route/types'; @@ -17,7 +17,7 @@ import { TabNavigationComponent } from './'; import { TabNavigationProps } from './types'; describe('Tab Navigation', () => { - const pageName = SiemPageName.hosts; + const pageName = SecurityPageName.hosts; const hostName = 'siem-window'; const tabName = HostsTableType.authentications; const pathName = `/${pageName}/${hostName}/${tabName}`; @@ -137,8 +137,8 @@ describe('Tab Navigation', () => { wrapper.find(`[data-test-subj="navigation-${HostsTableType.events}"]`).first(); expect(tableNavigationTab().prop('isSelected')).toBeFalsy(); wrapper.setProps({ - pageName: SiemPageName.hosts, - pathName: `/${SiemPageName.hosts}`, + pageName: SecurityPageName.hosts, + pathName: `/${SecurityPageName.hosts}`, tabName: HostsTableType.events, }); wrapper.update(); diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts b/x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts index 8f13e4dd0cdcf..338b3cd088665 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts +++ b/x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts @@ -12,7 +12,7 @@ import * as H from 'history'; import { Query, Filter } from '../../../../../../../src/plugins/data/public'; import { url } from '../../../../../../../src/plugins/kibana_utils/public'; -import { SiemPageName } from '../../../app/types'; +import { SecurityPageName } from '../../../app/types'; import { inputsSelectors, State } from '../../store'; import { UrlInputsModel } from '../../store/inputs/model'; import { TimelineUrl } from '../../../timelines/store/timeline/model'; @@ -84,17 +84,17 @@ export const replaceQueryStringInLocation = ( }; export const getUrlType = (pageName: string): UrlStateType => { - if (pageName === SiemPageName.overview) { + if (pageName === SecurityPageName.overview) { return 'overview'; - } else if (pageName === SiemPageName.hosts) { + } else if (pageName === SecurityPageName.hosts) { return 'host'; - } else if (pageName === SiemPageName.network) { + } else if (pageName === SecurityPageName.network) { return 'network'; - } else if (pageName === SiemPageName.detections) { + } else if (pageName === SecurityPageName.detections) { return 'detections'; - } else if (pageName === SiemPageName.timelines) { + } else if (pageName === SecurityPageName.timelines) { return 'timeline'; - } else if (pageName === SiemPageName.case) { + } else if (pageName === SecurityPageName.case) { return 'case'; } return 'overview'; 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 index 138ab08e894a5..eeeaacc25a15e 100644 --- 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 @@ -8,7 +8,7 @@ import { mount } from 'enzyme'; import React from 'react'; import { HookWrapper } from '../../mock'; -import { SiemPageName } from '../../../app/types'; +import { SecurityPageName } from '../../../app/types'; import { RouteSpyState } from '../../utils/route/types'; import { CONSTANTS } from './constants'; import { @@ -26,7 +26,7 @@ import { wait } from '../../lib/helpers'; let mockProps: UrlStateContainerPropTypes; const mockRouteSpy: RouteSpyState = { - pageName: SiemPageName.network, + pageName: SecurityPageName.network, detailName: undefined, tabName: undefined, search: '', @@ -186,7 +186,7 @@ describe('UrlStateContainer', () => { page: CONSTANTS.hostsPage, examplePath: '/hosts', namespaceLower: 'hosts', - pageName: SiemPageName.hosts, + pageName: SecurityPageName.hosts, detailName: undefined, }).relativeTimeSearch.undefinedQuery, }); 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 index 1650e3bea2022..f7502661da308 100644 --- 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 @@ -8,7 +8,7 @@ import { mount } from 'enzyme'; import React from 'react'; import { HookWrapper } from '../../mock/hook_wrapper'; -import { SiemPageName } from '../../../app/types'; +import { SecurityPageName } from '../../../app/types'; import { CONSTANTS } from './constants'; import { getFilterQuery, getMockPropsObj, mockHistory, testCases } from './test_dependencies'; @@ -42,7 +42,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => page: CONSTANTS.networkPage, examplePath: '/network', namespaceLower: 'network', - pageName: SiemPageName.network, + pageName: SecurityPageName.network, detailName: undefined, }).noSearch.definedQuery; const wrapper = mount( @@ -93,7 +93,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => page: CONSTANTS.networkPage, examplePath: '/network', namespaceLower: 'network', - pageName: SiemPageName.network, + pageName: SecurityPageName.network, detailName: undefined, }).noSearch.undefinedQuery; const wrapper = mount( @@ -124,7 +124,7 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => page: CONSTANTS.networkPage, examplePath: '/network', namespaceLower: 'network', - pageName: SiemPageName.network, + pageName: SecurityPageName.network, detailName: undefined, }).noSearch.undefinedQuery; @@ -187,14 +187,14 @@ describe('UrlStateContainer - lodash.throttle mocked to test update url', () => page: CONSTANTS.hostsPage, examplePath: '/hosts', namespaceLower: 'hosts', - pageName: SiemPageName.hosts, + pageName: SecurityPageName.hosts, detailName: undefined, }).noSearch.undefinedQuery; const updatedProps = getMockPropsObj({ page: CONSTANTS.networkPage, examplePath: '/network', namespaceLower: 'network', - pageName: SiemPageName.network, + pageName: SecurityPageName.network, detailName: undefined, }).noSearch.definedQuery; const wrapper = mount( 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 index 0c69e24fb7476..dec1672b076eb 100644 --- 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 @@ -7,7 +7,7 @@ import { ActionCreator } from 'typescript-fsa'; import { DispatchUpdateTimeline } from '../../../timelines/components/open_timeline/types'; import { navTabs } from '../../../app/home/home_navigations'; -import { SiemPageName } from '../../../app/types'; +import { SecurityPageName } from '../../../app/types'; import { inputsActions } from '../../store/actions'; import { CONSTANTS } from './constants'; @@ -71,7 +71,7 @@ export const mockHistory = { }; export const defaultProps: UrlStateContainerPropTypes = { - pageName: SiemPageName.network, + pageName: SecurityPageName.network, detailName: undefined, tabName: HostsTableType.authentications, search: '', @@ -292,7 +292,7 @@ export const testCases: Array<[ /* namespaceUpper */ 'Network', /* pathName */ '/network', /* type */ networkModel.NetworkType.page, - /* pageName */ SiemPageName.network, + /* pageName */ SecurityPageName.network, /* detailName */ undefined, ], [ @@ -301,7 +301,7 @@ export const testCases: Array<[ /* namespaceUpper */ 'Hosts', /* pathName */ '/hosts', /* type */ hostsModel.HostsType.page, - /* pageName */ SiemPageName.hosts, + /* pageName */ SecurityPageName.hosts, /* detailName */ undefined, ], [ @@ -310,7 +310,7 @@ export const testCases: Array<[ /* namespaceUpper */ 'Hosts', /* pathName */ '/hosts/siem-es', /* type */ hostsModel.HostsType.details, - /* pageName */ SiemPageName.hosts, + /* pageName */ SecurityPageName.hosts, /* detailName */ 'host-test', ], [ @@ -319,7 +319,7 @@ export const testCases: Array<[ /* namespaceUpper */ 'Network', /* pathName */ '/network/ip/100.90.80', /* type */ networkModel.NetworkType.details, - /* pageName */ SiemPageName.network, + /* pageName */ SecurityPageName.network, /* detailName */ '100.90.80', ], [ @@ -328,7 +328,7 @@ export const testCases: Array<[ /* namespaceUpper */ 'Overview', /* pathName */ '/overview', /* type */ null, - /* pageName */ SiemPageName.overview, + /* pageName */ SecurityPageName.overview, /* detailName */ undefined, ], [ @@ -337,7 +337,7 @@ export const testCases: Array<[ /* namespaceUpper */ 'Timeline', /* pathName */ '/timeline', /* type */ null, - /* pageName */ SiemPageName.timelines, + /* pageName */ SecurityPageName.timelines, /* detailName */ undefined, ], ]; diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/use_url_state.tsx b/x-pack/plugins/security_solution/public/common/components/url_state/use_url_state.tsx index b3436a7da8297..3186843a4d709 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/use_url_state.tsx +++ b/x-pack/plugins/security_solution/public/common/components/url_state/use_url_state.tsx @@ -27,7 +27,7 @@ import { ALL_URL_STATE_KEYS, UrlStateToRedux, } from './types'; -import { SiemPageName } from '../../../app/types'; +import { SecurityPageName } from '../../../app/types'; function usePrevious(value: PreviousLocationUrlState) { const ref = useRef(value); @@ -204,7 +204,7 @@ export const useUrlStateHooks = ({ } }); } else if (pathName !== prevProps.pathName) { - handleInitialize(type, pageName === SiemPageName.detections); + handleInitialize(type, pageName === SecurityPageName.alerts); } }, [isInitializing, history, pathName, pageName, prevProps, urlState]); diff --git a/x-pack/plugins/security_solution/public/common/utils/route/spy_routes.tsx b/x-pack/plugins/security_solution/public/common/utils/route/spy_routes.tsx index 9030e2713548b..9b2f6c2d6b770 100644 --- a/x-pack/plugins/security_solution/public/common/utils/route/spy_routes.tsx +++ b/x-pack/plugins/security_solution/public/common/utils/route/spy_routes.tsx @@ -12,13 +12,16 @@ import deepEqual from 'fast-deep-equal'; import { SpyRouteProps } from './types'; import { useRouteSpy } from './use_route_spy'; -export const SpyRouteComponent = memo( +export const SpyRouteComponent = memo< + SpyRouteProps & { location: H.Location; pageName: string | undefined } +>( ({ location: { pathname, search }, history, match: { - params: { pageName, detailName, tabName, flowTarget }, + params: { detailName, tabName, flowTarget }, }, + pageName, state, }) => { const [isInitializing, setIsInitializing] = useState(true); diff --git a/x-pack/plugins/security_solution/public/common/utils/route/types.ts b/x-pack/plugins/security_solution/public/common/utils/route/types.ts index 912da545a66a3..8656f20c92959 100644 --- a/x-pack/plugins/security_solution/public/common/utils/route/types.ts +++ b/x-pack/plugins/security_solution/public/common/utils/route/types.ts @@ -60,7 +60,6 @@ export interface ManageRoutesSpyProps { } export type SpyRouteProps = RouteComponentProps<{ - pageName: string | undefined; detailName: string | undefined; tabName: HostsTableType | undefined; search: string; diff --git a/x-pack/plugins/security_solution/public/common/utils/timeline/use_show_timeline.tsx b/x-pack/plugins/security_solution/public/common/utils/timeline/use_show_timeline.tsx index 78f22a86c1893..6cd56bba5e6b0 100644 --- a/x-pack/plugins/security_solution/public/common/utils/timeline/use_show_timeline.tsx +++ b/x-pack/plugins/security_solution/public/common/utils/timeline/use_show_timeline.tsx @@ -7,9 +7,9 @@ import { useLocation } from 'react-router-dom'; import { useState, useEffect } from 'react'; -import { SiemPageName } from '../../../app/types'; +import { SecurityPageName } from '../../../app/types'; -const hideTimelineForRoutes = [`/${SiemPageName.case}/configure`]; +const hideTimelineForRoutes = [`/${SecurityPageName.case}/configure`]; export const useShowTimeline = () => { const currentLocation = useLocation(); diff --git a/x-pack/plugins/security_solution/public/hosts/pages/details/nav_tabs.tsx b/x-pack/plugins/security_solution/public/hosts/pages/details/nav_tabs.tsx index 4d04d16580a63..b1bee56535a06 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/details/nav_tabs.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/details/nav_tabs.tsx @@ -8,10 +8,10 @@ import { omit } from 'lodash/fp'; import * as i18n from '../translations'; import { HostDetailsNavTab } from './types'; import { HostsTableType } from '../../store/model'; -import { SiemPageName } from '../../../app/types'; +import { SecurityPageName } from '../../../app/types'; const getTabsOnHostDetailsUrl = (hostName: string, tabName: HostsTableType) => - `#/${SiemPageName.hosts}/${hostName}/${tabName}`; + `#/${SecurityPageName.hosts}/${hostName}/${tabName}`; export const navTabsHostDetails = ( hostName: string, diff --git a/x-pack/plugins/security_solution/public/hosts/pages/index.tsx b/x-pack/plugins/security_solution/public/hosts/pages/index.tsx index 336abc60e5ba1..38796d280df3e 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/index.tsx @@ -11,7 +11,7 @@ import { HostDetails } from './details'; import { HostsTableType } from '../store/model'; import { GlobalTime } from '../../common/containers/global_time'; -import { SiemPageName } from '../../app/types'; +import { SecurityPageName } from '../../app/types'; import { Hosts } from './hosts'; import { hostsPagePath, hostDetailsPagePath } from './types'; @@ -84,7 +84,7 @@ export const HostsContainer = React.memo(({ url }) => ( ( - + )} /> diff --git a/x-pack/plugins/security_solution/public/hosts/pages/nav_tabs.tsx b/x-pack/plugins/security_solution/public/hosts/pages/nav_tabs.tsx index 9bab3f7efe74a..dc26f588ed77a 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/nav_tabs.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/nav_tabs.tsx @@ -8,9 +8,9 @@ import { omit } from 'lodash/fp'; import * as i18n from './translations'; import { HostsTableType } from '../store/model'; import { HostsNavTab } from './navigation/types'; -import { SiemPageName } from '../../app/types'; +import { SecurityPageName } from '../../app/types'; -const getTabsOnHostsUrl = (tabName: HostsTableType) => `#/${SiemPageName.hosts}/${tabName}`; +const getTabsOnHostsUrl = (tabName: HostsTableType) => `#/${SecurityPageName.hosts}/${tabName}`; export const navTabsHosts = (hasMlUserPermissions: boolean): HostsNavTab => { const hostsNavTabs = { diff --git a/x-pack/plugins/security_solution/public/hosts/pages/types.ts b/x-pack/plugins/security_solution/public/hosts/pages/types.ts index 229349f390ecd..27d39a739bda1 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/types.ts +++ b/x-pack/plugins/security_solution/public/hosts/pages/types.ts @@ -7,12 +7,12 @@ import { IIndexPattern } from 'src/plugins/data/public'; import { ActionCreator } from 'typescript-fsa'; -import { SiemPageName } from '../../app/types'; +import { SecurityPageName } from '../../app/types'; import { hostsModel } from '../store'; import { GlobalTimeArgs } from '../../common/containers/global_time'; import { InputsModelId } from '../../common/store/inputs/constants'; -export const hostsPagePath = `/:pageName(${SiemPageName.hosts})`; +export const hostsPagePath = `/:pageName(${SecurityPageName.hosts})`; export const hostDetailsPagePath = `${hostsPagePath}/:detailName`; export type HostsTabsProps = HostsComponentProps & { diff --git a/x-pack/plugins/security_solution/public/hosts/routes.tsx b/x-pack/plugins/security_solution/public/hosts/routes.tsx index bf44d8f6f1ee2..0fcf1233bb4a2 100644 --- a/x-pack/plugins/security_solution/public/hosts/routes.tsx +++ b/x-pack/plugins/security_solution/public/hosts/routes.tsx @@ -8,12 +8,12 @@ import React from 'react'; import { Route, Switch } from 'react-router-dom'; import { HostsContainer } from './pages'; -import { SiemPageName } from '../app/types'; +import { SecurityPageName } from '../app/types'; export const HostsRoutes = () => ( } /> diff --git a/x-pack/plugins/security_solution/public/management/common/constants.ts b/x-pack/plugins/security_solution/public/management/common/constants.ts index 9ec6817c0bfce..e01d85bbbb9a2 100644 --- a/x-pack/plugins/security_solution/public/management/common/constants.ts +++ b/x-pack/plugins/security_solution/public/management/common/constants.ts @@ -3,11 +3,11 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { SiemPageName } from '../../app/types'; +import { SecurityPageName } from '../../app/types'; import { ManagementStoreGlobalNamespace, ManagementSubTab } from '../types'; // --[ ROUTING ]--------------------------------------------------------------------------- -export const MANAGEMENT_ROUTING_ROOT_PATH = `/:pageName(${SiemPageName.management})`; +export const MANAGEMENT_ROUTING_ROOT_PATH = `/:pageName(${SecurityPageName.management})`; export const MANAGEMENT_ROUTING_ENDPOINTS_PATH = `${MANAGEMENT_ROUTING_ROOT_PATH}/:tabName(${ManagementSubTab.endpoints})`; export const MANAGEMENT_ROUTING_POLICIES_PATH = `${MANAGEMENT_ROUTING_ROOT_PATH}/:tabName(${ManagementSubTab.policies})`; export const MANAGEMENT_ROUTING_POLICY_DETAILS_PATH = `${MANAGEMENT_ROUTING_ROOT_PATH}/:tabName(${ManagementSubTab.policies})/:policyId`; diff --git a/x-pack/plugins/security_solution/public/management/common/routing.ts b/x-pack/plugins/security_solution/public/management/common/routing.ts index e64fcf0c5f68a..27e97eb0580a1 100644 --- a/x-pack/plugins/security_solution/public/management/common/routing.ts +++ b/x-pack/plugins/security_solution/public/management/common/routing.ts @@ -12,7 +12,7 @@ import { MANAGEMENT_ROUTING_ROOT_PATH, } from './constants'; import { ManagementSubTab } from '../types'; -import { SiemPageName } from '../../app/types'; +import { SecurityPageName } from '../../app/types'; export type GetManagementUrlProps = { /** @@ -42,24 +42,24 @@ export const getManagementUrl = (props: GetManagementUrlProps): string => { switch (props.name) { case 'default': url += generatePath(MANAGEMENT_ROUTING_ROOT_PATH, { - pageName: SiemPageName.management, + pageName: SecurityPageName.management, }); break; case 'endpointList': url += generatePath(MANAGEMENT_ROUTING_ENDPOINTS_PATH, { - pageName: SiemPageName.management, + pageName: SecurityPageName.management, tabName: ManagementSubTab.endpoints, }); break; case 'policyList': url += generatePath(MANAGEMENT_ROUTING_POLICIES_PATH, { - pageName: SiemPageName.management, + pageName: SecurityPageName.management, tabName: ManagementSubTab.policies, }); break; case 'policyDetails': url += generatePath(MANAGEMENT_ROUTING_POLICY_DETAILS_PATH, { - pageName: SiemPageName.management, + pageName: SecurityPageName.management, tabName: ManagementSubTab.policies, policyId: props.policyId, }); diff --git a/x-pack/plugins/security_solution/public/management/types.ts b/x-pack/plugins/security_solution/public/management/types.ts index eeeafb4cbe150..6bcc17e627a1c 100644 --- a/x-pack/plugins/security_solution/public/management/types.ts +++ b/x-pack/plugins/security_solution/public/management/types.ts @@ -5,7 +5,7 @@ */ import { CombinedState } from 'redux'; -import { SiemPageName } from '../app/types'; +import { SecurityPageName } from '../app/types'; import { PolicyListState, PolicyDetailsState } from './pages/policy/types'; /** @@ -31,7 +31,7 @@ export enum ManagementSubTab { * The URL route params for the Management Policy List section */ export interface ManagementRoutePolicyListParams { - pageName: SiemPageName.management; + pageName: SecurityPageName.management; tabName: ManagementSubTab.policies; } diff --git a/x-pack/plugins/security_solution/public/network/pages/index.tsx b/x-pack/plugins/security_solution/public/network/pages/index.tsx index c6f13c118c309..06d020d6e3b00 100644 --- a/x-pack/plugins/security_solution/public/network/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/index.tsx @@ -14,13 +14,13 @@ import { FlowTarget } from '../../graphql/types'; import { IPDetails } from './ip_details'; import { Network } from './network'; import { GlobalTime } from '../../common/containers/global_time'; -import { SiemPageName } from '../../app/types'; +import { SecurityPageName } from '../../app/types'; import { getNetworkRoutePath } from './navigation'; import { NetworkRouteType } from './navigation/types'; type Props = Partial> & { url: string }; -const networkPagePath = `/:pageName(${SiemPageName.network})`; +const networkPagePath = `/:pageName(${SecurityPageName.network})`; const ipDetailsPageBasePath = `${networkPagePath}/ip/:detailName`; const NetworkContainerComponent: React.FC = () => { @@ -81,14 +81,14 @@ const NetworkContainerComponent: React.FC = () => { }, }) => ( )} /> ( - + )} /> diff --git a/x-pack/plugins/security_solution/public/network/routes.tsx b/x-pack/plugins/security_solution/public/network/routes.tsx index 4fc39e54a8607..30d7327ffcc97 100644 --- a/x-pack/plugins/security_solution/public/network/routes.tsx +++ b/x-pack/plugins/security_solution/public/network/routes.tsx @@ -8,12 +8,12 @@ import React from 'react'; import { Route, Switch } from 'react-router-dom'; import { NetworkContainer } from './pages'; -import { SiemPageName } from '../app/types'; +import { SecurityPageName } from '../app/types'; export const NetworkRoutes = () => ( } /> diff --git a/x-pack/plugins/security_solution/public/overview/routes.tsx b/x-pack/plugins/security_solution/public/overview/routes.tsx index 5ece275a84ed2..25fc5f9402c4d 100644 --- a/x-pack/plugins/security_solution/public/overview/routes.tsx +++ b/x-pack/plugins/security_solution/public/overview/routes.tsx @@ -8,10 +8,10 @@ import React from 'react'; import { Route, Switch } from 'react-router-dom'; import { Overview } from './pages'; -import { SiemPageName } from '../app/types'; +import { SecurityPageName } from '../app/types'; export const OverviewRoutes = () => ( - } /> + } /> ); diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index 439cf222a5394..4e959ebf61875 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -22,18 +22,17 @@ import { initTelemetry } from './common/lib/telemetry'; import { KibanaServices } from './common/lib/kibana/services'; import { serviceNowActionType, jiraActionType } from './common/lib/connectors'; import { PluginSetup, PluginStart, SetupPlugins, StartPlugins, StartServices } from './types'; -import { APP_ID, APP_ICON, APP_PATH } from '../common/constants'; +import { APP_ID, APP_ICON, APP_PATH, APP_ALERT_PATH } from '../common/constants'; import { ConfigureEndpointDatasource } from './management/pages/policy/view/ingest_manager_integration/configure_datasource'; import { State, createStore, createInitialState } from './common/store'; export class Plugin implements IPlugin { private kibanaVersion: string; - private store: Store | null; + private store!: Store; constructor(initializerContext: PluginInitializerContext) { this.kibanaVersion = initializerContext.env.packageInfo.version; - this.store = null; } public setup(core: CoreSetup, plugins: SetupPlugins) { @@ -72,14 +71,16 @@ export class Plugin implements IPlugin { const [ - { coreStart, startPlugins, services }, + { coreStart, store, services }, { renderApp, composeLibs }, { overviewSubPlugin }, ] = await Promise.all([ @@ -91,7 +92,7 @@ export class Plugin implements IPlugin { const [ { coreStart, store, services }, { renderApp, composeLibs }, @@ -131,7 +134,7 @@ export class Plugin implements IPlugin { const [ { coreStart, store, services }, { renderApp, composeLibs }, @@ -158,7 +161,7 @@ export class Plugin implements IPlugin { const [ { coreStart, store, services }, { renderApp, composeLibs }, @@ -185,7 +188,7 @@ export class Plugin implements IPlugin { const [ { coreStart, store, services }, { renderApp, composeLibs }, @@ -212,7 +215,7 @@ export class Plugin implements IPlugin { const [ { coreStart, store, services }, { renderApp, composeLibs }, @@ -239,9 +242,9 @@ export class Plugin implements IPlugin { const [ - { coreStart, store, services }, + { coreStart, startPlugins, store, services }, { renderApp, composeLibs }, { managementSubPlugin }, ] = await Promise.all([ @@ -254,7 +257,7 @@ export class Plugin implements IPlugin( const handleClick = useCallback(() => { onClosePopover(); history.push({ - pathname: `/${SiemPageName.case}/create`, + pathname: `/${SecurityPageName.case}/create`, state: { insertTimeline: { timelineId, diff --git a/x-pack/plugins/security_solution/public/timelines/pages/index.tsx b/x-pack/plugins/security_solution/public/timelines/pages/index.tsx index 73494fb585b30..f07bb71c6f46c 100644 --- a/x-pack/plugins/security_solution/public/timelines/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/pages/index.tsx @@ -15,13 +15,13 @@ import { TAB_TIMELINES, TAB_TEMPLATES } from '../components/open_timeline/transl import { getTimelinesUrl } from '../../common/components/link_to'; import { TimelineRouteSpyState } from '../../common/utils/route/types'; -import { SiemPageName } from '../../app/types'; +import { SecurityPageName } from '../../app/types'; import { TimelinesPage } from './timelines_page'; import { PAGE_TITLE } from './translations'; import { appendSearch } from '../../common/components/link_to/helpers'; -const timelinesPagePath = `/:pageName(${SiemPageName.timelines})/:tabName(${TimelineType.default}|${TimelineType.template})`; -const timelinesDefaultPath = `/${SiemPageName.timelines}/${TimelineType.default}`; +const timelinesPagePath = `/:pageName(${SecurityPageName.timelines})/:tabName(${TimelineType.default}|${TimelineType.template})`; +const timelinesDefaultPath = `/${SecurityPageName.timelines}/${TimelineType.default}`; const TabNameMappedToI18nKey: Record = { [TimelineType.default]: TAB_TIMELINES, @@ -59,7 +59,7 @@ export const Timelines = React.memo(() => { {(client) => } ( )} diff --git a/x-pack/plugins/security_solution/public/timelines/routes.tsx b/x-pack/plugins/security_solution/public/timelines/routes.tsx index 322a7e704b490..ce55ae9ed6d8f 100644 --- a/x-pack/plugins/security_solution/public/timelines/routes.tsx +++ b/x-pack/plugins/security_solution/public/timelines/routes.tsx @@ -8,10 +8,10 @@ import React from 'react'; import { Route, Switch } from 'react-router-dom'; import { Timelines } from './pages'; -import { SiemPageName } from '../app/types'; +import { SecurityPageName } from '../app/types'; export const TimelinesRoutes = () => ( - } /> + } /> ); From da728cb6aa4890db11ea241a9928b6950d2ea3f0 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Wed, 10 Jun 2020 22:15:59 -0400 Subject: [PATCH 03/43] modify alerts routes --- .../components/activity_monitor/columns.tsx | 97 ------ .../activity_monitor/index.test.tsx | 18 - .../components/activity_monitor/index.tsx | 320 ------------------ .../components/activity_monitor/types.ts | 29 -- .../alerts_histogram_panel/index.tsx | 25 +- .../pre_packaged_rules/load_empty_prompt.tsx | 24 +- .../rules/rule_actions_overflow/index.tsx | 4 +- .../security_solution/public/alerts/index.ts | 4 +- .../detection_engine/detection_engine.tsx | 26 +- .../alerts/pages/detection_engine/index.tsx | 28 +- .../detection_engine/rules/all/actions.tsx | 7 +- .../detection_engine/rules/all/columns.tsx | 33 +- .../detection_engine/rules/all/index.tsx | 10 +- .../detection_engine/rules/create/index.tsx | 26 +- .../detection_engine/rules/details/index.tsx | 31 +- .../detection_engine/rules/edit/index.tsx | 33 +- .../pages/detection_engine/rules/index.tsx | 29 +- .../pages/detection_engine/rules/utils.ts | 2 +- .../public/alerts/routes.tsx | 23 +- 19 files changed, 214 insertions(+), 555 deletions(-) delete mode 100644 x-pack/plugins/security_solution/public/alerts/components/activity_monitor/columns.tsx delete mode 100644 x-pack/plugins/security_solution/public/alerts/components/activity_monitor/index.test.tsx delete mode 100644 x-pack/plugins/security_solution/public/alerts/components/activity_monitor/index.tsx delete mode 100644 x-pack/plugins/security_solution/public/alerts/components/activity_monitor/types.ts diff --git a/x-pack/plugins/security_solution/public/alerts/components/activity_monitor/columns.tsx b/x-pack/plugins/security_solution/public/alerts/components/activity_monitor/columns.tsx deleted file mode 100644 index 51a5397637e7c..0000000000000 --- a/x-pack/plugins/security_solution/public/alerts/components/activity_monitor/columns.tsx +++ /dev/null @@ -1,97 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -/* eslint-disable react/display-name */ - -import { - EuiIconTip, - EuiLink, - EuiTextColor, - EuiBasicTableColumn, - EuiTableActionsColumnType, -} from '@elastic/eui'; -import React from 'react'; -import { getEmptyTagValue } from '../../../common/components/empty_value'; -import { ColumnTypes } from './types'; - -const actions: EuiTableActionsColumnType['actions'] = [ - { - available: (item: ColumnTypes) => item.status === 'Running', - description: 'Stop', - icon: 'stop', - isPrimary: true, - name: 'Stop', - onClick: () => {}, - type: 'icon', - }, - { - available: (item: ColumnTypes) => item.status === 'Stopped', - description: 'Resume', - icon: 'play', - isPrimary: true, - name: 'Resume', - onClick: () => {}, - type: 'icon', - }, -]; - -// Michael: Are we able to do custom, in-table-header filters, as shown in my wireframes? -export const columns: Array> = [ - { - field: 'rule' as const, - name: 'Rule', - render: (value: ColumnTypes['rule'], _: ColumnTypes) => ( - {value.name} - ), - sortable: true, - truncateText: true, - }, - { - field: 'ran' as const, - name: 'Ran', - render: (value: ColumnTypes['ran'], _: ColumnTypes) => '--', - sortable: true, - truncateText: true, - }, - { - field: 'lookedBackTo' as const, - name: 'Looked back to', - render: (value: ColumnTypes['lookedBackTo'], _: ColumnTypes) => '--', - sortable: true, - truncateText: true, - }, - { - field: 'status' as const, - name: 'Status', - sortable: true, - truncateText: true, - }, - { - field: 'response' as const, - name: 'Response', - render: (value: ColumnTypes['response'], _: ColumnTypes) => { - return value === undefined ? ( - getEmptyTagValue() - ) : ( - <> - {value === 'Fail' ? ( - - {value} - - ) : ( - {value} - )} - - ); - }, - sortable: true, - truncateText: true, - }, - { - actions, - width: '40px', - }, -]; diff --git a/x-pack/plugins/security_solution/public/alerts/components/activity_monitor/index.test.tsx b/x-pack/plugins/security_solution/public/alerts/components/activity_monitor/index.test.tsx deleted file mode 100644 index c5a4057b64ea7..0000000000000 --- a/x-pack/plugins/security_solution/public/alerts/components/activity_monitor/index.test.tsx +++ /dev/null @@ -1,18 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { shallow } from 'enzyme'; - -import { ActivityMonitor } from './index'; - -describe('activity_monitor', () => { - it('renders correctly', () => { - const wrapper = shallow(); - - expect(wrapper.find('[title="Activity monitor"]')).toBeTruthy(); - }); -}); diff --git a/x-pack/plugins/security_solution/public/alerts/components/activity_monitor/index.tsx b/x-pack/plugins/security_solution/public/alerts/components/activity_monitor/index.tsx deleted file mode 100644 index c2b6b0f025e1d..0000000000000 --- a/x-pack/plugins/security_solution/public/alerts/components/activity_monitor/index.tsx +++ /dev/null @@ -1,320 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { EuiBasicTable, EuiPanel, EuiSpacer } from '@elastic/eui'; -import React, { useState, useCallback } from 'react'; -import { HeaderSection } from '../../../common/components/header_section'; -import { - UtilityBar, - UtilityBarAction, - UtilityBarGroup, - UtilityBarSection, - UtilityBarText, -} from '../../../common/components/utility_bar'; -import { columns } from './columns'; -import { ColumnTypes, PageTypes, SortTypes } from './types'; - -export const ActivityMonitor = React.memo(() => { - const sampleTableData: ColumnTypes[] = [ - { - id: 1, - rule: { - href: '#/detections/rules/rule-details', - name: 'Automated exfiltration', - }, - ran: '2019-12-28 00:00:00.000-05:00', - lookedBackTo: '2019-12-28 00:00:00.000-05:00', - status: 'Running', - }, - { - id: 2, - rule: { - href: '#/detections/rules/rule-details', - name: 'Automated exfiltration', - }, - ran: '2019-12-28 00:00:00.000-05:00', - lookedBackTo: '2019-12-28 00:00:00.000-05:00', - status: 'Stopped', - }, - { - id: 3, - rule: { - href: '#/detections/rules/rule-details', - name: 'Automated exfiltration', - }, - ran: '2019-12-28 00:00:00.000-05:00', - lookedBackTo: '2019-12-28 00:00:00.000-05:00', - status: 'Completed', - response: 'Fail', - }, - { - id: 4, - rule: { - href: '#/detections/rules/rule-details', - name: 'Automated exfiltration', - }, - ran: '2019-12-28 00:00:00.000-05:00', - lookedBackTo: '2019-12-28 00:00:00.000-05:00', - status: 'Completed', - response: 'Success', - }, - { - id: 5, - rule: { - href: '#/detections/rules/rule-details', - name: 'Automated exfiltration', - }, - ran: '2019-12-28 00:00:00.000-05:00', - lookedBackTo: '2019-12-28 00:00:00.000-05:00', - status: 'Completed', - response: 'Success', - }, - { - id: 6, - rule: { - href: '#/detections/rules/rule-details', - name: 'Automated exfiltration', - }, - ran: '2019-12-28 00:00:00.000-05:00', - lookedBackTo: '2019-12-28 00:00:00.000-05:00', - status: 'Completed', - response: 'Success', - }, - { - id: 7, - rule: { - href: '#/detections/rules/rule-details', - name: 'Automated exfiltration', - }, - ran: '2019-12-28 00:00:00.000-05:00', - lookedBackTo: '2019-12-28 00:00:00.000-05:00', - status: 'Completed', - response: 'Success', - }, - { - id: 8, - rule: { - href: '#/detections/rules/rule-details', - name: 'Automated exfiltration', - }, - ran: '2019-12-28 00:00:00.000-05:00', - lookedBackTo: '2019-12-28 00:00:00.000-05:00', - status: 'Completed', - response: 'Success', - }, - { - id: 9, - rule: { - href: '#/detections/rules/rule-details', - name: 'Automated exfiltration', - }, - ran: '2019-12-28 00:00:00.000-05:00', - lookedBackTo: '2019-12-28 00:00:00.000-05:00', - status: 'Completed', - response: 'Success', - }, - { - id: 10, - rule: { - href: '#/detections/rules/rule-details', - name: 'Automated exfiltration', - }, - ran: '2019-12-28 00:00:00.000-05:00', - lookedBackTo: '2019-12-28 00:00:00.000-05:00', - status: 'Completed', - response: 'Success', - }, - { - id: 11, - rule: { - href: '#/detections/rules/rule-details', - name: 'Automated exfiltration', - }, - ran: '2019-12-28 00:00:00.000-05:00', - lookedBackTo: '2019-12-28 00:00:00.000-05:00', - status: 'Completed', - response: 'Success', - }, - { - id: 12, - rule: { - href: '#/detections/rules/rule-details', - name: 'Automated exfiltration', - }, - ran: '2019-12-28 00:00:00.000-05:00', - lookedBackTo: '2019-12-28 00:00:00.000-05:00', - status: 'Completed', - response: 'Success', - }, - { - id: 13, - rule: { - href: '#/detections/rules/rule-details', - name: 'Automated exfiltration', - }, - ran: '2019-12-28 00:00:00.000-05:00', - lookedBackTo: '2019-12-28 00:00:00.000-05:00', - status: 'Completed', - response: 'Success', - }, - { - id: 14, - rule: { - href: '#/detections/rules/rule-details', - name: 'Automated exfiltration', - }, - ran: '2019-12-28 00:00:00.000-05:00', - lookedBackTo: '2019-12-28 00:00:00.000-05:00', - status: 'Completed', - response: 'Success', - }, - { - id: 15, - rule: { - href: '#/detections/rules/rule-details', - name: 'Automated exfiltration', - }, - ran: '2019-12-28 00:00:00.000-05:00', - lookedBackTo: '2019-12-28 00:00:00.000-05:00', - status: 'Completed', - response: 'Success', - }, - { - id: 16, - rule: { - href: '#/detections/rules/rule-details', - name: 'Automated exfiltration', - }, - ran: '2019-12-28 00:00:00.000-05:00', - lookedBackTo: '2019-12-28 00:00:00.000-05:00', - status: 'Completed', - response: 'Success', - }, - { - id: 17, - rule: { - href: '#/detections/rules/rule-details', - name: 'Automated exfiltration', - }, - ran: '2019-12-28 00:00:00.000-05:00', - lookedBackTo: '2019-12-28 00:00:00.000-05:00', - status: 'Completed', - response: 'Success', - }, - { - id: 18, - rule: { - href: '#/detections/rules/rule-details', - name: 'Automated exfiltration', - }, - ran: '2019-12-28 00:00:00.000-05:00', - lookedBackTo: '2019-12-28 00:00:00.000-05:00', - status: 'Completed', - response: 'Success', - }, - { - id: 19, - rule: { - href: '#/detections/rules/rule-details', - name: 'Automated exfiltration', - }, - ran: '2019-12-28 00:00:00.000-05:00', - lookedBackTo: '2019-12-28 00:00:00.000-05:00', - status: 'Completed', - response: 'Success', - }, - { - id: 20, - rule: { - href: '#/detections/rules/rule-details', - name: 'Automated exfiltration', - }, - ran: '2019-12-28 00:00:00.000-05:00', - lookedBackTo: '2019-12-28 00:00:00.000-05:00', - status: 'Completed', - response: 'Success', - }, - { - id: 21, - rule: { - href: '#/detections/rules/rule-details', - name: 'Automated exfiltration', - }, - ran: '2019-12-28 00:00:00.000-05:00', - lookedBackTo: '2019-12-28 00:00:00.000-05:00', - status: 'Completed', - response: 'Success', - }, - ]; - - const [itemsTotalState] = useState(sampleTableData.length); - const [pageState, setPageState] = useState({ index: 0, size: 20 }); - // const [selectedState, setSelectedState] = useState([]); - const [sortState, setSortState] = useState({ field: 'ran', direction: 'desc' }); - - const handleChange = useCallback( - ({ page, sort }: { page?: PageTypes; sort?: SortTypes }) => { - setPageState(page!); - setSortState(sort!); - }, - [setPageState, setSortState] - ); - - return ( - <> - - - - - - - - {'Showing: 39 activites'} - - - - {'Selected: 2 activities'} - - {'Stop selected'} - - - - {'Clear 7 filters'} - - - - { - // @ts-ignore `Columns` interface differs from EUI's `column` type and is used all over this plugin, so ignore the differences instead of refactoring a lot of code - } - item.status !== 'Completed', - selectableMessage: (selectable: boolean) => - selectable ? '' : 'Completed runs cannot be acted upon', - onSelectionChange: (selectedItems: ColumnTypes[]) => { - // setSelectedState(selectedItems); - }, - }} - sorting={{ - sort: sortState, - }} - /> - - - ); -}); -ActivityMonitor.displayName = 'ActivityMonitor'; diff --git a/x-pack/plugins/security_solution/public/alerts/components/activity_monitor/types.ts b/x-pack/plugins/security_solution/public/alerts/components/activity_monitor/types.ts deleted file mode 100644 index 816992ff940dd..0000000000000 --- a/x-pack/plugins/security_solution/public/alerts/components/activity_monitor/types.ts +++ /dev/null @@ -1,29 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -export interface RuleTypes { - href: string; - name: string; -} - -export interface ColumnTypes { - id: number; - rule: RuleTypes; - ran: string; - lookedBackTo: string; - status: string; - response?: string | undefined; -} - -export interface PageTypes { - index: number; - size: number; -} - -export interface SortTypes { - field: keyof ColumnTypes; - direction: 'asc' | 'desc'; -} diff --git a/x-pack/plugins/security_solution/public/alerts/components/alerts_histogram_panel/index.tsx b/x-pack/plugins/security_solution/public/alerts/components/alerts_histogram_panel/index.tsx index 7451ea6ec0ca1..5ddb5a81e027c 100644 --- a/x-pack/plugins/security_solution/public/alerts/components/alerts_histogram_panel/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/components/alerts_histogram_panel/index.tsx @@ -4,13 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ import { Position } from '@elastic/charts'; -import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiSelect, EuiPanel } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiSelect, EuiPanel } from '@elastic/eui'; import numeral from '@elastic/numeral'; import React, { memo, useCallback, useMemo, useState, useEffect } from 'react'; import styled from 'styled-components'; import { isEmpty } from 'lodash/fp'; import uuid from 'uuid'; +import { useHistory } from 'react-router-dom'; import { DEFAULT_NUMBER_FORMAT } from '../../../../common/constants'; import { UpdateDateRange } from '../../../common/components/charts/common'; import { LegendItem } from '../../../common/components/charts/draggable_legend_item'; @@ -18,19 +19,19 @@ import { escapeDataProviderId } from '../../../common/components/drag_and_drop/h import { HeaderSection } from '../../../common/components/header_section'; import { Filter, esQuery, Query } from '../../../../../../../src/plugins/data/public'; import { useQueryAlerts } from '../../containers/detection_engine/alerts/use_query'; -import { getDetectionEngineUrl } from '../../../common/components/link_to'; +import { getDetectionEngineUrl, useFormatUrl } from '../../../common/components/link_to'; import { defaultLegendColors } from '../../../common/components/matrix_histogram/utils'; import { InspectButtonContainer } from '../../../common/components/inspect'; -import { useGetUrlSearch } from '../../../common/components/navigation/use_get_url_search'; import { MatrixLoader } from '../../../common/components/matrix_histogram/matrix_loader'; import { MatrixHistogramOption } from '../../../common/components/matrix_histogram/types'; import { useKibana, useUiSetting$ } from '../../../common/lib/kibana'; -import { navTabs } from '../../../app/home/home_navigations'; import { alertsHistogramOptions } from './config'; import { formatAlertsData, getAlertsHistogramQuery, showInitialLoadingSpinner } from './helpers'; import { AlertsHistogram } from './alerts_histogram'; import * as i18n from './translations'; import { RegisterQuery, AlertsHistogramOption, AlertsAggregation, AlertsTotal } from './types'; +import { LinkButton } from '../../../common/components/links'; +import { SecurityPageName } from '../../../app/types'; const DEFAULT_PANEL_HEIGHT = 300; @@ -102,6 +103,7 @@ export const AlertsHistogramPanel = memo( title = i18n.HISTOGRAM_HEADER, updateDateRange, }) => { + const history = useHistory(); // create a unique, but stable (across re-renders) query id const uniqueQueryId = useMemo(() => `${DETECTIONS_HISTOGRAM_ID}-${uuid.v4()}`, []); const [isInitialLoading, setIsInitialLoading] = useState(true); @@ -122,7 +124,7 @@ export const AlertsHistogramPanel = memo( signalIndexName ); const kibana = useKibana(); - const urlSearch = useGetUrlSearch(navTabs.detections); + const { formatUrl, search: urlSearch } = useFormatUrl(SecurityPageName.alerts); const totalAlerts = useMemo( () => @@ -140,6 +142,13 @@ export const AlertsHistogramPanel = memo( ); }, []); + const goToDetectionEngine = useCallback( + (ev) => { + ev.preventDefault(); + history.push(getDetectionEngineUrl(urlSearch)); + }, + [history, urlSearch] + ); const formattedAlertsData = useMemo(() => formatAlertsData(alertsData), [alertsData]); const legendItems: LegendItem[] = useMemo( @@ -225,11 +234,13 @@ export const AlertsHistogramPanel = memo( if (showLinkToAlerts) { return ( - {i18n.VIEW_ALERTS} + + {i18n.VIEW_ALERTS} + ); } - }, [showLinkToAlerts, urlSearch]); + }, [showLinkToAlerts, goToDetectionEngine]); const titleText = useMemo(() => (onlyField == null ? title : i18n.TOP(onlyField)), [ onlyField, diff --git a/x-pack/plugins/security_solution/public/alerts/components/rules/pre_packaged_rules/load_empty_prompt.tsx b/x-pack/plugins/security_solution/public/alerts/components/rules/pre_packaged_rules/load_empty_prompt.tsx index cd88c4ce72af8..d82b930210ecd 100644 --- a/x-pack/plugins/security_solution/public/alerts/components/rules/pre_packaged_rules/load_empty_prompt.tsx +++ b/x-pack/plugins/security_solution/public/alerts/components/rules/pre_packaged_rules/load_empty_prompt.tsx @@ -8,8 +8,12 @@ import { EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem, EuiButton } from '@elastic/e import React, { memo, useCallback } from 'react'; import styled from 'styled-components'; -import { DETECTION_ENGINE_PAGE_NAME } from '../../../../common/components/link_to/redirect_to_detection_engine'; +import { useHistory } from 'react-router-dom'; +import { getCreateRuleUrl } from '../../../../common/components/link_to/redirect_to_detection_engine'; import * as i18n from './translations'; +import { LinkButton } from '../../../../common/components/links'; +import { SecurityPageName } from '../../../../app/types'; +import { useFormatUrl } from '../../../../common/components/link_to'; const EmptyPrompt = styled(EuiEmptyPrompt)` align-self: center; /* Corrects horizontal centering in IE11 */ @@ -28,9 +32,20 @@ const PrePackagedRulesPromptComponent: React.FC = ( loading = false, userHasNoPermissions = true, }) => { + const history = useHistory(); const handlePreBuiltCreation = useCallback(() => { createPrePackagedRules(); }, [createPrePackagedRules]); + const { formatUrl } = useFormatUrl(SecurityPageName.alerts); + + const goToCreateRule = useCallback( + (ev) => { + ev.preventDefault(); + history.push(getCreateRuleUrl()); + }, + [history] + ); + return ( = ( - {i18n.CREATE_RULE_ACTION} - + } diff --git a/x-pack/plugins/security_solution/public/alerts/components/rules/rule_actions_overflow/index.tsx b/x-pack/plugins/security_solution/public/alerts/components/rules/rule_actions_overflow/index.tsx index 5a5156fa2b9a3..789892af9c7fc 100644 --- a/x-pack/plugins/security_solution/public/alerts/components/rules/rule_actions_overflow/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/components/rules/rule_actions_overflow/index.tsx @@ -25,7 +25,7 @@ import { duplicateRulesAction, } from '../../../pages/detection_engine/rules/all/actions'; import { GenericDownloader } from '../../../../common/components/generic_downloader'; -import { DETECTION_ENGINE_PAGE_NAME } from '../../../../common/components/link_to/redirect_to_detection_engine'; +import { getRulesUrl } from '../../../../common/components/link_to/redirect_to_detection_engine'; const MyEuiButtonIcon = styled(EuiButtonIcon)` &.euiButtonIcon { @@ -56,7 +56,7 @@ const RuleActionsOverflowComponent = ({ const [, dispatchToaster] = useStateToaster(); const onRuleDeletedCallback = useCallback(() => { - history.push(`/${DETECTION_ENGINE_PAGE_NAME}/rules`); + history.push(getRulesUrl()); }, [history]); const actions = useMemo( diff --git a/x-pack/plugins/security_solution/public/alerts/index.ts b/x-pack/plugins/security_solution/public/alerts/index.ts index c1501419a1cf6..ebf68e093a4c6 100644 --- a/x-pack/plugins/security_solution/public/alerts/index.ts +++ b/x-pack/plugins/security_solution/public/alerts/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { getAlertsRoutes } from './routes'; +import { AlertsRoutes } from './routes'; import { SecuritySubPlugin } from '../app/types'; export class Alerts { @@ -12,7 +12,7 @@ export class Alerts { public start(): SecuritySubPlugin { return { - routes: getAlertsRoutes(), + SubPluginRoutes: AlertsRoutes, }; } } diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/detection_engine.tsx b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/detection_engine.tsx index e3eb4666522ad..41379f562bb34 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/detection_engine.tsx +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/detection_engine.tsx @@ -4,11 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiButton, EuiSpacer } from '@elastic/eui'; +import { EuiSpacer } from '@elastic/eui'; import React, { useCallback, useMemo } from 'react'; import { StickyContainer } from 'react-sticky'; import { connect, ConnectedProps } from 'react-redux'; +import { useHistory } from 'react-router-dom'; +import { SecurityPageName } from '../../../app/types'; import { GlobalTime } from '../../../common/containers/global_time'; import { indicesExistOrDataTemporarilyUnavailable, @@ -36,6 +38,8 @@ import { DetectionEngineNoIndex } from './detection_engine_no_signal_index'; import { DetectionEngineHeaderPage } from '../../components/detection_engine_header_page'; import { DetectionEngineUserUnauthenticated } from './detection_engine_user_unauthenticated'; import * as i18n from './translations'; +import { LinkButton } from '../../../common/components/links'; +import { useFormatUrl } from '../../../common/components/link_to'; export const DetectionEnginePageComponent: React.FC = ({ filters, @@ -51,8 +55,9 @@ export const DetectionEnginePageComponent: React.FC = ({ signalIndexName, hasIndexWrite, } = useUserInfo(); - + const history = useHistory(); const [lastAlerts] = useAlertInfo({}); + const { formatUrl } = useFormatUrl(SecurityPageName.alerts); const updateDateRangeCallback = useCallback( ({ x }) => { @@ -65,6 +70,14 @@ export const DetectionEnginePageComponent: React.FC = ({ [setAbsoluteRangeDatePicker] ); + const goToRules = useCallback( + (ev) => { + ev.preventDefault(); + history.push(getRulesUrl()); + }, + [history] + ); + const indexToAdd = useMemo(() => (signalIndexName == null ? [] : [signalIndexName]), [ signalIndexName, ]); @@ -110,14 +123,15 @@ export const DetectionEnginePageComponent: React.FC = ({ } title={i18n.PAGE_TITLE} > - {i18n.BUTTON_MANAGE_RULES} - + @@ -159,7 +173,7 @@ export const DetectionEnginePageComponent: React.FC = ({ ); }} - + ); }; diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/index.tsx b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/index.tsx index 1f9b1373d404d..914734aba4ec6 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/index.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; -import { Redirect, Route, Switch, RouteComponentProps } from 'react-router-dom'; +import { Route, Switch, RouteComponentProps } from 'react-router-dom'; import { ManageUserInfo } from '../../components/user_info'; import { CreateRulePage } from './rules/create'; @@ -14,34 +14,26 @@ import { EditRulePage } from './rules/edit'; import { RuleDetailsPage } from './rules/details'; import { RulesPage } from './rules'; -const detectionEnginePath = `/:pageName(detections)`; - type Props = Partial> & { url: string }; const DetectionEngineContainerComponent: React.FC = () => ( - - + + - - + + - + - - + + - - + + - ( - - )} - /> ); diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/all/actions.tsx b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/all/actions.tsx index d414321e4b775..5169ff009d63c 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/all/actions.tsx +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/all/actions.tsx @@ -7,14 +7,14 @@ import * as H from 'history'; import React, { Dispatch } from 'react'; -import { DETECTION_ENGINE_PAGE_NAME } from '../../../../../common/components/link_to/redirect_to_detection_engine'; import { deleteRules, duplicateRules, enableRules, Rule, } from '../../../../../alerts/containers/detection_engine/rules'; -import { Action } from './reducer'; + +import { getEditRuleUrl } from '../../../../../common/components/link_to/redirect_to_detection_engine'; import { ActionToaster, @@ -26,9 +26,10 @@ import { track, METRIC_TYPE, TELEMETRY_EVENT } from '../../../../../common/lib/t import * as i18n from '../translations'; import { bucketRulesResponse } from './helpers'; +import { Action } from './reducer'; export const editRuleAction = (rule: Rule, history: H.History) => { - history.push(`/${DETECTION_ENGINE_PAGE_NAME}/rules/id/${rule.id}/edit`); + history.push(getEditRuleUrl(rule.id)); }; export const duplicateRulesAction = async ( diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/all/columns.tsx b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/all/columns.tsx index cf8a3fdb6f1e6..030f510b7aa37 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/all/columns.tsx +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/all/columns.tsx @@ -8,7 +8,6 @@ import { EuiBadge, - EuiLink, EuiBasicTableColumn, EuiTableActionsColumnType, EuiText, @@ -39,6 +38,7 @@ import { import { Action } from './reducer'; import { LocalizedDateTooltip } from '../../../../../common/components/localized_date_tooltip'; import * as detectionI18n from '../../translations'; +import { LinkAnchor } from '../../../../../common/components/links'; export const getActions = ( dispatch: React.Dispatch, @@ -87,10 +87,11 @@ export type RuleStatusRowItemType = RuleStatus & { }; export type RulesColumns = EuiBasicTableColumn | EuiTableActionsColumnType; export type RulesStatusesColumns = EuiBasicTableColumn; - +type FormatUrl = (path: string) => string; interface GetColumns { dispatch: React.Dispatch; dispatchToaster: Dispatch; + formatUrl: FormatUrl; history: H.History; hasMlPermissions: boolean; hasNoPermissions: boolean; @@ -102,6 +103,7 @@ interface GetColumns { export const getColumns = ({ dispatch, dispatchToaster, + formatUrl, history, hasMlPermissions, hasNoPermissions, @@ -113,9 +115,16 @@ export const getColumns = ({ field: 'name', name: i18n.COLUMN_RULE, render: (value: Rule['name'], item: Rule) => ( - + void }) => { + ev.preventDefault(); + history.push(getRuleDetailsUrl(item.id)); + }} + href={formatUrl(getRuleDetailsUrl(item.id))} + > {value} - + ), truncateText: true, width: '24%', @@ -222,16 +231,26 @@ export const getColumns = ({ return hasNoPermissions ? cols : [...cols, ...actions]; }; -export const getMonitoringColumns = (): RulesStatusesColumns[] => { +export const getMonitoringColumns = ( + history: H.History, + formatUrl: FormatUrl +): RulesStatusesColumns[] => { const cols: RulesStatusesColumns[] = [ { field: 'name', name: i18n.COLUMN_RULE, render: (value: RuleStatus['current_status']['status'], item: RuleStatusRowItemType) => { return ( - + void }) => { + ev.preventDefault(); + history.push(getRuleDetailsUrl(item.id)); + }} + href={formatUrl(getRuleDetailsUrl(item.id))} + > {value} - + ); }, truncateText: true, diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/all/index.tsx b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/all/index.tsx index 9e61ec0eb3bcf..142da4c0f87ac 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/all/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/all/index.tsx @@ -49,6 +49,8 @@ import { allRulesReducer, State } from './reducer'; import { RulesTableFilters } from './rules_table_filters/rules_table_filters'; import { useMlCapabilities } from '../../../../../common/components/ml_popover/hooks/use_ml_capabilities'; import { hasMlAdminPermissions } from '../../../../../../common/machine_learning/has_ml_admin_permissions'; +import { SecurityPageName } from '../../../../../app/types'; +import { useFormatUrl } from '../../../../../common/components/link_to'; const SORT_FIELD = 'enabled'; const initialState: State = { @@ -140,6 +142,7 @@ export const AllRules = React.memo( const [, dispatchToaster] = useStateToaster(); const mlCapabilities = useMlCapabilities(); const [allRulesTab, setAllRulesTab] = useState(AllRulesTabs.rules); + const { formatUrl } = useFormatUrl(SecurityPageName.alerts); // TODO: Refactor license check + hasMlAdminPermissions to common check const hasMlPermissions = @@ -225,6 +228,7 @@ export const AllRules = React.memo( return getColumns({ dispatch, dispatchToaster, + formatUrl, history, hasMlPermissions, hasNoPermissions, @@ -238,6 +242,7 @@ export const AllRules = React.memo( }, [ dispatch, dispatchToaster, + formatUrl, hasMlPermissions, history, loadingRuleIds, @@ -245,7 +250,10 @@ export const AllRules = React.memo( reFetchRulesData, ]); - const monitoringColumns = useMemo(() => getMonitoringColumns(), []); + const monitoringColumns = useMemo(() => getMonitoringColumns(history, formatUrl), [ + history, + formatUrl, + ]); useEffect(() => { if (reFetchRulesData != null) { diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/create/index.tsx b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/create/index.tsx index dd290ec1ae582..57c2223f455b2 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/create/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/create/index.tsx @@ -6,12 +6,15 @@ import { EuiButtonEmpty, EuiAccordion, EuiHorizontalRule, EuiPanel, EuiSpacer } from '@elastic/eui'; import React, { useCallback, useRef, useState, useMemo } from 'react'; -import { Redirect } from 'react-router-dom'; +import { useHistory } from 'react-router-dom'; import styled, { StyledComponent } from 'styled-components'; import { usePersistRule } from '../../../../../alerts/containers/detection_engine/rules'; -import { DETECTION_ENGINE_PAGE_NAME } from '../../../../../common/components/link_to/redirect_to_detection_engine'; +import { + getRulesUrl, + getDetectionEngineUrl, +} from '../../../../../common/components/link_to/redirect_to_detection_engine'; import { WrapperPage } from '../../../../../common/components/wrapper_page'; import { displaySuccessToast, useStateToaster } from '../../../../../common/components/toasters'; import { SpyRoute } from '../../../../../common/utils/route/spy_routes'; @@ -35,6 +38,7 @@ import { } from '../types'; import { formatRule } from './helpers'; import * as i18n from './translations'; +import { SecurityPageName } from '../../../../../app/types'; const stepsRuleOrder = [ RuleStep.defineRule, @@ -116,6 +120,7 @@ const CreateRulePageComponent: React.FC = () => { getActionMessageParams((stepsData.current['define-rule'].data as DefineStepRule).ruleType), [stepsData.current['define-rule'].data] ); + const history = useHistory(); const setStepData = useCallback( (step: RuleStep, data: unknown, isValid: boolean) => { @@ -266,20 +271,27 @@ const CreateRulePageComponent: React.FC = () => { if (isSaved) { const ruleName = (stepsData.current[RuleStep.aboutRule].data as AboutStepRule).name; displaySuccessToast(i18n.SUCCESSFULLY_CREATED_RULES(ruleName), dispatchToaster); - return ; + history.replace(getRulesUrl()); + return null; } if (redirectToDetections(isSignalIndexExists, isAuthenticated, hasEncryptionKey)) { - return ; + history.replace(getDetectionEngineUrl()); + return null; } else if (userHasNoPermissions(canUserCRUD)) { - return ; + history.replace(getRulesUrl()); + return null; } return ( <> { - + ); }; diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/details/index.tsx b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/details/index.tsx index ebd6ed118f937..af5bed38955d3 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/details/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/details/index.tsx @@ -7,7 +7,6 @@ /* eslint-disable react-hooks/rules-of-hooks */ import { - EuiButton, EuiLoadingSpinner, EuiFlexGroup, EuiFlexItem, @@ -18,7 +17,7 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { FC, memo, useCallback, useMemo, useState } from 'react'; -import { Redirect, useParams } from 'react-router-dom'; +import { useParams, useHistory } from 'react-router-dom'; import { StickyContainer } from 'react-sticky'; import { connect, ConnectedProps } from 'react-redux'; @@ -28,7 +27,7 @@ import { FormattedDate } from '../../../../../common/components/formatted_date'; import { getEditRuleUrl, getRulesUrl, - DETECTION_ENGINE_PAGE_NAME, + getDetectionEngineUrl, } from '../../../../../common/components/link_to/redirect_to_detection_engine'; import { SiemSearchBar } from '../../../../../common/components/search_bar'; import { WrapperPage } from '../../../../../common/components/wrapper_page'; @@ -70,6 +69,9 @@ import { FailureHistory } from './failure_history'; import { RuleStatus } from '../../../../components/rules//rule_status'; import { useMlCapabilities } from '../../../../../common/components/ml_popover/hooks/use_ml_capabilities'; import { hasMlAdminPermissions } from '../../../../../../common/machine_learning/has_ml_admin_permissions'; +import { SecurityPageName } from '../../../../../app/types'; +import { LinkButton } from '../../../../../common/components/links'; +import { useFormatUrl } from '../../../../../common/components/link_to'; enum RuleDetailTabs { alerts = 'alerts', @@ -119,6 +121,8 @@ export const RuleDetailsPageComponent: FC = ({ }; const [lastAlerts] = useAlertInfo({ ruleId }); const mlCapabilities = useMlCapabilities(); + const history = useHistory(); + const { formatUrl } = useFormatUrl(SecurityPageName.alerts); // TODO: Refactor license check + hasMlAdminPermissions to common check const hasMlPermissions = @@ -230,8 +234,17 @@ export const RuleDetailsPageComponent: FC = ({ [ruleEnabled, setRuleEnabled] ); + const goToEditRule = useCallback( + (ev) => { + ev.preventDefault(); + history.push(getEditRuleUrl(ruleId ?? '')); + }, + [history, ruleId] + ); + if (redirectToDetections(isSignalIndexExists, isAuthenticated, hasEncryptionKey)) { - return ; + history.replace(getDetectionEngineUrl()); + return null; } return ( @@ -253,6 +266,7 @@ export const RuleDetailsPageComponent: FC = ({ backOptions={{ href: getRulesUrl(), text: i18n.BACK_TO_RULES, + pageId: SecurityPageName.alerts, }} border subtitle={subTitle} @@ -296,13 +310,14 @@ export const RuleDetailsPageComponent: FC = ({ - {ruleI18n.EDIT_RULE_SETTINGS} - + = ({ }} - + ); }; diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/edit/index.tsx b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/edit/index.tsx index 8fc646c01b17c..733fbbfe900a4 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/edit/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/edit/index.tsx @@ -17,11 +17,14 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { FC, memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { Redirect, useParams } from 'react-router-dom'; +import { useParams, useHistory } from 'react-router-dom'; import { useRule, usePersistRule } from '../../../../../alerts/containers/detection_engine/rules'; import { WrapperPage } from '../../../../../common/components/wrapper_page'; -import { DETECTION_ENGINE_PAGE_NAME } from '../../../../../common/components/link_to/redirect_to_detection_engine'; +import { + getRuleDetailsUrl, + getDetectionEngineUrl, +} from '../../../../../common/components/link_to/redirect_to_detection_engine'; import { displaySuccessToast, useStateToaster } from '../../../../../common/components/toasters'; import { SpyRoute } from '../../../../../common/utils/route/spy_routes'; import { useUserInfo } from '../../../../components/user_info'; @@ -48,6 +51,7 @@ import { ActionsStepRule, } from '../types'; import * as i18n from './translations'; +import { SecurityPageName } from '../../../../../app/types'; interface StepRuleForm { isValid: boolean; @@ -67,6 +71,7 @@ interface ActionsStepRuleForm extends StepRuleForm { } const EditRulePageComponent: FC = () => { + const history = useHistory(); const [, dispatchToaster] = useStateToaster(); const { loading: initLoading, @@ -323,6 +328,14 @@ const EditRulePageComponent: FC = () => { [selectedTab, stepsForm.current] ); + const goToDetailsRule = useCallback( + (ev) => { + ev.preventDefault(); + history.replace(getRuleDetailsUrl(ruleId ?? '')); + }, + [history, ruleId] + ); + useEffect(() => { if (rule != null) { const { aboutRuleData, defineRuleData, scheduleRuleData, ruleActionsData } = getStepsData({ @@ -342,13 +355,16 @@ const EditRulePageComponent: FC = () => { if (isSaved) { displaySuccessToast(i18n.SUCCESSFULLY_SAVED_RULE(rule?.name ?? ''), dispatchToaster); - return ; + history.replace(getRuleDetailsUrl(ruleId ?? '')); + return null; } if (redirectToDetections(isSignalIndexExists, isAuthenticated, hasEncryptionKey)) { - return ; + history.replace(getDetectionEngineUrl()); + return null; } else if (userHasNoPermissions(canUserCRUD)) { - return ; + history.replace(getRuleDetailsUrl(ruleId ?? '')); + return null; } return ( @@ -356,8 +372,9 @@ const EditRulePageComponent: FC = () => { { responsive={false} > - + {i18n.CANCEL} @@ -423,7 +440,7 @@ const EditRulePageComponent: FC = () => { - + ); }; diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/index.tsx b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/index.tsx index 4d36a24781727..7684f710952e6 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/index.tsx @@ -6,14 +6,13 @@ import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import React, { useCallback, useRef, useState } from 'react'; -import { Redirect } from 'react-router-dom'; +import { useHistory } from 'react-router-dom'; import { usePrePackagedRules, importRules, } from '../../../../alerts/containers/detection_engine/rules'; import { - DETECTION_ENGINE_PAGE_NAME, getDetectionEngineUrl, getCreateRuleUrl, } from '../../../../common/components/link_to/redirect_to_detection_engine'; @@ -28,10 +27,14 @@ import { ReadOnlyCallOut } from '../../../components/rules/read_only_callout'; import { UpdatePrePackagedRulesCallOut } from '../../../components/rules/pre_packaged_rules/update_callout'; import { getPrePackagedRuleStatus, redirectToDetections, userHasNoPermissions } from './helpers'; import * as i18n from './translations'; +import { SecurityPageName } from '../../../../app/types'; +import { LinkButton } from '../../../../common/components/links'; +import { useFormatUrl } from '../../../../common/components/link_to'; type Func = (refreshPrePackagedRule?: boolean) => void; const RulesPageComponent: React.FC = () => { + const history = useHistory(); const [showImportModal, setShowImportModal] = useState(false); const refreshRulesData = useRef(null); const { @@ -63,6 +66,7 @@ const RulesPageComponent: React.FC = () => { rulesNotInstalled, rulesNotUpdated ); + const { formatUrl } = useFormatUrl(SecurityPageName.alerts); const handleRefreshRules = useCallback(async () => { if (refreshRulesData.current != null) { @@ -87,8 +91,17 @@ const RulesPageComponent: React.FC = () => { refreshRulesData.current = refreshRule; }, []); + const goToNewRule = useCallback( + (ev) => { + ev.preventDefault(); + history.push(getCreateRuleUrl()); + }, + [history] + ); + if (redirectToDetections(isSignalIndexExists, isAuthenticated, hasEncryptionKey)) { - return ; + history.replace(getDetectionEngineUrl()); + return null; } return ( @@ -114,6 +127,7 @@ const RulesPageComponent: React.FC = () => { backOptions={{ href: getDetectionEngineUrl(), text: i18n.BACK_TO_ALERTS, + pageId: SecurityPageName.alerts, }} title={i18n.PAGE_TITLE} > @@ -155,15 +169,16 @@ const RulesPageComponent: React.FC = () => { - {i18n.ADD_NEW_RULE} - + @@ -188,7 +203,7 @@ const RulesPageComponent: React.FC = () => { /> - + ); }; diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/utils.ts b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/utils.ts index e5cdbd7123ff4..5a87f4e0b532e 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/utils.ts +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/utils.ts @@ -21,7 +21,7 @@ import * as i18nRules from './translations'; import { RouteSpyState } from '../../../../common/utils/route/types'; const getTabBreadcrumb = (pathname: string, search: string[]) => { - const tabPath = pathname.split('/')[2]; + const tabPath = pathname.split('/')[1]; if (tabPath === 'alerts') { return { diff --git a/x-pack/plugins/security_solution/public/alerts/routes.tsx b/x-pack/plugins/security_solution/public/alerts/routes.tsx index 897ba3269546f..8f542d1f88670 100644 --- a/x-pack/plugins/security_solution/public/alerts/routes.tsx +++ b/x-pack/plugins/security_solution/public/alerts/routes.tsx @@ -5,16 +5,19 @@ */ import React from 'react'; -import { Route } from 'react-router-dom'; +import { Route, Switch } from 'react-router-dom'; import { DetectionEngineContainer } from './pages/detection_engine'; -import { SiemPageName } from '../app/types'; +import { NotFoundPage } from '../app/404'; -export const getAlertsRoutes = () => [ - ( - - )} - />, -]; +export const AlertsRoutes: React.FC = () => ( + + ( + + )} + /> + } /> + +); From 0d2ded1ff0c2c44aa4b41efa247282cdf6d5f4e2 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Wed, 10 Jun 2020 22:16:35 -0400 Subject: [PATCH 04/43] modify cases routes --- .../cases/components/all_cases/index.tsx | 35 ++++++++++++------- .../cases/components/case_view/actions.tsx | 7 ++-- .../cases/components/case_view/index.tsx | 6 ++-- .../components/configure_cases/button.tsx | 26 ++++++++++---- .../public/cases/components/create/index.tsx | 11 +++--- .../components/use_push_to_service/index.tsx | 29 ++++++++++----- .../public/cases/pages/case.tsx | 3 +- .../public/cases/pages/case_details.tsx | 9 +++-- .../public/cases/pages/configure_cases.tsx | 10 ++++-- .../public/cases/pages/create_case.tsx | 10 ++++-- .../public/cases/pages/index.tsx | 17 +++++---- .../security_solution/public/cases/routes.tsx | 3 +- 12 files changed, 110 insertions(+), 56 deletions(-) diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx index 12f0d02eb10f6..47e92eca6c6fe 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx @@ -5,9 +5,9 @@ */ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useHistory } from 'react-router-dom'; import { EuiBasicTable, - EuiButton, EuiContextMenuPanel, EuiEmptyPrompt, EuiFlexGroup, @@ -27,7 +27,6 @@ import { useGetCases, UpdateCase } from '../../containers/use_get_cases'; import { useGetCasesStatus } from '../../containers/use_get_cases_status'; import { useDeleteCases } from '../../containers/use_delete_cases'; import { EuiBasicTableOnChange } from '../../../alerts/pages/detection_engine/rules/types'; -import { useGetUrlSearch } from '../../../common/components/navigation/use_get_url_search'; import { Panel } from '../../../common/components/panel'; import { UtilityBar, @@ -36,13 +35,11 @@ import { UtilityBarSection, UtilityBarText, } from '../../../common/components/utility_bar'; -import { getCreateCaseUrl } from '../../../common/components/link_to'; +import { getCreateCaseUrl, useFormatUrl } from '../../../common/components/link_to'; import { getBulkItems } from '../bulk_actions'; import { CaseHeaderPage } from '../case_header_page'; import { ConfirmDeleteCaseModal } from '../confirm_delete_case'; import { OpenClosedStats } from '../open_closed_stats'; -import { navTabs } from '../../../app/home/home_navigations'; - import { getActions } from './actions'; import { CasesTableFilters } from './table_filters'; import { useUpdateCases } from '../../containers/use_bulk_update_case'; @@ -51,6 +48,8 @@ import { getActionLicenseError } from '../use_push_to_service/helpers'; import { CaseCallOut } from '../callout'; import { ConfigureCaseButton } from '../configure_cases/button'; import { ERROR_PUSH_SERVICE_CALLOUT_TITLE } from '../use_push_to_service/translations'; +import { LinkButton } from '../../../common/components/links'; +import { SecurityPageName } from '../../../app/types'; const Div = styled.div` margin-top: ${({ theme }) => theme.eui.paddingSizes.m}; @@ -86,7 +85,9 @@ interface AllCasesProps { userCanCrud: boolean; } export const AllCases = React.memo(({ userCanCrud }) => { - const urlSearch = useGetUrlSearch(navTabs.case); + const history = useHistory(); + const { formatUrl, search: urlSearch } = useFormatUrl(SecurityPageName.case); + const { actionLicense } = useGetActionLicense(); const { countClosedCases, @@ -229,6 +230,14 @@ export const AllCases = React.memo(({ userCanCrud }) => { [dispatchUpdateCaseProperty, fetchCasesStatus] ); + const goToCreateCase = useCallback( + (ev) => { + ev.preventDefault(); + history.push(getCreateCaseUrl(urlSearch)); + }, + [history, urlSearch] + ); + const actions = useMemo( () => getActions({ @@ -338,15 +347,16 @@ export const AllCases = React.memo(({ userCanCrud }) => { /> - {i18n.CREATE_TITLE} - + @@ -411,15 +421,16 @@ export const AllCases = React.memo(({ userCanCrud }) => { titleSize="xs" body={i18n.NO_CASES_BODY} actions={ - {i18n.ADD_NEW_CASE} - + } /> } diff --git a/x-pack/plugins/security_solution/public/cases/components/case_view/actions.tsx b/x-pack/plugins/security_solution/public/cases/components/case_view/actions.tsx index c345f69960bcd..d06ad3c016c40 100644 --- a/x-pack/plugins/security_solution/public/cases/components/case_view/actions.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/case_view/actions.tsx @@ -6,11 +6,10 @@ import { isEmpty } from 'lodash/fp'; import React, { useMemo } from 'react'; -import { Redirect } from 'react-router-dom'; +import { useHistory } from 'react-router-dom'; import * as i18n from './translations'; import { useDeleteCases } from '../../containers/use_delete_cases'; import { ConfirmDeleteCaseModal } from '../confirm_delete_case'; -import { SecurityPageName } from '../../../app/types'; import { PropertyActions } from '../property_actions'; import { Case } from '../../containers/types'; import { CaseService } from '../../containers/use_get_case_user_actions'; @@ -26,6 +25,7 @@ const CaseViewActionsComponent: React.FC = ({ currentExternalIncident, disabled = false, }) => { + const history = useHistory(); // Delete case const { handleToggleModal, @@ -68,7 +68,8 @@ const CaseViewActionsComponent: React.FC = ({ ); if (isDeleted) { - return ; + history.push('/'); + return null; } return ( <> diff --git a/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx b/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx index 669fc89ffe804..3718249479b63 100644 --- a/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/case_view/index.tsx @@ -37,6 +37,7 @@ import { useGetCaseUserActions } from '../../containers/use_get_case_user_action import { usePushToService } from '../use_push_to_service'; import { EditConnector } from '../edit_connector'; import { useConnectors } from '../../containers/configure/use_connectors'; +import { SecurityPageName } from '../../../app/types'; interface Props { caseId: string; @@ -70,7 +71,7 @@ export interface CaseProps extends Props { export const CaseComponent = React.memo( ({ caseId, caseData, fetchCase, updateCase, userCanCrud }) => { const basePath = window.location.origin + useBasePath(); - const caseLink = `${basePath}/app/siem#/case/${caseId}`; + const caseLink = `${basePath}/app/security/cases/${caseId}`; const search = useGetUrlSearch(navTabs.case); const [initLoadingData, setInitLoadingData] = useState(true); const { @@ -252,6 +253,7 @@ export const CaseComponent = React.memo( href: getCaseUrl(search), text: i18n.BACK_TO_ALL, dataTestSubj: 'backToCases', + pageId: SecurityPageName.case, }), [search] ); @@ -356,7 +358,7 @@ export const CaseComponent = React.memo( - + ); } diff --git a/x-pack/plugins/security_solution/public/cases/components/configure_cases/button.tsx b/x-pack/plugins/security_solution/public/cases/components/configure_cases/button.tsx index a6d78d4a2a620..69c9e2c30f930 100644 --- a/x-pack/plugins/security_solution/public/cases/components/configure_cases/button.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/configure_cases/button.tsx @@ -4,9 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiButton, EuiToolTip } from '@elastic/eui'; -import React, { memo, useMemo } from 'react'; -import { getConfigureCasesUrl } from '../../../common/components/link_to'; +import { EuiToolTip } from '@elastic/eui'; +import React, { memo, useCallback, useMemo } from 'react'; +import { useHistory } from 'react-router-dom'; + +import { getConfigureCasesUrl, useFormatUrl } from '../../../common/components/link_to'; +import { LinkButton } from '../../../common/components/links'; +import { SecurityPageName } from '../../../app/types'; export interface ConfigureCaseButtonProps { label: string; @@ -25,17 +29,27 @@ const ConfigureCaseButtonComponent: React.FC = ({ titleTooltip, urlSearch, }: ConfigureCaseButtonProps) => { + const history = useHistory(); + const { formatUrl } = useFormatUrl(SecurityPageName.case); + const goToCaseConfigure = useCallback( + (ev) => { + ev.preventDefault(); + history.push(getConfigureCasesUrl(urlSearch)); + }, + [history, urlSearch] + ); const configureCaseButton = useMemo( () => ( - {label} - + ), [label, isDisabled, urlSearch] ); diff --git a/x-pack/plugins/security_solution/public/cases/components/create/index.tsx b/x-pack/plugins/security_solution/public/cases/components/create/index.tsx index 01511abf642d3..8abbc51c360c8 100644 --- a/x-pack/plugins/security_solution/public/cases/components/create/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/create/index.tsx @@ -13,7 +13,7 @@ import { EuiPanel, } from '@elastic/eui'; import styled, { css } from 'styled-components'; -import { Redirect } from 'react-router-dom'; +import { useHistory } from 'react-router-dom'; import { isEqual } from 'lodash/fp'; import { CasePostRequest } from '../../../../../case/common/api'; @@ -30,9 +30,9 @@ import { schema } from './schema'; import { InsertTimelinePopover } from '../../../timelines/components/timeline/insert_timeline_popover'; import { useInsertTimeline } from '../../../timelines/components/timeline/insert_timeline_popover/use_insert_timeline'; import * as i18n from '../../translations'; -import { SecurityPageName } from '../../../app/types'; import { MarkdownEditorForm } from '../../../common/components//markdown_editor/form'; import { useGetTags } from '../../containers/use_get_tags'; +import { getCaseDetailsUrl } from '../../../common/components/link_to'; export const CommonUseField = getUseField({ component: Field }); @@ -61,6 +61,7 @@ const initialCaseValue: CasePostRequest = { }; export const Create = React.memo(() => { + const history = useHistory(); const { caseData, isLoading, postCase } = usePostCase(); const [isCancel, setIsCancel] = useState(false); const { form } = useForm({ @@ -100,11 +101,13 @@ export const Create = React.memo(() => { }, []); if (caseData != null && caseData.id) { - return ; + history.push(getCaseDetailsUrl({ id: caseData.id })); + return null; } if (isCancel) { - return ; + history.push('/'); + return null; } return ( diff --git a/x-pack/plugins/security_solution/public/cases/components/use_push_to_service/index.tsx b/x-pack/plugins/security_solution/public/cases/components/use_push_to_service/index.tsx index 2e9e3cce4306a..8af05d99b11e2 100644 --- a/x-pack/plugins/security_solution/public/cases/components/use_push_to_service/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/use_push_to_service/index.tsx @@ -4,21 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiButton, EuiLink, EuiToolTip } from '@elastic/eui'; +import { EuiButton, EuiToolTip } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { useCallback, useMemo } from 'react'; +import { useHistory } from 'react-router-dom'; import { Case } from '../../containers/types'; import { useGetActionLicense } from '../../containers/use_get_action_license'; import { usePostPushToService } from '../../containers/use_post_push_to_service'; -import { getConfigureCasesUrl } from '../../../common/components/link_to'; -import { useGetUrlSearch } from '../../../common/components/navigation/use_get_url_search'; -import { navTabs } from '../../../app/home/home_navigations'; +import { getConfigureCasesUrl, useFormatUrl } from '../../../common/components/link_to'; import { CaseCallOut } from '../callout'; import { getLicenseError, getKibanaConfigError } from './helpers'; import * as i18n from './translations'; import { Connector } from '../../../../../case/common/api/cases'; import { CaseServices } from '../../containers/use_get_case_user_actions'; +import { LinkAnchor } from '../../../common/components/links'; +import { SecurityPageName } from '../../../app/types'; export interface UsePushToService { caseId: string; @@ -48,8 +49,8 @@ export const usePushToService = ({ userCanCrud, isValidConnector, }: UsePushToService): ReturnUsePushToService => { - const urlSearch = useGetUrlSearch(navTabs.case); - + const history = useHistory(); + const { formatUrl, search: urlSearch } = useFormatUrl(SecurityPageName.case); const { isLoading, postPushToService } = usePostPushToService(); const { isLoading: loadingLicense, actionLicense } = useGetActionLicense(); @@ -66,6 +67,14 @@ export const usePushToService = ({ } }, [caseId, caseServices, caseConnectorId, caseConnectorName, postPushToService, updateCase]); + const goToConfigureCases = useCallback( + (ev) => { + ev.preventDefault(); + history.push(getConfigureCasesUrl(urlSearch)); + }, + [history, urlSearch] + ); + const errorsMsg = useMemo(() => { let errors: Array<{ title: string; @@ -86,9 +95,13 @@ export const usePushToService = ({ id="xpack.securitySolution.case.caseView.pushToServiceDisableByNoConnectors" values={{ link: ( - + {i18n.LINK_CONNECTOR_CONFIGURE} - + ), }} /> diff --git a/x-pack/plugins/security_solution/public/cases/pages/case.tsx b/x-pack/plugins/security_solution/public/cases/pages/case.tsx index 03ebec34c2cdd..eb6da9579e4e8 100644 --- a/x-pack/plugins/security_solution/public/cases/pages/case.tsx +++ b/x-pack/plugins/security_solution/public/cases/pages/case.tsx @@ -13,6 +13,7 @@ import { AllCases } from '../components/all_cases'; import { savedObjectReadOnly, CaseCallOut } from '../components/callout'; import { CaseSavedObjectNoPermissions } from './saved_object_no_permissions'; +import { SecurityPageName } from '../../app/types'; export const CasesPage = React.memo(() => { const userPermissions = useGetUserSavedObjectPermissions(); @@ -28,7 +29,7 @@ export const CasesPage = React.memo(() => { )} - + ) : ( diff --git a/x-pack/plugins/security_solution/public/cases/pages/case_details.tsx b/x-pack/plugins/security_solution/public/cases/pages/case_details.tsx index 5dfe12179b990..90d6f66741a43 100644 --- a/x-pack/plugins/security_solution/public/cases/pages/case_details.tsx +++ b/x-pack/plugins/security_solution/public/cases/pages/case_details.tsx @@ -5,8 +5,9 @@ */ import React from 'react'; -import { useParams, Redirect } from 'react-router-dom'; +import { useParams, useHistory } from 'react-router-dom'; +import { SecurityPageName } from '../../app/types'; import { WrapperPage } from '../../common/components/wrapper_page'; import { useGetUrlSearch } from '../../common/components/navigation/use_get_url_search'; import { useGetUserSavedObjectPermissions } from '../../common/lib/kibana'; @@ -17,12 +18,14 @@ import { CaseView } from '../components/case_view'; import { savedObjectReadOnly, CaseCallOut } from '../components/callout'; export const CaseDetailsPage = React.memo(() => { + const history = useHistory(); const userPermissions = useGetUserSavedObjectPermissions(); const { detailName: caseId } = useParams(); const search = useGetUrlSearch(navTabs.case); if (userPermissions != null && !userPermissions.read) { - return ; + history.replace(getCaseUrl(search)); + return null; } return caseId != null ? ( @@ -36,7 +39,7 @@ export const CaseDetailsPage = React.memo(() => { )} - + ) : null; }); diff --git a/x-pack/plugins/security_solution/public/cases/pages/configure_cases.tsx b/x-pack/plugins/security_solution/public/cases/pages/configure_cases.tsx index f70ff859e8e7d..83354dabca1f2 100644 --- a/x-pack/plugins/security_solution/public/cases/pages/configure_cases.tsx +++ b/x-pack/plugins/security_solution/public/cases/pages/configure_cases.tsx @@ -5,9 +5,10 @@ */ import React, { useMemo } from 'react'; -import { Redirect } from 'react-router-dom'; +import { useHistory } from 'react-router-dom'; import styled from 'styled-components'; +import { SecurityPageName } from '../../app/types'; import { getCaseUrl } from '../../common/components/link_to'; import { useGetUrlSearch } from '../../common/components/navigation/use_get_url_search'; import { WrapperPage } from '../../common/components/wrapper_page'; @@ -20,6 +21,7 @@ import { WhitePageWrapper, SectionWrapper } from '../components/wrappers'; import * as i18n from './translations'; const ConfigureCasesPageComponent: React.FC = () => { + const history = useHistory(); const userPermissions = useGetUserSavedObjectPermissions(); const search = useGetUrlSearch(navTabs.case); @@ -27,12 +29,14 @@ const ConfigureCasesPageComponent: React.FC = () => { () => ({ href: getCaseUrl(search), text: i18n.BACK_TO_ALL, + pageId: SecurityPageName.case, }), [search] ); if (userPermissions != null && !userPermissions.read) { - return ; + history.push(getCaseUrl(search)); + return null; } const HeaderWrapper = styled.div` @@ -51,7 +55,7 @@ const ConfigureCasesPageComponent: React.FC = () => { - + ); }; diff --git a/x-pack/plugins/security_solution/public/cases/pages/create_case.tsx b/x-pack/plugins/security_solution/public/cases/pages/create_case.tsx index c586a90e5ef9c..672f44bfe275f 100644 --- a/x-pack/plugins/security_solution/public/cases/pages/create_case.tsx +++ b/x-pack/plugins/security_solution/public/cases/pages/create_case.tsx @@ -5,8 +5,9 @@ */ import React, { useMemo } from 'react'; -import { Redirect } from 'react-router-dom'; +import { useHistory } from 'react-router-dom'; +import { SecurityPageName } from '../../app/types'; import { getCaseUrl } from '../../common/components/link_to'; import { useGetUrlSearch } from '../../common/components/navigation/use_get_url_search'; import { WrapperPage } from '../../common/components/wrapper_page'; @@ -18,6 +19,7 @@ import { Create } from '../components/create'; import * as i18n from './translations'; export const CreateCasePage = React.memo(() => { + const history = useHistory(); const userPermissions = useGetUserSavedObjectPermissions(); const search = useGetUrlSearch(navTabs.case); @@ -25,12 +27,14 @@ export const CreateCasePage = React.memo(() => { () => ({ href: getCaseUrl(search), text: i18n.BACK_TO_ALL, + pageId: SecurityPageName.case, }), [search] ); if (userPermissions != null && !userPermissions.crud) { - return ; + history.replace(getCaseUrl(search)); + return null; } return ( @@ -39,7 +43,7 @@ export const CreateCasePage = React.memo(() => { - + ); }); diff --git a/x-pack/plugins/security_solution/public/cases/pages/index.tsx b/x-pack/plugins/security_solution/public/cases/pages/index.tsx index a5ef4a0e7a110..814be240644fc 100644 --- a/x-pack/plugins/security_solution/public/cases/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/pages/index.tsx @@ -7,13 +7,12 @@ import React from 'react'; import { Route, Switch } from 'react-router-dom'; -import { SecurityPageName } from '../../app/types'; import { CaseDetailsPage } from './case_details'; import { CasesPage } from './case'; import { CreateCasePage } from './create_case'; import { ConfigureCasesPage } from './configure_cases'; -const casesPagePath = `/:pageName(${SecurityPageName.case})`; +const casesPagePath = ''; const caseDetailsPagePath = `${casesPagePath}/:detailName`; const caseDetailsPagePathWithCommentId = `${casesPagePath}/:detailName/:commentId`; const createCasePagePath = `${casesPagePath}/create`; @@ -21,21 +20,21 @@ const configureCasesPagePath = `${casesPagePath}/configure`; const CaseContainerComponent: React.FC = () => ( - - - - + - + - + - + + + + ); diff --git a/x-pack/plugins/security_solution/public/cases/routes.tsx b/x-pack/plugins/security_solution/public/cases/routes.tsx index 8d0533b4826e7..11b3fdd18f245 100644 --- a/x-pack/plugins/security_solution/public/cases/routes.tsx +++ b/x-pack/plugins/security_solution/public/cases/routes.tsx @@ -8,11 +8,10 @@ import React from 'react'; import { Route, Switch } from 'react-router-dom'; import { Case } from './pages'; -import { SecurityPageName } from '../app/types'; export const CasesRoutes: React.FC = () => ( - + From 85694c6388e8ec0b6d606a03c9257c2e41180e0b Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Wed, 10 Jun 2020 22:17:11 -0400 Subject: [PATCH 05/43] modify hosts routes --- .../public/hosts/pages/details/index.tsx | 3 +- .../public/hosts/pages/details/nav_tabs.tsx | 3 +- .../public/hosts/pages/details/types.ts | 2 - .../public/hosts/pages/hosts.tsx | 5 +- .../public/hosts/pages/hosts_tabs.tsx | 12 +- .../public/hosts/pages/index.tsx | 127 +++++++++--------- .../public/hosts/pages/nav_tabs.tsx | 8 +- .../public/hosts/pages/types.ts | 5 +- .../security_solution/public/hosts/routes.tsx | 6 +- 9 files changed, 88 insertions(+), 83 deletions(-) diff --git a/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx b/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx index a5fabf4d515f8..e3f00a377d272 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/details/index.tsx @@ -9,6 +9,7 @@ import React, { useEffect, useCallback, useMemo } from 'react'; import { connect, ConnectedProps } from 'react-redux'; import { StickyContainer } from 'react-sticky'; +import { SecurityPageName } from '../../../app/types'; import { UpdateDateRange } from '../../../common/components/charts/common'; import { FiltersGlobal } from '../../../common/components/filters_global'; import { HeaderPage } from '../../../common/components/header_page'; @@ -209,7 +210,7 @@ const HostDetailsComponent = React.memo( }} - + ); } diff --git a/x-pack/plugins/security_solution/public/hosts/pages/details/nav_tabs.tsx b/x-pack/plugins/security_solution/public/hosts/pages/details/nav_tabs.tsx index b1bee56535a06..c3438aca93b60 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/details/nav_tabs.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/details/nav_tabs.tsx @@ -8,10 +8,9 @@ import { omit } from 'lodash/fp'; import * as i18n from '../translations'; import { HostDetailsNavTab } from './types'; import { HostsTableType } from '../../store/model'; -import { SecurityPageName } from '../../../app/types'; const getTabsOnHostDetailsUrl = (hostName: string, tabName: HostsTableType) => - `#/${SecurityPageName.hosts}/${hostName}/${tabName}`; + `/${hostName}/${tabName}`; export const navTabsHostDetails = ( hostName: string, diff --git a/x-pack/plugins/security_solution/public/hosts/pages/details/types.ts b/x-pack/plugins/security_solution/public/hosts/pages/details/types.ts index f145abed2d8ff..aa6288d473c91 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/details/types.ts +++ b/x-pack/plugins/security_solution/public/hosts/pages/details/types.ts @@ -7,7 +7,6 @@ import { ActionCreator } from 'typescript-fsa'; import { Query, IIndexPattern, Filter } from 'src/plugins/data/public'; import { InputsModelId } from '../../../common/store/inputs/constants'; -import { HostComponentProps } from '../../../common/components/link_to/redirect_to_hosts'; import { HostsTableType } from '../../store/model'; import { HostsQueryProps } from '../types'; import { NavTab } from '../../../common/components/navigation/types'; @@ -40,7 +39,6 @@ export interface HostDetailsProps extends HostsQueryProps { export type HostDetailsComponentProps = HostDetailsComponentReduxProps & HostDetailsComponentDispatchProps & - HostComponentProps & HostsQueryProps; type KeyHostDetailsNavTabWithoutMlPermission = HostsTableType.authentications & diff --git a/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx b/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx index f7583f65a4fcd..f6429544f855e 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx @@ -8,8 +8,9 @@ import { EuiSpacer } from '@elastic/eui'; import React, { useCallback } from 'react'; import { connect, ConnectedProps } from 'react-redux'; import { StickyContainer } from 'react-sticky'; - import { useParams } from 'react-router-dom'; + +import { SecurityPageName } from '../../app/types'; import { UpdateDateRange } from '../../common/components/charts/common'; import { FiltersGlobal } from '../../common/components/filters_global'; import { HeaderPage } from '../../common/components/header_page'; @@ -158,7 +159,7 @@ export const HostsComponent = React.memo( }} - + ); } diff --git a/x-pack/plugins/security_solution/public/hosts/pages/hosts_tabs.tsx b/x-pack/plugins/security_solution/public/hosts/pages/hosts_tabs.tsx index 549c198a43526..e2a0ba0f1fe6c 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/hosts_tabs.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/hosts_tabs.tsx @@ -70,22 +70,22 @@ export const HostsTabs = memo( return ( - + - + - + - + - + - + diff --git a/x-pack/plugins/security_solution/public/hosts/pages/index.tsx b/x-pack/plugins/security_solution/public/hosts/pages/index.tsx index 38796d280df3e..f205a0c578ba6 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/index.tsx @@ -5,18 +5,17 @@ */ import React from 'react'; -import { Redirect, Route, Switch, RouteComponentProps } from 'react-router-dom'; +import { Route, Switch, RouteComponentProps, useHistory } from 'react-router-dom'; import { HostDetails } from './details'; import { HostsTableType } from '../store/model'; import { GlobalTime } from '../../common/containers/global_time'; -import { SecurityPageName } from '../../app/types'; import { Hosts } from './hosts'; import { hostsPagePath, hostDetailsPagePath } from './types'; -const getHostsTabPath = (pagePath: string) => - `${pagePath}/:tabName(` + +const getHostsTabPath = () => + `/:tabName(` + `${HostsTableType.hosts}|` + `${HostsTableType.authentications}|` + `${HostsTableType.uncommonProcesses}|` + @@ -34,62 +33,68 @@ const getHostDetailsTabPath = (pagePath: string) => type Props = Partial> & { url: string }; -export const HostsContainer = React.memo(({ url }) => ( - - {({ to, from, setQuery, deleteQuery, isInitializing }) => ( - - ( - - )} - /> - ( - - )} - /> - } - /> - ( - - )} - /> - - )} - -)); +export const HostsContainer = React.memo(({ url }) => { + const history = useHistory(); + return ( + + {({ to, from, setQuery, deleteQuery, isInitializing }) => ( + + ( + + )} + /> + ( + + )} + /> + { + history.replace(`${detailName}/${HostsTableType.authentications}${search}`); + return null; + }} + /> + { + history.replace(`${HostsTableType.hosts}${search}`); + return null; + }} + /> + + )} + + ); +}); HostsContainer.displayName = 'HostsContainer'; diff --git a/x-pack/plugins/security_solution/public/hosts/pages/nav_tabs.tsx b/x-pack/plugins/security_solution/public/hosts/pages/nav_tabs.tsx index dc26f588ed77a..dd1d3b1508925 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/nav_tabs.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/nav_tabs.tsx @@ -10,7 +10,7 @@ import { HostsTableType } from '../store/model'; import { HostsNavTab } from './navigation/types'; import { SecurityPageName } from '../../app/types'; -const getTabsOnHostsUrl = (tabName: HostsTableType) => `#/${SecurityPageName.hosts}/${tabName}`; +const getTabsOnHostsUrl = (tabName: HostsTableType) => `/${tabName}`; export const navTabsHosts = (hasMlUserPermissions: boolean): HostsNavTab => { const hostsNavTabs = { @@ -20,6 +20,7 @@ export const navTabsHosts = (hasMlUserPermissions: boolean): HostsNavTab => { href: getTabsOnHostsUrl(HostsTableType.hosts), disabled: false, urlKey: 'host', + pageId: SecurityPageName.hosts, }, [HostsTableType.authentications]: { id: HostsTableType.authentications, @@ -27,6 +28,7 @@ export const navTabsHosts = (hasMlUserPermissions: boolean): HostsNavTab => { href: getTabsOnHostsUrl(HostsTableType.authentications), disabled: false, urlKey: 'host', + pageId: SecurityPageName.hosts, }, [HostsTableType.uncommonProcesses]: { id: HostsTableType.uncommonProcesses, @@ -34,6 +36,7 @@ export const navTabsHosts = (hasMlUserPermissions: boolean): HostsNavTab => { href: getTabsOnHostsUrl(HostsTableType.uncommonProcesses), disabled: false, urlKey: 'host', + pageId: SecurityPageName.hosts, }, [HostsTableType.anomalies]: { id: HostsTableType.anomalies, @@ -41,6 +44,7 @@ export const navTabsHosts = (hasMlUserPermissions: boolean): HostsNavTab => { href: getTabsOnHostsUrl(HostsTableType.anomalies), disabled: false, urlKey: 'host', + pageId: SecurityPageName.hosts, }, [HostsTableType.events]: { id: HostsTableType.events, @@ -48,6 +52,7 @@ export const navTabsHosts = (hasMlUserPermissions: boolean): HostsNavTab => { href: getTabsOnHostsUrl(HostsTableType.events), disabled: false, urlKey: 'host', + pageId: SecurityPageName.hosts, }, [HostsTableType.alerts]: { id: HostsTableType.alerts, @@ -55,6 +60,7 @@ export const navTabsHosts = (hasMlUserPermissions: boolean): HostsNavTab => { href: getTabsOnHostsUrl(HostsTableType.alerts), disabled: false, urlKey: 'host', + pageId: SecurityPageName.hosts, }, }; diff --git a/x-pack/plugins/security_solution/public/hosts/pages/types.ts b/x-pack/plugins/security_solution/public/hosts/pages/types.ts index 27d39a739bda1..ffd17b0ef46f6 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/types.ts +++ b/x-pack/plugins/security_solution/public/hosts/pages/types.ts @@ -7,13 +7,12 @@ import { IIndexPattern } from 'src/plugins/data/public'; import { ActionCreator } from 'typescript-fsa'; -import { SecurityPageName } from '../../app/types'; import { hostsModel } from '../store'; import { GlobalTimeArgs } from '../../common/containers/global_time'; import { InputsModelId } from '../../common/store/inputs/constants'; -export const hostsPagePath = `/:pageName(${SecurityPageName.hosts})`; -export const hostDetailsPagePath = `${hostsPagePath}/:detailName`; +export const hostsPagePath = '/'; +export const hostDetailsPagePath = `/:detailName`; export type HostsTabsProps = HostsComponentProps & { filterQuery: string; diff --git a/x-pack/plugins/security_solution/public/hosts/routes.tsx b/x-pack/plugins/security_solution/public/hosts/routes.tsx index 0fcf1233bb4a2..323432e141061 100644 --- a/x-pack/plugins/security_solution/public/hosts/routes.tsx +++ b/x-pack/plugins/security_solution/public/hosts/routes.tsx @@ -8,13 +8,9 @@ import React from 'react'; import { Route, Switch } from 'react-router-dom'; import { HostsContainer } from './pages'; -import { SecurityPageName } from '../app/types'; export const HostsRoutes = () => ( - } - /> + } /> ); From 8b14aaa90dae4815da71f55bc61936576b69562f Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Wed, 10 Jun 2020 22:17:41 -0400 Subject: [PATCH 06/43] modify network routes --- .../public/network/pages/index.tsx | 28 +++++++++---------- .../public/network/pages/ip_details/index.tsx | 3 +- .../network/pages/navigation/nav_tabs.tsx | 9 +++++- .../pages/navigation/network_routes.tsx | 12 ++++---- .../public/network/pages/navigation/types.ts | 1 - .../public/network/pages/navigation/utils.ts | 5 ++-- .../public/network/pages/network.tsx | 3 +- .../public/network/routes.tsx | 3 +- 8 files changed, 35 insertions(+), 29 deletions(-) diff --git a/x-pack/plugins/security_solution/public/network/pages/index.tsx b/x-pack/plugins/security_solution/public/network/pages/index.tsx index 06d020d6e3b00..0184252b1f23c 100644 --- a/x-pack/plugins/security_solution/public/network/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/index.tsx @@ -5,7 +5,7 @@ */ import React, { useMemo } from 'react'; -import { Redirect, Route, Switch, RouteComponentProps } from 'react-router-dom'; +import { Route, Switch, RouteComponentProps, useHistory } from 'react-router-dom'; import { useMlCapabilities } from '../../common/components/ml_popover/hooks/use_ml_capabilities'; import { hasMlUserPermissions } from '../../../common/machine_learning/has_ml_user_permissions'; @@ -14,23 +14,23 @@ import { FlowTarget } from '../../graphql/types'; import { IPDetails } from './ip_details'; import { Network } from './network'; import { GlobalTime } from '../../common/containers/global_time'; -import { SecurityPageName } from '../../app/types'; import { getNetworkRoutePath } from './navigation'; import { NetworkRouteType } from './navigation/types'; type Props = Partial> & { url: string }; -const networkPagePath = `/:pageName(${SecurityPageName.network})`; -const ipDetailsPageBasePath = `${networkPagePath}/ip/:detailName`; +const networkPagePath = ''; +const ipDetailsPageBasePath = `/ip/:detailName`; const NetworkContainerComponent: React.FC = () => { + const history = useHistory(); const capabilities = useMlCapabilities(); const capabilitiesFetched = capabilities.capabilitiesFetched; const userHasMlUserPermissions = useMemo(() => hasMlUserPermissions(capabilities), [ capabilities, ]); const networkRoutePath = useMemo( - () => getNetworkRoutePath(networkPagePath, capabilitiesFetched, userHasMlUserPermissions), + () => getNetworkRoutePath(capabilitiesFetched, userHasMlUserPermissions), [capabilitiesFetched, userHasMlUserPermissions] ); @@ -79,17 +79,17 @@ const NetworkContainerComponent: React.FC = () => { match: { params: { detailName }, }, - }) => ( - - )} + }) => { + history.replace(`ip/${detailName}/${FlowTarget.source}${search}`); + return null; + }} /> ( - - )} + path="/" + render={({ location: { search = '' } }) => { + history.replace(`${NetworkRouteType.flows}${search}`); + return null; + }} /> )} diff --git a/x-pack/plugins/security_solution/public/network/pages/ip_details/index.tsx b/x-pack/plugins/security_solution/public/network/pages/ip_details/index.tsx index 9ae09d6c6cec7..face3f8904794 100644 --- a/x-pack/plugins/security_solution/public/network/pages/ip_details/index.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/ip_details/index.tsx @@ -45,6 +45,7 @@ import { UsersQueryTable } from './users_query_table'; import { AnomaliesQueryTabBody } from '../../../common/containers/anomalies/anomalies_query_tab_body'; import { esQuery } from '../../../../../../../src/plugins/data/public'; import { networkModel } from '../../store'; +import { SecurityPageName } from '../../../app/types'; export { getBreadcrumbs } from './utils'; const IpOverviewManage = manageQuery(IpOverview); @@ -273,7 +274,7 @@ export const IPDetailsComponent: React.FC - + ); }; diff --git a/x-pack/plugins/security_solution/public/network/pages/navigation/nav_tabs.tsx b/x-pack/plugins/security_solution/public/network/pages/navigation/nav_tabs.tsx index 61f1a5aacb9c0..a563d44012ea2 100644 --- a/x-pack/plugins/security_solution/public/network/pages/navigation/nav_tabs.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/navigation/nav_tabs.tsx @@ -7,8 +7,9 @@ import { omit } from 'lodash/fp'; import * as i18n from '../translations'; import { NetworkNavTab, NetworkRouteType } from './types'; +import { SecurityPageName } from '../../../app/types'; -const getTabsOnNetworkUrl = (tabName: NetworkRouteType) => `#/network/${tabName}`; +const getTabsOnNetworkUrl = (tabName: NetworkRouteType) => `/${tabName}`; export const navTabsNetwork = (hasMlUserPermissions: boolean): NetworkNavTab => { const networkNavTabs = { @@ -18,6 +19,7 @@ export const navTabsNetwork = (hasMlUserPermissions: boolean): NetworkNavTab => href: getTabsOnNetworkUrl(NetworkRouteType.flows), disabled: false, urlKey: 'network', + pageId: SecurityPageName.network, }, [NetworkRouteType.dns]: { id: NetworkRouteType.dns, @@ -25,6 +27,7 @@ export const navTabsNetwork = (hasMlUserPermissions: boolean): NetworkNavTab => href: getTabsOnNetworkUrl(NetworkRouteType.dns), disabled: false, urlKey: 'network', + pageId: SecurityPageName.network, }, [NetworkRouteType.http]: { id: NetworkRouteType.http, @@ -32,6 +35,7 @@ export const navTabsNetwork = (hasMlUserPermissions: boolean): NetworkNavTab => href: getTabsOnNetworkUrl(NetworkRouteType.http), disabled: false, urlKey: 'network', + pageId: SecurityPageName.network, }, [NetworkRouteType.tls]: { id: NetworkRouteType.tls, @@ -39,6 +43,7 @@ export const navTabsNetwork = (hasMlUserPermissions: boolean): NetworkNavTab => href: getTabsOnNetworkUrl(NetworkRouteType.tls), disabled: false, urlKey: 'network', + pageId: SecurityPageName.network, }, [NetworkRouteType.anomalies]: { id: NetworkRouteType.anomalies, @@ -46,6 +51,7 @@ export const navTabsNetwork = (hasMlUserPermissions: boolean): NetworkNavTab => href: getTabsOnNetworkUrl(NetworkRouteType.anomalies), disabled: false, urlKey: 'network', + pageId: SecurityPageName.network, }, [NetworkRouteType.alerts]: { id: NetworkRouteType.alerts, @@ -53,6 +59,7 @@ export const navTabsNetwork = (hasMlUserPermissions: boolean): NetworkNavTab => href: getTabsOnNetworkUrl(NetworkRouteType.alerts), disabled: false, urlKey: 'network', + pageId: SecurityPageName.network, }, }; diff --git a/x-pack/plugins/security_solution/public/network/pages/navigation/network_routes.tsx b/x-pack/plugins/security_solution/public/network/pages/navigation/network_routes.tsx index 08ed0d9769be8..e1e84b3ef4271 100644 --- a/x-pack/plugins/security_solution/public/network/pages/navigation/network_routes.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/navigation/network_routes.tsx @@ -100,10 +100,10 @@ export const NetworkRoutes = React.memo( return ( - + - + <> @@ -129,19 +129,19 @@ export const NetworkRoutes = React.memo( - + - + - + - + diff --git a/x-pack/plugins/security_solution/public/network/pages/navigation/types.ts b/x-pack/plugins/security_solution/public/network/pages/navigation/types.ts index 0f48aad57b3a8..5fd8cc172d83e 100644 --- a/x-pack/plugins/security_solution/public/network/pages/navigation/types.ts +++ b/x-pack/plugins/security_solution/public/network/pages/navigation/types.ts @@ -71,7 +71,6 @@ export enum NetworkRouteType { } export type GetNetworkRoutePath = ( - pagePath: string, capabilitiesFetched: boolean, hasMlUserPermission: boolean ) => string; diff --git a/x-pack/plugins/security_solution/public/network/pages/navigation/utils.ts b/x-pack/plugins/security_solution/public/network/pages/navigation/utils.ts index 24c2011fd3800..25a91db4eef9b 100644 --- a/x-pack/plugins/security_solution/public/network/pages/navigation/utils.ts +++ b/x-pack/plugins/security_solution/public/network/pages/navigation/utils.ts @@ -7,16 +7,15 @@ import { GetNetworkRoutePath, NetworkRouteType } from './types'; export const getNetworkRoutePath: GetNetworkRoutePath = ( - pagePath, capabilitiesFetched, hasMlUserPermission ) => { if (capabilitiesFetched && !hasMlUserPermission) { - return `${pagePath}/:tabName(${NetworkRouteType.flows}|${NetworkRouteType.dns}|${NetworkRouteType.http}|${NetworkRouteType.tls}|${NetworkRouteType.alerts})`; + return `/:tabName(${NetworkRouteType.flows}|${NetworkRouteType.dns}|${NetworkRouteType.http}|${NetworkRouteType.tls}|${NetworkRouteType.alerts})`; } return ( - `${pagePath}/:tabName(` + + `/:tabName(` + `${NetworkRouteType.flows}|` + `${NetworkRouteType.dns}|` + `${NetworkRouteType.anomalies}|` + diff --git a/x-pack/plugins/security_solution/public/network/pages/network.tsx b/x-pack/plugins/security_solution/public/network/pages/network.tsx index 2f7a97ed3d19e..845a6bbd95dd6 100644 --- a/x-pack/plugins/security_solution/public/network/pages/network.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/network.tsx @@ -11,6 +11,7 @@ import { useParams } from 'react-router-dom'; import { StickyContainer } from 'react-sticky'; import { esQuery } from '../../../../../../src/plugins/data/public'; +import { SecurityPageName } from '../../app/types'; import { UpdateDateRange } from '../../common/components/charts/common'; import { EmbeddedMap } from '../components/embeddables/embedded_map'; import { FiltersGlobal } from '../../common/components/filters_global'; @@ -175,7 +176,7 @@ const NetworkComponent = React.memo( }} - + ); } diff --git a/x-pack/plugins/security_solution/public/network/routes.tsx b/x-pack/plugins/security_solution/public/network/routes.tsx index 30d7327ffcc97..4eca6710d04a4 100644 --- a/x-pack/plugins/security_solution/public/network/routes.tsx +++ b/x-pack/plugins/security_solution/public/network/routes.tsx @@ -8,12 +8,11 @@ import React from 'react'; import { Route, Switch } from 'react-router-dom'; import { NetworkContainer } from './pages'; -import { SecurityPageName } from '../app/types'; export const NetworkRoutes = () => ( } /> From e22ffc13db6ce19162938499e71643c01ae33fb1 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Wed, 10 Jun 2020 22:18:21 -0400 Subject: [PATCH 07/43] modify overview routes --- .../components/alerts_by_category/index.tsx | 32 +++++++++++------ .../components/events_by_dataset/index.tsx | 32 ++++++++++++----- .../components/overview_host/index.tsx | 34 ++++++++++++------ .../components/overview_network/index.tsx | 35 +++++++++++++------ .../components/recent_cases/index.tsx | 33 ++++++++++++----- .../recent_cases/no_cases/index.tsx | 33 ++++++++++++----- .../components/recent_cases/recent_cases.tsx | 25 +++++++++---- .../components/recent_timelines/index.tsx | 30 ++++++++++++---- .../public/overview/pages/overview.tsx | 3 +- .../public/overview/routes.tsx | 3 +- 10 files changed, 187 insertions(+), 73 deletions(-) diff --git a/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx b/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx index 574260a819071..a1661dfe1c503 100644 --- a/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.tsx @@ -4,12 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiButton } from '@elastic/eui'; import numeral from '@elastic/numeral'; -import React, { useEffect, useMemo } from 'react'; +import React, { useEffect, useMemo, useCallback } from 'react'; import { Position } from '@elastic/charts'; -import { DEFAULT_NUMBER_FORMAT } from '../../../../common/constants'; +import { DEFAULT_NUMBER_FORMAT, APP_ID } from '../../../../common/constants'; import { SHOWING, UNIT } from '../../../common/components/alerts_viewer/translations'; import { MatrixHistogramContainer } from '../../../common/components/matrix_histogram'; import { useKibana, useUiSetting$ } from '../../../common/lib/kibana'; @@ -29,9 +28,10 @@ import { histogramConfigs, } from '../../../common/components/alerts_viewer/histogram_configs'; import { MatrixHisrogramConfigs } from '../../../common/components/matrix_histogram/types'; -import { useGetUrlSearch } from '../../../common/components/navigation/use_get_url_search'; -import { navTabs } from '../../../app/home/home_navigations'; import { getTabsOnHostsUrl } from '../../../common/components/link_to/redirect_to_hosts'; +import { SecurityPageName } from '../../../app/types'; +import { useFormatUrl } from '../../../common/components/link_to'; +import { LinkButton } from '../../../common/components/links'; const ID = 'alertsByCategoryOverview'; @@ -74,19 +74,31 @@ const AlertsByCategoryComponent: React.FC = ({ }, []); const kibana = useKibana(); + const { formatUrl, search: urlSearch } = useFormatUrl(SecurityPageName.hosts); + const { navigateToApp } = kibana.services.application; const [defaultNumberFormat] = useUiSetting$(DEFAULT_NUMBER_FORMAT); - const urlSearch = useGetUrlSearch(navTabs.hosts); + + const goToHostAlerts = useCallback( + (ev) => { + ev.preventDefault(); + navigateToApp(`${APP_ID}:${SecurityPageName.hosts}`, { + path: getTabsOnHostsUrl(HostsTableType.alerts, urlSearch), + }); + }, + [navigateToApp, urlSearch] + ); const alertsCountViewAlertsButton = useMemo( () => ( - {i18n.VIEW_ALERTS} - + ), - [urlSearch] + [goToHostAlerts, formatUrl] ); const alertsByCategoryHistogramConfigs: MatrixHisrogramConfigs = useMemo( diff --git a/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx b/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx index 0065e5a61b3ff..fe3f9f8ecda33 100644 --- a/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/events_by_dataset/index.tsx @@ -5,12 +5,11 @@ */ import { Position } from '@elastic/charts'; -import { EuiButton } from '@elastic/eui'; import numeral from '@elastic/numeral'; -import React, { useEffect, useMemo } from 'react'; +import React, { useEffect, useMemo, useCallback } from 'react'; import uuid from 'uuid'; -import { DEFAULT_NUMBER_FORMAT } from '../../../../common/constants'; +import { DEFAULT_NUMBER_FORMAT, APP_ID } from '../../../../common/constants'; import { SHOWING, UNIT } from '../../../common/components/events_viewer/translations'; import { getTabsOnHostsUrl } from '../../../common/components/link_to/redirect_to_hosts'; import { MatrixHistogramContainer } from '../../../common/components/matrix_histogram'; @@ -18,8 +17,6 @@ import { MatrixHisrogramConfigs, MatrixHistogramOption, } from '../../../common/components/matrix_histogram/types'; -import { useGetUrlSearch } from '../../../common/components/navigation/use_get_url_search'; -import { navTabs } from '../../../app/home/home_navigations'; import { eventsStackByOptions } from '../../../hosts/pages/navigation'; import { convertToBuildEsQuery } from '../../../common/lib/keury'; import { useKibana, useUiSetting$ } from '../../../common/lib/kibana'; @@ -35,6 +32,9 @@ import { HostsTableType, HostsType } from '../../../hosts/store/model'; import { InputsModelId } from '../../../common/store/inputs/constants'; import * as i18n from '../../pages/translations'; +import { SecurityPageName } from '../../../app/types'; +import { useFormatUrl } from '../../../common/components/link_to'; +import { LinkButton } from '../../../common/components/links'; const NO_FILTERS: Filter[] = []; const DEFAULT_QUERY: Query = { query: '', language: 'kuery' }; @@ -95,16 +95,30 @@ const EventsByDatasetComponent: React.FC = ({ }, [deleteQuery, uniqueQueryId]); const kibana = useKibana(); + const { formatUrl, search: urlSearch } = useFormatUrl(SecurityPageName.hosts); + const { navigateToApp } = kibana.services.application; const [defaultNumberFormat] = useUiSetting$(DEFAULT_NUMBER_FORMAT); - const urlSearch = useGetUrlSearch(navTabs.hosts); + + const goToHostEvents = useCallback( + (ev) => { + ev.preventDefault(); + navigateToApp(`${APP_ID}:${SecurityPageName.hosts}`, { + path: getTabsOnHostsUrl(HostsTableType.events, urlSearch), + }); + }, + [navigateToApp, urlSearch] + ); const eventsCountViewEventsButton = useMemo( () => ( - + {i18n.VIEW_EVENTS} - + ), - [urlSearch] + [goToHostEvents, formatUrl] ); const filterQuery = useMemo( diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_host/index.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_host/index.tsx index 4fb37a9979245..195bb4fa0807a 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_host/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_host/index.tsx @@ -5,23 +5,23 @@ */ import { isEmpty } from 'lodash/fp'; -import { EuiButton, EuiFlexItem, EuiPanel } from '@elastic/eui'; +import { EuiFlexItem, EuiPanel } from '@elastic/eui'; import numeral from '@elastic/numeral'; import { FormattedMessage } from '@kbn/i18n/react'; -import React, { useMemo } from 'react'; +import React, { useMemo, useCallback } from 'react'; -import { DEFAULT_NUMBER_FORMAT } from '../../../../common/constants'; +import { DEFAULT_NUMBER_FORMAT, APP_ID } from '../../../../common/constants'; import { ESQuery } from '../../../../common/typed_json'; import { ID as OverviewHostQueryId, OverviewHostQuery } from '../../containers/overview_host'; import { HeaderSection } from '../../../common/components/header_section'; -import { useUiSetting$ } from '../../../common/lib/kibana'; -import { getHostsUrl } from '../../../common/components/link_to'; +import { useUiSetting$, useKibana } from '../../../common/lib/kibana'; +import { getHostsUrl, useFormatUrl } from '../../../common/components/link_to'; import { getOverviewHostStats, OverviewHostStats } from '../overview_host_stats'; import { manageQuery } from '../../../common/components/page/manage_query'; import { inputsModel } from '../../../common/store/inputs'; import { InspectButtonContainer } from '../../../common/components/inspect'; -import { useGetUrlSearch } from '../../../common/components/navigation/use_get_url_search'; -import { navTabs } from '../../../app/home/home_navigations'; +import { SecurityPageName } from '../../../app/types'; +import { LinkButton } from '../../../common/components/links'; export interface OwnProps { startDate: number; @@ -49,18 +49,30 @@ const OverviewHostComponent: React.FC = ({ startDate, setQuery, }) => { + const { formatUrl, search: urlSearch } = useFormatUrl(SecurityPageName.hosts); + const { navigateToApp } = useKibana().services.application; const [defaultNumberFormat] = useUiSetting$(DEFAULT_NUMBER_FORMAT); - const urlSearch = useGetUrlSearch(navTabs.hosts); + + const goToHost = useCallback( + (ev) => { + ev.preventDefault(); + navigateToApp(`${APP_ID}:${SecurityPageName.hosts}`, { + path: getHostsUrl(urlSearch), + }); + }, + [navigateToApp, urlSearch] + ); + const hostPageButton = useMemo( () => ( - + - + ), - [urlSearch] + [goToHost, formatUrl] ); return ( diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_network/index.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_network/index.tsx index 94a3c13e39947..31544eaa2d3b0 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_network/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_network/index.tsx @@ -5,15 +5,15 @@ */ import { isEmpty } from 'lodash/fp'; -import { EuiButton, EuiFlexItem, EuiPanel } from '@elastic/eui'; +import { EuiFlexItem, EuiPanel } from '@elastic/eui'; import numeral from '@elastic/numeral'; import { FormattedMessage } from '@kbn/i18n/react'; -import React, { useMemo } from 'react'; +import React, { useMemo, useCallback } from 'react'; -import { DEFAULT_NUMBER_FORMAT } from '../../../../common/constants'; +import { DEFAULT_NUMBER_FORMAT, APP_ID } from '../../../../common/constants'; import { ESQuery } from '../../../../common/typed_json'; import { HeaderSection } from '../../../common/components/header_section'; -import { useUiSetting$ } from '../../../common/lib/kibana'; +import { useUiSetting$, useKibana } from '../../../common/lib/kibana'; import { manageQuery } from '../../../common/components/page/manage_query'; import { ID as OverviewNetworkQueryId, @@ -21,10 +21,10 @@ import { } from '../../containers/overview_network'; import { inputsModel } from '../../../common/store/inputs'; import { getOverviewNetworkStats, OverviewNetworkStats } from '../overview_network_stats'; -import { getNetworkUrl } from '../../../common/components/link_to'; +import { getNetworkUrl, useFormatUrl } from '../../../common/components/link_to'; import { InspectButtonContainer } from '../../../common/components/inspect'; -import { useGetUrlSearch } from '../../../common/components/navigation/use_get_url_search'; -import { navTabs } from '../../../app/home/home_navigations'; +import { SecurityPageName } from '../../../app/types'; +import { LinkButton } from '../../../common/components/links'; export interface OverviewNetworkProps { startDate: number; @@ -51,19 +51,32 @@ const OverviewNetworkComponent: React.FC = ({ startDate, setQuery, }) => { + const { formatUrl, search: urlSearch } = useFormatUrl(SecurityPageName.hosts); + const { navigateToApp } = useKibana().services.application; const [defaultNumberFormat] = useUiSetting$(DEFAULT_NUMBER_FORMAT); - const urlSearch = useGetUrlSearch(navTabs.network); + + const goToNetwork = useCallback( + (ev) => { + ev.preventDefault(); + navigateToApp(`${APP_ID}:${SecurityPageName.hosts}`, { + path: getNetworkUrl(urlSearch), + }); + }, + [navigateToApp, urlSearch] + ); + const networkPageButton = useMemo( () => ( - + - + ), - [urlSearch] + [goToNetwork, formatUrl] ); + return ( diff --git a/x-pack/plugins/security_solution/public/overview/components/recent_cases/index.tsx b/x-pack/plugins/security_solution/public/overview/components/recent_cases/index.tsx index 03c1754f1b8d5..2618ad950a77a 100644 --- a/x-pack/plugins/security_solution/public/overview/components/recent_cases/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/recent_cases/index.tsx @@ -4,18 +4,20 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiHorizontalRule, EuiLink, EuiText } from '@elastic/eui'; -import React, { useEffect, useMemo, useRef } from 'react'; +import { EuiHorizontalRule, EuiText } from '@elastic/eui'; +import React, { useEffect, useMemo, useRef, useCallback } from 'react'; import { FilterOptions, QueryParams } from '../../../cases/containers/types'; import { DEFAULT_QUERY_PARAMS, useGetCases } from '../../../cases/containers/use_get_cases'; -import { getCaseUrl } from '../../../common/components/link_to/redirect_to_case'; -import { useGetUrlSearch } from '../../../common/components/navigation/use_get_url_search'; -import { navTabs } from '../../../app/home/home_navigations'; import { LoadingPlaceholders } from '../loading_placeholders'; import { NoCases } from './no_cases'; import { RecentCases } from './recent_cases'; import * as i18n from './translations'; +import { useKibana } from '../../../common/lib/kibana'; +import { APP_ID } from '../../../../common/constants'; +import { SecurityPageName } from '../../../app/types'; +import { useFormatUrl } from '../../../common/components/link_to'; +import { LinkAnchor } from '../../../common/components/links'; const usePrevious = (value: FilterOptions) => { const ref = useRef(); @@ -34,16 +36,31 @@ const queryParams: QueryParams = { const StatefulRecentCasesComponent = React.memo( ({ filterOptions }: { filterOptions: FilterOptions }) => { + const { formatUrl } = useFormatUrl(SecurityPageName.case); + const { navigateToApp } = useKibana().services.application; const previousFilterOptions = usePrevious(filterOptions); const { data, loading, setFilters } = useGetCases(queryParams); const isLoadingCases = useMemo( () => loading.indexOf('cases') > -1 || loading.indexOf('caseUpdate') > -1, [loading] ); - const search = useGetUrlSearch(navTabs.case); + + const goToCases = useCallback( + (ev) => { + ev.preventDefault(); + navigateToApp(`${APP_ID}:${SecurityPageName.case}`); + }, + [navigateToApp] + ); + const allCasesLink = useMemo( - () => {i18n.VIEW_ALL_CASES}, - [search] + () => ( + + {' '} + {i18n.VIEW_ALL_CASES} + + ), + [goToCases, formatUrl] ); useEffect(() => { diff --git a/x-pack/plugins/security_solution/public/overview/components/recent_cases/no_cases/index.tsx b/x-pack/plugins/security_solution/public/overview/components/recent_cases/no_cases/index.tsx index e29223ca07e65..4fa41507f244a 100644 --- a/x-pack/plugins/security_solution/public/overview/components/recent_cases/no_cases/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/recent_cases/no_cases/index.tsx @@ -4,20 +4,37 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiLink } from '@elastic/eui'; -import React, { useMemo } from 'react'; +import React, { useMemo, useCallback } from 'react'; +import { APP_ID } from '../../../../../common/constants'; import { getCreateCaseUrl } from '../../../../common/components/link_to/redirect_to_case'; -import { useGetUrlSearch } from '../../../../common/components/navigation/use_get_url_search'; -import { navTabs } from '../../../../app/home/home_navigations'; - +import { LinkAnchor } from '../../../../common/components/links'; +import { useFormatUrl } from '../../../../common/components/link_to'; import * as i18n from '../translations'; +import { useKibana } from '../../../../common/lib/kibana'; +import { SecurityPageName } from '../../../../app/types'; const NoCasesComponent = () => { - const urlSearch = useGetUrlSearch(navTabs.case); + const { formatUrl, search } = useFormatUrl(SecurityPageName.case); + const { navigateToApp } = useKibana().services.application; + + const goToCreateCase = useCallback( + (ev) => { + ev.preventDefault(); + navigateToApp(`${APP_ID}:${SecurityPageName.hosts}`, { + path: getCreateCaseUrl(search), + }); + }, + [navigateToApp, search] + ); const newCaseLink = useMemo( - () => {` ${i18n.START_A_NEW_CASE}`}, - [urlSearch] + () => ( + {` ${i18n.START_A_NEW_CASE}`} + ), + [goToCreateCase] ); return ( diff --git a/x-pack/plugins/security_solution/public/overview/components/recent_cases/recent_cases.tsx b/x-pack/plugins/security_solution/public/overview/components/recent_cases/recent_cases.tsx index 9618ddb05716d..5867a9d859f04 100644 --- a/x-pack/plugins/security_solution/public/overview/components/recent_cases/recent_cases.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/recent_cases/recent_cases.tsx @@ -4,18 +4,20 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiSpacer, EuiText } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText } from '@elastic/eui'; import React from 'react'; import styled from 'styled-components'; import { Case } from '../../../cases/containers/types'; import { getCaseDetailsUrl } from '../../../common/components/link_to/redirect_to_case'; import { Markdown } from '../../../common/components/markdown'; -import { useGetUrlSearch } from '../../../common/components/navigation/use_get_url_search'; -import { navTabs } from '../../../app/home/home_navigations'; +import { useFormatUrl } from '../../../common/components/link_to'; import { IconWithCount } from '../recent_timelines/counts'; - +import { LinkAnchor } from '../../../common/components/links'; import * as i18n from './translations'; +import { useKibana } from '../../../common/lib/kibana'; +import { APP_ID } from '../../../../common/constants'; +import { SecurityPageName } from '../../../app/types'; const MarkdownContainer = styled.div` max-height: 150px; @@ -24,7 +26,8 @@ const MarkdownContainer = styled.div` `; const RecentCasesComponent = ({ cases }: { cases: Case[] }) => { - const search = useGetUrlSearch(navTabs.case); + const { formatUrl, search } = useFormatUrl(SecurityPageName.case); + const { navigateToApp } = useKibana().services.application; return ( <> @@ -32,7 +35,17 @@ const RecentCasesComponent = ({ cases }: { cases: Case[] }) => { - {c.title} + void }) => { + ev.preventDefault(); + navigateToApp(`${APP_ID}:${SecurityPageName.case}`, { + path: getCaseDetailsUrl({ id: c.id, search }), + }); + }} + href={formatUrl(getCaseDetailsUrl({ id: c.id }))} + > + {c.title} + diff --git a/x-pack/plugins/security_solution/public/overview/components/recent_timelines/index.tsx b/x-pack/plugins/security_solution/public/overview/components/recent_timelines/index.tsx index 75b157a282eeb..52f8bdbc016b3 100644 --- a/x-pack/plugins/security_solution/public/overview/components/recent_timelines/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/recent_timelines/index.tsx @@ -5,7 +5,7 @@ */ import ApolloClient from 'apollo-client'; -import { EuiHorizontalRule, EuiLink, EuiText } from '@elastic/eui'; +import { EuiHorizontalRule, EuiText } from '@elastic/eui'; import React, { useCallback, useMemo, useEffect } from 'react'; import { connect, ConnectedProps } from 'react-redux'; import { Dispatch } from 'redux'; @@ -23,10 +23,12 @@ import { updateIsLoading as dispatchUpdateIsLoading } from '../../../timelines/s import { RecentTimelines } from './recent_timelines'; import * as i18n from './translations'; import { FilterMode } from './types'; -import { useGetUrlSearch } from '../../../common/components/navigation/use_get_url_search'; -import { navTabs } from '../../../app/home/home_navigations'; -import { getTimelinesUrl } from '../../../common/components/link_to/redirect_to_timelines'; import { LoadingPlaceholders } from '../loading_placeholders'; +import { useKibana } from '../../../common/lib/kibana'; +import { SecurityPageName } from '../../../app/types'; +import { APP_ID } from '../../../../common/constants'; +import { useFormatUrl } from '../../../common/components/link_to'; +import { LinkAnchor } from '../../../common/components/links'; interface OwnProps { apolloClient: ApolloClient<{}>; @@ -39,6 +41,8 @@ const PAGE_SIZE = 3; const StatefulRecentTimelinesComponent = React.memo( ({ apolloClient, filterBy, updateIsLoading, updateTimeline }) => { + const { formatUrl } = useFormatUrl(SecurityPageName.timelines); + const { navigateToApp } = useKibana().services.application; const onOpenTimeline: OnOpenTimeline = useCallback( ({ duplicate, timelineId }: { duplicate: boolean; timelineId: string }) => { queryTimelineById({ @@ -52,12 +56,24 @@ const StatefulRecentTimelinesComponent = React.memo( [apolloClient, updateIsLoading, updateTimeline] ); + const goToTimelines = useCallback( + (ev) => { + ev.preventDefault(); + navigateToApp(`${APP_ID}:${SecurityPageName.timelines}`); + }, + [navigateToApp] + ); + const noTimelinesMessage = filterBy === 'favorites' ? i18n.NO_FAVORITE_TIMELINES : i18n.NO_TIMELINES; - const urlSearch = useGetUrlSearch(navTabs.timelines); + const linkAllTimelines = useMemo( - () => {i18n.VIEW_ALL_TIMELINES}, - [urlSearch] + () => ( + + {i18n.VIEW_ALL_TIMELINES} + + ), + [goToTimelines, formatUrl] ); const loadingPlaceholders = useMemo( () => ( diff --git a/x-pack/plugins/security_solution/public/overview/pages/overview.tsx b/x-pack/plugins/security_solution/public/overview/pages/overview.tsx index 57a82f6f254f2..543dafd50c8e0 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/overview.tsx +++ b/x-pack/plugins/security_solution/public/overview/pages/overview.tsx @@ -28,6 +28,7 @@ import { SignalsByCategory } from '../components/signals_by_category'; import { inputsSelectors, State } from '../../common/store'; import { setAbsoluteRangeDatePicker as dispatchSetAbsoluteRangeDatePicker } from '../../common/store/inputs/actions'; import { SpyRoute } from '../../common/utils/route/spy_routes'; +import { SecurityPageName } from '../../app/types'; const DEFAULT_QUERY: Query = { query: '', language: 'kuery' }; const NO_FILTERS: Filter[] = []; @@ -120,7 +121,7 @@ const OverviewComponent: React.FC = ({ } - + ); diff --git a/x-pack/plugins/security_solution/public/overview/routes.tsx b/x-pack/plugins/security_solution/public/overview/routes.tsx index 25fc5f9402c4d..31a0c482ab2c9 100644 --- a/x-pack/plugins/security_solution/public/overview/routes.tsx +++ b/x-pack/plugins/security_solution/public/overview/routes.tsx @@ -8,10 +8,9 @@ import React from 'react'; import { Route, Switch } from 'react-router-dom'; import { Overview } from './pages'; -import { SecurityPageName } from '../app/types'; export const OverviewRoutes = () => ( - } /> + } /> ); From b6fa804ad8cbff8593462565703675e2ad406c4c Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Wed, 10 Jun 2020 22:18:54 -0400 Subject: [PATCH 08/43] modify timelines routes --- .../components/open_timeline/types.ts | 1 + .../open_timeline/use_timeline_types.tsx | 43 ++++++++++++++----- .../body/renderers/formatted_field.tsx | 13 +++++- .../renderers/formatted_field_helpers.tsx | 37 +++++++++++----- .../timelines/components/timeline/index.tsx | 4 +- .../public/timelines/pages/index.tsx | 18 ++++---- .../public/timelines/pages/timelines_page.tsx | 3 +- .../public/timelines/routes.tsx | 3 +- 8 files changed, 86 insertions(+), 36 deletions(-) diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/types.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/types.ts index 105f1adf05a62..e1515a3a79254 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/types.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/types.ts @@ -200,4 +200,5 @@ export interface TimelineTab { name: string; disabled: boolean; href: string; + onClick: (ev: { preventDefault: () => void }) => void; } diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/use_timeline_types.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/use_timeline_types.tsx index f99d8c566c4a5..c736c034a0500 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/use_timeline_types.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/use_timeline_types.tsx @@ -4,14 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ import React, { useState, useCallback, useMemo } from 'react'; -import { useParams } from 'react-router-dom'; +import { useParams, useHistory } from 'react-router-dom'; import { EuiTabs, EuiTab, EuiSpacer, EuiFilterButton } from '@elastic/eui'; import { TimelineTypeLiteralWithNull, TimelineType } from '../../../../common/types/timeline'; - -import { getTimelineTabsUrl } from '../../../common/components/link_to'; -import { navTabs } from '../../../app/home/home_navigations'; -import { useGetUrlSearch } from '../../../common/components/navigation/use_get_url_search'; +import { SecurityPageName } from '../../../app/types'; +import { getTimelineTabsUrl, useFormatUrl } from '../../../common/components/link_to'; import * as i18n from './translations'; import { TimelineTabsStyle, TimelineTab } from './types'; @@ -20,12 +18,29 @@ export const useTimelineTypes = (): { timelineTabs: JSX.Element; timelineFilters: JSX.Element; } => { - const urlSearch = useGetUrlSearch(navTabs.timelines); + const history = useHistory(); + const { formatUrl, search: urlSearch } = useFormatUrl(SecurityPageName.timelines); const { tabName } = useParams<{ pageName: string; tabName: string }>(); const [timelineType, setTimelineTypes] = useState( tabName === TimelineType.default || tabName === TimelineType.template ? tabName : null ); + const goToTimeline = useCallback( + (ev) => { + ev.preventDefault(); + history.push(getTimelineTabsUrl(TimelineType.default, urlSearch)); + }, + [history] + ); + + const goToTemplateTimeline = useCallback( + (ev) => { + ev.preventDefault(); + history.push(getTimelineTabsUrl(TimelineType.template, urlSearch)); + }, + [history] + ); + const getFilterOrTabs: (timelineTabsStyle: TimelineTabsStyle) => TimelineTab[] = ( timelineTabsStyle: TimelineTabsStyle ) => [ @@ -35,8 +50,9 @@ export const useTimelineTypes = (): { timelineTabsStyle === TimelineTabsStyle.filter ? i18n.FILTER_TIMELINES(i18n.TAB_TIMELINES) : i18n.TAB_TIMELINES, - href: getTimelineTabsUrl(TimelineType.default, urlSearch), + href: formatUrl(getTimelineTabsUrl(TimelineType.default, urlSearch)), disabled: false, + onClick: goToTimeline, }, { id: TimelineType.template, @@ -44,8 +60,9 @@ export const useTimelineTypes = (): { timelineTabsStyle === TimelineTabsStyle.filter ? i18n.FILTER_TIMELINES(i18n.TAB_TEMPLATES) : i18n.TAB_TEMPLATES, - href: getTimelineTabsUrl(TimelineType.template, urlSearch), + href: formatUrl(getTimelineTabsUrl(TimelineType.template, urlSearch)), disabled: false, + onClick: goToTemplateTimeline, }, ]; @@ -70,7 +87,10 @@ export const useTimelineTypes = (): { disabled={tab.disabled} key={`timeline-${TimelineTabsStyle.tab}-${tab.id}`} href={tab.href} - onClick={onFilterClicked.bind(null, TimelineTabsStyle.tab, tab.id)} + onClick={(ev) => { + tab.onClick(ev); + onFilterClicked(TimelineTabsStyle.tab, tab.id); + }} > {tab.name} @@ -88,7 +108,10 @@ export const useTimelineTypes = (): { void }) => { + tab.onClick(ev); + onFilterClicked.bind(null, TimelineTabsStyle.filter, tab.id); + }} > {tab.name} diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx index aaac3e7f7f4d5..b2588e19800a6 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.tsx @@ -30,7 +30,7 @@ import { RULE_REFERENCE_FIELD_NAME, SIGNAL_RULE_NAME_FIELD_NAME, } from './constants'; -import { renderRuleName, renderEventModule, renderRulReference } from './formatted_field_helpers'; +import { RenderRuleName, renderEventModule, renderRulReference } from './formatted_field_helpers'; // simple black-list to prevent dragging and dropping fields such as message name const columnNamesNotDraggable = [MESSAGE_FIELD_NAME]; @@ -95,7 +95,16 @@ const FormattedFieldValueComponent: React.FC<{ ); } else if (fieldName === SIGNAL_RULE_NAME_FIELD_NAME) { - return renderRuleName({ contextId, eventId, fieldName, linkValue, truncate, value }); + return ( + + ); } else if (fieldName === EVENT_MODULE_FIELD_NAME) { return renderEventModule({ contextId, eventId, fieldName, linkValue, truncate, value }); } else if (fieldName === RULE_REFERENCE_FIELD_NAME) { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field_helpers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field_helpers.tsx index b24c262c8a2e2..f950b847d7adc 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field_helpers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field_helpers.tsx @@ -6,7 +6,7 @@ import { EuiLink, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiToolTip } from '@elastic/eui'; import { isString, isEmpty } from 'lodash/fp'; -import React from 'react'; +import React, { useCallback } from 'react'; import styled from 'styled-components'; import { DefaultDraggable } from '../../../../../common/components/draggables'; @@ -18,31 +18,44 @@ import { isUrlInvalid } from '../../../../../common/utils/validators'; import endPointSvg from '../../../../../common/utils/logo_endpoint/64_color.svg'; import * as i18n from './translations'; +import { SecurityPageName } from '../../../../../app/types'; +import { useFormatUrl } from '../../../../../common/components/link_to'; +import { useKibana } from '../../../../../common/lib/kibana'; +import { APP_ID } from '../../../../../../common/constants'; +import { LinkAnchor } from '../../../../../common/components/links'; const EventModuleFlexItem = styled(EuiFlexItem)` width: 100%; `; -export const renderRuleName = ({ - contextId, - eventId, - fieldName, - linkValue, - truncate, - value, -}: { +interface RenderRuleNameProps { contextId: string; eventId: string; fieldName: string; linkValue: string | null | undefined; truncate?: boolean; value: string | number | null | undefined; +} +export const RenderRuleName: React.FC = ({ + contextId, + eventId, + fieldName, + linkValue, + truncate, + value, }) => { const ruleName = `${value}`; const ruleId = linkValue; - + const { formatUrl } = useFormatUrl(SecurityPageName.alerts); + const { navigateToApp } = useKibana().services.application; const content = truncate ? {value} : value; + const goToRuleDetails = useCallback(() => { + navigateToApp(`${APP_ID}:${SecurityPageName.alerts}`, { + path: getRuleDetailsUrl(ruleId ?? ''), + }); + }, [navigateToApp, ruleId]); + return isString(value) && ruleName.length > 0 && ruleId != null ? ( - {content} + + {content} + ) : ( getEmptyTagValue() diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx index c52be64f94bf1..73bdc66bc5a88 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx @@ -42,6 +42,7 @@ const StatefulTimelineComponent = React.memo( filters, id, isLive, + isTimelineExists, itemsPerPage, itemsPerPageOptions, kqlMode, @@ -144,7 +145,7 @@ const StatefulTimelineComponent = React.memo( ); useEffect(() => { - if (createTimeline != null) { + if (createTimeline != null && !isTimelineExists) { createTimeline({ id, columns: defaultHeaders, show: false }); } }, []); @@ -240,6 +241,7 @@ const makeMapStateToProps = () => { filters: timelineFilter, id, isLive: input.policy.kind === 'interval', + isTimelineExists: getTimeline(state, id) != null, itemsPerPage, itemsPerPageOptions, kqlMode, diff --git a/x-pack/plugins/security_solution/public/timelines/pages/index.tsx b/x-pack/plugins/security_solution/public/timelines/pages/index.tsx index f07bb71c6f46c..8aea1e45cce76 100644 --- a/x-pack/plugins/security_solution/public/timelines/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/pages/index.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { ApolloConsumer } from 'react-apollo'; -import { Switch, Route, Redirect } from 'react-router-dom'; +import { Switch, Route, useHistory } from 'react-router-dom'; import { ChromeBreadcrumb } from '../../../../../../src/core/public'; @@ -15,13 +15,11 @@ import { TAB_TIMELINES, TAB_TEMPLATES } from '../components/open_timeline/transl import { getTimelinesUrl } from '../../common/components/link_to'; import { TimelineRouteSpyState } from '../../common/utils/route/types'; -import { SecurityPageName } from '../../app/types'; - import { TimelinesPage } from './timelines_page'; import { PAGE_TITLE } from './translations'; import { appendSearch } from '../../common/components/link_to/helpers'; -const timelinesPagePath = `/:pageName(${SecurityPageName.timelines})/:tabName(${TimelineType.default}|${TimelineType.template})`; -const timelinesDefaultPath = `/${SecurityPageName.timelines}/${TimelineType.default}`; +const timelinesPagePath = `/:tabName(${TimelineType.default}|${TimelineType.template})`; +const timelinesDefaultPath = `/${TimelineType.default}`; const TabNameMappedToI18nKey: Record = { [TimelineType.default]: TAB_TIMELINES, @@ -53,16 +51,18 @@ export const getBreadcrumbs = ( }; export const Timelines = React.memo(() => { + const history = useHistory(); return ( {(client) => } ( - - )} + path="/" + render={({ location: { search = '' } }) => { + history.replace(`${timelinesDefaultPath}${appendSearch(search)}`); + return null; + }} /> ); diff --git a/x-pack/plugins/security_solution/public/timelines/pages/timelines_page.tsx b/x-pack/plugins/security_solution/public/timelines/pages/timelines_page.tsx index fd734d10ecba0..c5bcfbaffbd63 100644 --- a/x-pack/plugins/security_solution/public/timelines/pages/timelines_page.tsx +++ b/x-pack/plugins/security_solution/public/timelines/pages/timelines_page.tsx @@ -14,6 +14,7 @@ import { useKibana } from '../../common/lib/kibana'; import { SpyRoute } from '../../common/utils/route/spy_routes'; import { StatefulOpenTimeline } from '../components/open_timeline'; import * as i18n from './translations'; +import { SecurityPageName } from '../../app/types'; const TimelinesContainer = styled.div` width: 100%; @@ -67,7 +68,7 @@ export const TimelinesPageComponent: React.FC = ({ apolloClient }) => - + ); }; diff --git a/x-pack/plugins/security_solution/public/timelines/routes.tsx b/x-pack/plugins/security_solution/public/timelines/routes.tsx index ce55ae9ed6d8f..853b06c3ea995 100644 --- a/x-pack/plugins/security_solution/public/timelines/routes.tsx +++ b/x-pack/plugins/security_solution/public/timelines/routes.tsx @@ -8,10 +8,9 @@ import React from 'react'; import { Route, Switch } from 'react-router-dom'; import { Timelines } from './pages'; -import { SecurityPageName } from '../app/types'; export const TimelinesRoutes = () => ( - } /> + } /> ); From 1ebe9cfe926c2d705fac11d790503f989019c004 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Wed, 10 Jun 2020 22:19:38 -0400 Subject: [PATCH 09/43] wip change management route --- .../public/management/common/constants.ts | 3 +-- .../public/management/pages/index.tsx | 12 +++++++++--- .../management/pages/policy/view/policy_details.tsx | 5 +++-- .../management/pages/policy/view/policy_list.tsx | 3 ++- .../security_solution/public/management/routes.tsx | 3 +-- 5 files changed, 16 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/common/constants.ts b/x-pack/plugins/security_solution/public/management/common/constants.ts index e01d85bbbb9a2..d5b212bc16bcf 100644 --- a/x-pack/plugins/security_solution/public/management/common/constants.ts +++ b/x-pack/plugins/security_solution/public/management/common/constants.ts @@ -3,11 +3,10 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { SecurityPageName } from '../../app/types'; import { ManagementStoreGlobalNamespace, ManagementSubTab } from '../types'; // --[ ROUTING ]--------------------------------------------------------------------------- -export const MANAGEMENT_ROUTING_ROOT_PATH = `/:pageName(${SecurityPageName.management})`; +export const MANAGEMENT_ROUTING_ROOT_PATH = ''; export const MANAGEMENT_ROUTING_ENDPOINTS_PATH = `${MANAGEMENT_ROUTING_ROOT_PATH}/:tabName(${ManagementSubTab.endpoints})`; export const MANAGEMENT_ROUTING_POLICIES_PATH = `${MANAGEMENT_ROUTING_ROOT_PATH}/:tabName(${ManagementSubTab.policies})`; export const MANAGEMENT_ROUTING_POLICY_DETAILS_PATH = `${MANAGEMENT_ROUTING_ROOT_PATH}/:tabName(${ManagementSubTab.policies})/:policyId`; diff --git a/x-pack/plugins/security_solution/public/management/pages/index.tsx b/x-pack/plugins/security_solution/public/management/pages/index.tsx index aba482db86519..1fab77800af3c 100644 --- a/x-pack/plugins/security_solution/public/management/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/index.tsx @@ -5,7 +5,9 @@ */ import React, { memo } from 'react'; -import { Redirect, Route, Switch } from 'react-router-dom'; +import { useHistory, Route, Switch } from 'react-router-dom'; + +import { SecurityPageName } from '../../app/types'; import { SpyRoute } from '../../common/utils/route/spy_routes'; import { PolicyContainer } from './policy'; import { @@ -20,12 +22,13 @@ const TmpEndpoints = () => { return (

{'Endpoints will go here'}

- +
); }; export const ManagementContainer = memo(() => { + const history = useHistory(); return ( @@ -33,7 +36,10 @@ export const ManagementContainer = memo(() => { } + render={() => { + history.replace('/endpoints'); + return null; + }} /> diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx index f4c4b36ce153f..a11980d173a3e 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx @@ -39,6 +39,7 @@ import { PageViewHeaderTitle } from '../../../../common/components/endpoint/page import { ManagementPageView } from '../../../components/management_page_view'; import { SpyRoute } from '../../../../common/utils/route/spy_routes'; import { getManagementUrl } from '../../../common/routing'; +import { SecurityPageName } from '../../../../app/types'; export const PolicyDetails = React.memo(() => { const dispatch = useDispatch<(action: AppAction) => void>(); @@ -119,7 +120,7 @@ export const PolicyDetails = React.memo(() => { {policyApiError?.message} ) : null} - + ); } @@ -224,7 +225,7 @@ export const PolicyDetails = React.memo(() => { - + ); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.tsx index d34a6d2d93893..9f1efe8fbef01 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.tsx @@ -35,6 +35,7 @@ import { ManagementPageView } from '../../../components/management_page_view'; import { SpyRoute } from '../../../../common/utils/route/spy_routes'; import { getManagementUrl } from '../../../common/routing'; import { FormattedDateAndTime } from '../../../../common/components/endpoint/formatted_date_time'; +import { SecurityPageName } from '../../../../app/types'; interface TableChangeCallbackArguments { page: { index: number; size: number }; @@ -285,7 +286,7 @@ export const PolicyList = React.memo(() => { data-test-subj="policyTable" hasActions={false} /> - + ); }); diff --git a/x-pack/plugins/security_solution/public/management/routes.tsx b/x-pack/plugins/security_solution/public/management/routes.tsx index fc74490515fd7..118d9b8c3e80b 100644 --- a/x-pack/plugins/security_solution/public/management/routes.tsx +++ b/x-pack/plugins/security_solution/public/management/routes.tsx @@ -7,13 +7,12 @@ import React from 'react'; import { Route, Switch } from 'react-router-dom'; import { ManagementContainer } from './pages'; -import { MANAGEMENT_ROUTING_ROOT_PATH } from './common/constants'; /** * Returns the React Router Routes for the management area */ export const ManagementRoutes = () => ( - + ); From 24b13c2f78cb122ffcf3346eb92aa62e513bb83f Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Wed, 10 Jun 2020 22:20:31 -0400 Subject: [PATCH 10/43] change route for common --- .../common/components/header_global/index.tsx | 27 +++- .../components/header_page/index.test.tsx | 6 +- .../common/components/header_page/index.tsx | 88 +++++++----- .../public/common/components/link_to/index.ts | 75 ++++++++--- .../common/components/link_to/link_to.tsx | 126 ------------------ .../components/link_to/redirect_to_case.tsx | 42 +----- .../link_to/redirect_to_detection_engine.tsx | 61 +-------- .../components/link_to/redirect_to_hosts.tsx | 53 ++------ .../link_to/redirect_to_management.tsx | 15 --- .../link_to/redirect_to_network.tsx | 33 +---- .../link_to/redirect_to_overview.tsx | 16 +-- .../link_to/redirect_to_timelines.tsx | 33 +---- .../components/link_to/redirect_wrapper.tsx | 18 --- .../public/common/components/links/index.tsx | 124 +++++++++++++---- .../navigation/breadcrumbs/index.ts | 12 +- .../navigation/tab_navigation/index.tsx | 38 ++++-- .../navigation/tab_navigation/types.ts | 2 + .../common/components/navigation/types.ts | 2 + .../components/news_feed/no_news/index.tsx | 26 ++-- .../common/components/url_state/constants.ts | 4 +- .../common/components/url_state/helpers.ts | 21 +-- .../components/url_state/index.test.tsx | 2 +- .../common/components/url_state/types.ts | 4 +- .../public/common/utils/route/index.test.tsx | 8 +- 24 files changed, 345 insertions(+), 491 deletions(-) delete mode 100644 x-pack/plugins/security_solution/public/common/components/link_to/link_to.tsx delete mode 100644 x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_management.tsx delete mode 100644 x-pack/plugins/security_solution/public/common/components/link_to/redirect_wrapper.tsx diff --git a/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx b/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx index b28bb8ec6456f..20c00171cdafe 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx @@ -4,20 +4,24 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLink } from '@elastic/eui'; +import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui'; import { pickBy } from 'lodash/fp'; -import React from 'react'; +import React, { useCallback } from 'react'; import styled, { css } from 'styled-components'; import { useLocation } from 'react-router-dom'; import { gutterTimeline } from '../../lib/helpers'; import { navTabs } from '../../../app/home/home_navigations'; import { SecurityPageName } from '../../../app/types'; -import { getOverviewUrl } from '../link_to'; +import { getAppOverviewUrl } from '../link_to'; import { MlPopover } from '../ml_popover/ml_popover'; import { SiemNavigation } from '../navigation'; import * as i18n from './translations'; import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../containers/source'; +import { useGetUrlSearch } from '../navigation/use_get_url_search'; +import { useKibana } from '../../lib/kibana'; +import { APP_ID } from '../../../../common/constants'; +import { LinkAnchor } from '../links'; const Wrapper = styled.header` ${({ theme }) => css` @@ -39,6 +43,15 @@ interface HeaderGlobalProps { } export const HeaderGlobal = React.memo(({ hideDetectionEngine = false }) => { const currentLocation = useLocation(); + const search = useGetUrlSearch(navTabs.overview); + const { navigateToApp } = useKibana().services.application; + const goToOverview = useCallback( + (ev) => { + ev.preventDefault(); + navigateToApp(`${APP_ID}:${SecurityPageName.overview}`, { path: search }); + }, + [navigateToApp, search] + ); return ( @@ -49,9 +62,9 @@ export const HeaderGlobal = React.memo(({ hideDetectionEngine - + - + @@ -60,7 +73,7 @@ export const HeaderGlobal = React.memo(({ hideDetectionEngine display="condensed" navTabs={ hideDetectionEngine - ? pickBy((_, key) => key !== SecurityPageName.detections, navTabs) + ? pickBy((_, key) => key !== SecurityPageName.alerts, navTabs) : navTabs } /> @@ -77,7 +90,7 @@ export const HeaderGlobal = React.memo(({ hideDetectionEngine {indicesExistOrDataTemporarilyUnavailable(indicesExist) && - currentLocation.pathname.includes(`/${SecurityPageName.detections}/`) && ( + currentLocation.pathname.includes(`/${SecurityPageName.alerts}/`) && ( diff --git a/x-pack/plugins/security_solution/public/common/components/header_page/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/header_page/index.test.tsx index 29f227578ed96..db88e0e07db5c 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_page/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/header_page/index.test.tsx @@ -11,6 +11,7 @@ import React from 'react'; import { TestProviders } from '../../mock'; import { HeaderPage } from './index'; import { useMountAppended } from '../../utils/use_mount_appended'; +import { SecurityPageName } from '../../../app/types'; describe('HeaderPage', () => { const mount = useMountAppended(); @@ -34,7 +35,10 @@ describe('HeaderPage', () => { test('it renders the back link when provided', () => { const wrapper = mount( - + ); diff --git a/x-pack/plugins/security_solution/public/common/components/header_page/index.tsx b/x-pack/plugins/security_solution/public/common/components/header_page/index.tsx index 3656b326e26d8..78138183d010d 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_page/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/header_page/index.tsx @@ -5,13 +5,16 @@ */ import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiProgress } from '@elastic/eui'; -import React from 'react'; +import React, { useCallback } from 'react'; import styled, { css } from 'styled-components'; +import { useHistory } from 'react-router-dom'; import { LinkIcon, LinkIconProps } from '../link_icon'; import { Subtitle, SubtitleProps } from '../subtitle'; import { Title } from './title'; import { DraggableArguments, BadgeOptions, TitleProp } from './types'; +import { useFormatUrl } from '../link_to'; +import { SecurityPageName } from '../../../app/types'; interface HeaderProps { border?: boolean; @@ -61,6 +64,7 @@ interface BackOptions { href: LinkIconProps['href']; text: LinkIconProps['children']; dataTestSubj?: string; + pageId: SecurityPageName; } export interface HeaderPageProps extends HeaderProps { @@ -86,42 +90,56 @@ const HeaderPageComponent: React.FC = ({ title, titleNode, ...rest -}) => ( -
- - - {backOptions && ( - - - {backOptions.text} - - - )} - - {titleNode || ( - - )} +}) => { + const history = useHistory(); + const { formatUrl } = useFormatUrl(backOptions?.pageId ?? SecurityPageName.overview); + const goTo = useCallback( + (ev) => { + ev.preventDefault(); + if (backOptions) { + history.push(backOptions.href ?? ''); + } + }, + [backOptions, history] + ); + return ( + <Header border={border} {...rest}> + <EuiFlexGroup alignItems="center"> + <FlexItem> + {backOptions && ( + <LinkBack> + <LinkIcon + dataTestSubj={backOptions.dataTestSubj} + onClick={goTo} + href={formatUrl(backOptions.href ?? '')} + iconType="arrowLeft" + > + {backOptions.text} + </LinkIcon> + </LinkBack> + )} - {subtitle && <Subtitle data-test-subj="header-page-subtitle" items={subtitle} />} - {subtitle2 && <Subtitle data-test-subj="header-page-subtitle-2" items={subtitle2} />} - {border && isLoading && <EuiProgress size="xs" color="accent" />} - </FlexItem> + {titleNode || ( + <Title + draggableArguments={draggableArguments} + title={title} + badgeOptions={badgeOptions} + /> + )} - {children && ( - <FlexItem data-test-subj="header-page-supplements" grow={false}> - {children} + {subtitle && <Subtitle data-test-subj="header-page-subtitle" items={subtitle} />} + {subtitle2 && <Subtitle data-test-subj="header-page-subtitle-2" items={subtitle2} />} + {border && isLoading && <EuiProgress size="xs" color="accent" />} </FlexItem> - )} - </EuiFlexGroup> - </Header> -); + + {children && ( + <FlexItem data-test-subj="header-page-supplements" grow={false}> + {children} + </FlexItem> + )} + </EuiFlexGroup> + </Header> + ); +}; export const HeaderPage = React.memo(HeaderPageComponent); diff --git a/x-pack/plugins/security_solution/public/common/components/link_to/index.ts b/x-pack/plugins/security_solution/public/common/components/link_to/index.ts index c35a60766d7bd..06ef26cef3743 100644 --- a/x-pack/plugins/security_solution/public/common/components/link_to/index.ts +++ b/x-pack/plugins/security_solution/public/common/components/link_to/index.ts @@ -4,25 +4,70 @@ * you may not use this file except in compliance with the Elastic License. */ -export { LinkToPage } from './link_to'; -export { - getDetectionEngineUrl, - RedirectToDetectionEnginePage, -} from './redirect_to_detection_engine'; -export { getOverviewUrl, RedirectToOverviewPage } from './redirect_to_overview'; +import { useCallback } from 'react'; +import { useBasePath } from '../../lib/kibana'; +import { SecurityPageName } from '../../../app/types'; +import { useGetUrlSearch } from '../navigation/use_get_url_search'; +import { navTabs } from '../../../app/home/home_navigations'; +import { + APP_ALERTS_PATH, + APP_CASES_PATH, + APP_HOSTS_PATH, + APP_MANAGEMENT_PATH, + APP_NETWORK_PATH, + APP_OVERVIEW_PATH, + APP_TIMELINES_PATH, +} from '../../../../common/constants'; + +export { getDetectionEngineUrl } from './redirect_to_detection_engine'; +export { getAppOverviewUrl } from './redirect_to_overview'; export { getHostDetailsUrl, getHostsUrl } from './redirect_to_hosts'; -export { getNetworkUrl, getIPDetailsUrl, RedirectToNetworkPage } from './redirect_to_network'; -export { - getTimelinesUrl, - getTimelineTabsUrl, - RedirectToTimelinesPage, -} from './redirect_to_timelines'; +export { getNetworkUrl, getIPDetailsUrl } from './redirect_to_network'; +export { getTimelinesUrl, getTimelineTabsUrl } from './redirect_to_timelines'; export { getCaseDetailsUrl, getCaseUrl, getCreateCaseUrl, getConfigureCasesUrl, - RedirectToCasePage, - RedirectToCreatePage, - RedirectToConfigureCasesPage, } from './redirect_to_case'; + +const getSubAppPath = (page: string) => { + switch (page) { + case SecurityPageName.alerts: { + return APP_ALERTS_PATH; + } + case SecurityPageName.case: { + return APP_CASES_PATH; + } + case SecurityPageName.hosts: { + return APP_HOSTS_PATH; + } + case SecurityPageName.management: { + return APP_MANAGEMENT_PATH; + } + case SecurityPageName.network: { + return APP_NETWORK_PATH; + } + case SecurityPageName.overview: { + return APP_OVERVIEW_PATH; + } + case SecurityPageName.timelines: { + return APP_TIMELINES_PATH; + } + default: + return APP_OVERVIEW_PATH; + } +}; + +export const useFormatUrl = (page: SecurityPageName) => { + const basePath = useBasePath(); + const search = useGetUrlSearch(navTabs[page]); + const formatUrl = useCallback( + (path: string) => { + const hasSearch = path.includes('?'); + return `${basePath}${getSubAppPath(page)}${path}${!hasSearch ? search : ''}`; + }, + [basePath, page, search] + ); + return { formatUrl, search }; +}; diff --git a/x-pack/plugins/security_solution/public/common/components/link_to/link_to.tsx b/x-pack/plugins/security_solution/public/common/components/link_to/link_to.tsx deleted file mode 100644 index 33198ef4aac4b..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/link_to/link_to.tsx +++ /dev/null @@ -1,126 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { match as RouteMatch, Redirect, Route, Switch } from 'react-router-dom'; - -import { SecurityPageName } from '../../../app/types'; -import { HostsTableType } from '../../../hosts/store/model'; -import { - RedirectToCreateRulePage, - RedirectToDetectionEnginePage, - RedirectToEditRulePage, - RedirectToRuleDetailsPage, - RedirectToRulesPage, -} from './redirect_to_detection_engine'; -import { RedirectToHostsPage, RedirectToHostDetailsPage } from './redirect_to_hosts'; -import { RedirectToNetworkPage } from './redirect_to_network'; -import { RedirectToOverviewPage } from './redirect_to_overview'; -import { RedirectToTimelinesPage } from './redirect_to_timelines'; -import { - RedirectToCasePage, - RedirectToCreatePage, - RedirectToConfigureCasesPage, -} from './redirect_to_case'; -import { TimelineType } from '../../../../common/types/timeline'; -import { RedirectToManagementPage } from './redirect_to_management'; - -interface LinkToPageProps { - match: RouteMatch<{}>; -} - -export const LinkToPage = React.memo<LinkToPageProps>(({ match }) => ( - <Switch> - <Route - component={RedirectToOverviewPage} - path={`${match.url}/:pageName(${SecurityPageName.overview})`} - /> - <Route - exact - component={RedirectToCasePage} - path={`${match.url}/:pageName(${SecurityPageName.case})`} - /> - <Route - exact - component={RedirectToCreatePage} - path={`${match.url}/:pageName(${SecurityPageName.case})/create`} - /> - <Route - exact - component={RedirectToConfigureCasesPage} - path={`${match.url}/:pageName(${SecurityPageName.case})/configure`} - /> - <Route - component={RedirectToCasePage} - path={`${match.url}/:pageName(${SecurityPageName.case})/:detailName`} - /> - <Route - component={RedirectToHostsPage} - exact - path={`${match.url}/:pageName(${SecurityPageName.hosts})`} - /> - <Route - component={RedirectToHostsPage} - path={`${match.url}/:pageName(${SecurityPageName.hosts})/:tabName(${HostsTableType.hosts}|${HostsTableType.authentications}|${HostsTableType.uncommonProcesses}|${HostsTableType.anomalies}|${HostsTableType.events}|${HostsTableType.alerts})`} - /> - <Route - component={RedirectToHostDetailsPage} - path={`${match.url}/:pageName(${SecurityPageName.hosts})/:detailName/:tabName(${HostsTableType.authentications}|${HostsTableType.uncommonProcesses}|${HostsTableType.anomalies}|${HostsTableType.events}|${HostsTableType.alerts})`} - /> - <Route - component={RedirectToHostDetailsPage} - path={`${match.url}/:pageName(${SecurityPageName.hosts})/:detailName`} - /> - <Route - component={RedirectToNetworkPage} - exact - path={`${match.url}/:pageName(${SecurityPageName.network})`} - /> - <Route - component={RedirectToNetworkPage} - path={`${match.url}/:pageName(${SecurityPageName.network})/ip/:detailName/:flowTarget`} - /> - <Route - component={RedirectToDetectionEnginePage} - exact - path={`${match.url}/:pageName(${SecurityPageName.detections})`} - /> - <Route - component={RedirectToRulesPage} - exact - path={`${match.url}/:pageName(${SecurityPageName.detections})/rules`} - /> - <Route - component={RedirectToCreateRulePage} - path={`${match.url}/:pageName(${SecurityPageName.detections})/rules/create`} - /> - <Route - component={RedirectToRuleDetailsPage} - exact - path={`${match.url}/:pageName(${SecurityPageName.detections})/rules/id/:detailName`} - /> - <Route - component={RedirectToEditRulePage} - path={`${match.url}/:pageName(${SecurityPageName.detections})/rules/id/:detailName/edit`} - /> - <Route - component={RedirectToTimelinesPage} - exact - path={`${match.url}/:pageName(${SecurityPageName.timelines})`} - /> - <Route - component={RedirectToTimelinesPage} - path={`${match.url}/:pageName(${SecurityPageName.timelines})/:tabName(${TimelineType.default}|${TimelineType.template})`} - /> - <Route - component={RedirectToManagementPage} - path={`${match.url}/:pageName(${SecurityPageName.management})`} - /> - <Redirect to="/" /> - </Switch> -)); - -LinkToPage.displayName = 'LinkToPage'; diff --git a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_case.tsx b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_case.tsx index 54541de4fc25b..7005460999fc7 100644 --- a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_case.tsx +++ b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_case.tsx @@ -4,43 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { RouteComponentProps } from 'react-router-dom'; import { appendSearch } from './helpers'; -import { RedirectWrapper } from './redirect_wrapper'; -import { SecurityPageName } from '../../../app/types'; -export type CaseComponentProps = RouteComponentProps<{ - detailName: string; -}>; +export const getCaseUrl = (search: string | null) => `${appendSearch(search ?? undefined)}`; -export const RedirectToCasePage = ({ - match: { - params: { detailName }, - }, -}: CaseComponentProps) => ( - <RedirectWrapper - to={detailName ? `/${SecurityPageName.case}/${detailName}` : `/${SecurityPageName.case}`} - /> -); +export const getCaseDetailsUrl = ({ id, search }: { id: string; search?: string | null }) => + `/${encodeURIComponent(id)}${appendSearch(search ?? undefined)}`; -export const RedirectToCreatePage = () => ( - <RedirectWrapper to={`/${SecurityPageName.case}/create`} /> -); -export const RedirectToConfigureCasesPage = () => ( - <RedirectWrapper to={`/${SecurityPageName.case}/configure`} /> -); +export const getCreateCaseUrl = (search?: string | null) => + `/create${appendSearch(search ?? undefined)}`; -const baseCaseUrl = `#/link-to/${SecurityPageName.case}`; - -export const getCaseUrl = (search: string | null) => - `${baseCaseUrl}${appendSearch(search ?? undefined)}`; - -export const getCaseDetailsUrl = ({ id, search }: { id: string; search: string | null }) => - `${baseCaseUrl}/${encodeURIComponent(id)}${appendSearch(search ?? undefined)}`; - -export const getCreateCaseUrl = (search: string | null) => - `${baseCaseUrl}/create${appendSearch(search ?? undefined)}`; - -export const getConfigureCasesUrl = (search: string) => - `${baseCaseUrl}/configure${appendSearch(search ?? undefined)}`; +export const getConfigureCasesUrl = (search?: string) => + `/configure${appendSearch(search ?? undefined)}`; diff --git a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_detection_engine.tsx b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_detection_engine.tsx index e1a021192ab88..530f689c55bbd 100644 --- a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_detection_engine.tsx +++ b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_detection_engine.tsx @@ -4,65 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { RouteComponentProps } from 'react-router-dom'; - import { appendSearch } from './helpers'; -import { RedirectWrapper } from './redirect_wrapper'; - -export type DetectionEngineComponentProps = RouteComponentProps<{ - detailName: string; - search: string; -}>; - -export const DETECTION_ENGINE_PAGE_NAME = 'detections'; - -export const RedirectToDetectionEnginePage = ({ - location: { search }, -}: DetectionEngineComponentProps) => { - const to = `/${DETECTION_ENGINE_PAGE_NAME}${search}`; - - return <RedirectWrapper to={to} />; -}; -export const RedirectToRulesPage = ({ location: { search } }: DetectionEngineComponentProps) => { - return <RedirectWrapper to={`/${DETECTION_ENGINE_PAGE_NAME}/rules${search}`} />; -}; +export const getDetectionEngineUrl = (search?: string) => `${appendSearch(search)}`; -export const RedirectToCreateRulePage = ({ - location: { search }, -}: DetectionEngineComponentProps) => { - return <RedirectWrapper to={`/${DETECTION_ENGINE_PAGE_NAME}/rules/create${search}`} />; -}; +export const getDetectionEngineTabUrl = (tabPath: string) => `/${tabPath}`; -export const RedirectToRuleDetailsPage = ({ - match: { - params: { detailName }, - }, - location: { search }, -}: DetectionEngineComponentProps) => { - return <RedirectWrapper to={`/${DETECTION_ENGINE_PAGE_NAME}/rules/id/${detailName}${search}`} />; -}; +export const getRulesUrl = () => '/rules'; -export const RedirectToEditRulePage = ({ - match: { - params: { detailName }, - }, - location: { search }, -}: DetectionEngineComponentProps) => { - return ( - <RedirectWrapper to={`/${DETECTION_ENGINE_PAGE_NAME}/rules/id/${detailName}/edit${search}`} /> - ); -}; +export const getCreateRuleUrl = () => `/rules/create`; -const baseDetectionEngineUrl = `#/link-to/${DETECTION_ENGINE_PAGE_NAME}`; +export const getRuleDetailsUrl = (detailName: string) => `/rules/id/${detailName}`; -export const getDetectionEngineUrl = (search?: string) => - `${baseDetectionEngineUrl}${appendSearch(search)}`; -export const getDetectionEngineTabUrl = (tabPath: string) => `${baseDetectionEngineUrl}/${tabPath}`; -export const getRulesUrl = () => `${baseDetectionEngineUrl}/rules`; -export const getCreateRuleUrl = () => `${baseDetectionEngineUrl}/rules/create`; -export const getRuleDetailsUrl = (detailName: string) => - `${baseDetectionEngineUrl}/rules/id/${detailName}`; -export const getEditRuleUrl = (detailName: string) => - `${baseDetectionEngineUrl}/rules/id/${detailName}/edit`; +export const getEditRuleUrl = (detailName: string) => `/rules/id/${detailName}/edit`; diff --git a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_hosts.tsx b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_hosts.tsx index 13d627bbfaa49..5af24e5f7ce63 100644 --- a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_hosts.tsx +++ b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_hosts.tsx @@ -4,55 +4,20 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { RouteComponentProps } from 'react-router-dom'; - import { HostsTableType } from '../../../hosts/store/model'; -import { SecurityPageName } from '../../../app/types'; import { appendSearch } from './helpers'; -import { RedirectWrapper } from './redirect_wrapper'; - -export type HostComponentProps = RouteComponentProps<{ - detailName: string; - tabName: HostsTableType; - search: string; -}>; - -export const RedirectToHostsPage = ({ - match: { - params: { tabName }, - }, - location: { search }, -}: HostComponentProps) => { - const defaultSelectedTab = HostsTableType.hosts; - const selectedTab = tabName ? tabName : defaultSelectedTab; - const to = `/${SecurityPageName.hosts}/${selectedTab}${search}`; - - return <RedirectWrapper to={to} />; -}; - -export const RedirectToHostDetailsPage = ({ - match: { - params: { detailName, tabName }, - }, - location: { search }, -}: HostComponentProps) => { - const defaultSelectedTab = HostsTableType.authentications; - const selectedTab = tabName ? tabName : defaultSelectedTab; - const to = `/${SecurityPageName.hosts}/${detailName}/${selectedTab}${search}`; - return <RedirectWrapper to={to} />; -}; - -const baseHostsUrl = `#/link-to/${SecurityPageName.hosts}`; -export const getHostsUrl = (search?: string) => `${baseHostsUrl}${appendSearch(search)}`; +export const getHostsUrl = (search?: string) => `${appendSearch(search)}`; export const getTabsOnHostsUrl = (tabName: HostsTableType, search?: string) => - `${baseHostsUrl}/${tabName}${appendSearch(search)}`; + `/${tabName}${appendSearch(search)}`; -export const getHostDetailsUrl = (detailName: string) => `${baseHostsUrl}/${detailName}`; +export const getHostDetailsUrl = (detailName: string, search?: string) => + `/${detailName}${appendSearch(search)}`; -export const getTabsOnHostDetailsUrl = (detailName: string, tabName: HostsTableType) => { - return `${baseHostsUrl}/${detailName}/${tabName}`; -}; +export const getTabsOnHostDetailsUrl = ( + detailName: string, + tabName: HostsTableType, + search?: string +) => `/${detailName}/${tabName}${appendSearch(search)}`; diff --git a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_management.tsx b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_management.tsx deleted file mode 100644 index f10add0802508..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_management.tsx +++ /dev/null @@ -1,15 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { memo } from 'react'; -import { RedirectWrapper } from './redirect_wrapper'; -import { SecurityPageName } from '../../../app/types'; - -export const RedirectToManagementPage = memo(() => { - return <RedirectWrapper to={`/${SecurityPageName.management}`} />; -}); - -RedirectToManagementPage.displayName = 'RedirectToManagementPage'; diff --git a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_network.tsx b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_network.tsx index 9449606a3459e..8e2b47bd91dbc 100644 --- a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_network.tsx +++ b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_network.tsx @@ -4,39 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { RouteComponentProps } from 'react-router-dom'; - -import { SecurityPageName } from '../../../app/types'; import { FlowTarget, FlowTargetSourceDest } from '../../../graphql/types'; import { appendSearch } from './helpers'; -import { RedirectWrapper } from './redirect_wrapper'; - -export type NetworkComponentProps = RouteComponentProps<{ - detailName?: string; - flowTarget?: string; - search: string; -}>; -export const RedirectToNetworkPage = ({ - match: { - params: { detailName, flowTarget }, - }, - location: { search }, -}: NetworkComponentProps) => ( - <RedirectWrapper - to={ - detailName - ? `/${SecurityPageName.network}/ip/${detailName}/${flowTarget}${search}` - : `/${SecurityPageName.network}${search}` - } - /> -); +export const getNetworkUrl = (search?: string) => `${appendSearch(search)}`; -const baseNetworkUrl = `#/link-to/${SecurityPageName.network}`; -export const getNetworkUrl = (search?: string) => `${baseNetworkUrl}${appendSearch(search)}`; export const getIPDetailsUrl = ( detailName: string, - flowTarget?: FlowTarget | FlowTargetSourceDest -) => `${baseNetworkUrl}/ip/${detailName}/${flowTarget || FlowTarget.source}`; + flowTarget?: FlowTarget | FlowTargetSourceDest, + search?: string +) => `/ip/${detailName}/${flowTarget || FlowTarget.source}${appendSearch(search)}`; diff --git a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_overview.tsx b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_overview.tsx index 4d3c500a4e24a..d0cf46bd90521 100644 --- a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_overview.tsx +++ b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_overview.tsx @@ -4,17 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { RouteComponentProps } from 'react-router-dom'; -import { RedirectWrapper } from './redirect_wrapper'; -import { SecurityPageName } from '../../../app/types'; +import { APP_OVERVIEW_PATH } from '../../../../common/constants'; +import { appendSearch } from './helpers'; -export type OverviewComponentProps = RouteComponentProps<{ - search: string; -}>; - -export const RedirectToOverviewPage = ({ location: { search } }: OverviewComponentProps) => ( - <RedirectWrapper to={`/${SecurityPageName.overview}${search}`} /> -); - -export const getOverviewUrl = () => `#/link-to/${SecurityPageName.overview}`; +export const getAppOverviewUrl = (search?: string) => `${APP_OVERVIEW_PATH}${appendSearch(search)}`; diff --git a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_timelines.tsx b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_timelines.tsx index bfcaddcf92c5d..75a2fa1efa414 100644 --- a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_timelines.tsx +++ b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_timelines.tsx @@ -4,37 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; -import { RouteComponentProps } from 'react-router-dom'; - -import { SecurityPageName } from '../../../app/types'; - +import { TimelineTypeLiteral } from '../../../../common/types/timeline'; import { appendSearch } from './helpers'; -import { RedirectWrapper } from './redirect_wrapper'; -import { TimelineTypeLiteral, TimelineType } from '../../../../common/types/timeline'; - -export type TimelineComponentProps = RouteComponentProps<{ - tabName: TimelineTypeLiteral; - search: string; -}>; - -export const RedirectToTimelinesPage = ({ - match: { - params: { tabName }, - }, - location: { search }, -}: TimelineComponentProps) => ( - <RedirectWrapper - to={ - tabName - ? `/${SecurityPageName.timelines}/${tabName}${search}` - : `/${SecurityPageName.timelines}/${TimelineType.default}${search}` - } - /> -); -export const getTimelinesUrl = (search?: string) => - `#/link-to/${SecurityPageName.timelines}${appendSearch(search)}`; +export const getTimelinesUrl = (search?: string) => `${appendSearch(search)}`; export const getTimelineTabsUrl = (tabName: TimelineTypeLiteral, search?: string) => - `#/link-to/${SecurityPageName.timelines}/${tabName}${appendSearch(search)}`; + `/${tabName}${appendSearch(search)}`; diff --git a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_wrapper.tsx b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_wrapper.tsx deleted file mode 100644 index 39f24e8351f63..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_wrapper.tsx +++ /dev/null @@ -1,18 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { Redirect } from 'react-router-dom'; -import { useScrollToTop } from '../scroll_to_top'; - -export interface RedirectWrapperProps { - to: string; -} - -export const RedirectWrapper = ({ to }: RedirectWrapperProps) => { - useScrollToTop(); - return <Redirect to={to} />; -}; diff --git a/x-pack/plugins/security_solution/public/common/components/links/index.tsx b/x-pack/plugins/security_solution/public/common/components/links/index.tsx index 8a41d4c36edbb..f4e31e3b584ba 100644 --- a/x-pack/plugins/security_solution/public/common/components/links/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/links/index.tsx @@ -4,12 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiLink, EuiToolTip, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import React, { useMemo } from 'react'; +import { + EuiButton, + EuiButtonProps, + EuiLink, + EuiLinkProps, + EuiToolTip, + EuiFlexGroup, + EuiFlexItem, + PropsForAnchor, + PropsForButton, +} from '@elastic/eui'; +import React, { useMemo, useCallback } from 'react'; import { isNil } from 'lodash/fp'; import styled from 'styled-components'; -import { IP_REPUTATION_LINKS_SETTING } from '../../../../common/constants'; +import { IP_REPUTATION_LINKS_SETTING, APP_ID } from '../../../../common/constants'; import { DefaultFieldRendererOverflow, DEFAULT_MORE_MAX_HEIGHT, @@ -20,27 +30,53 @@ import { getHostDetailsUrl, getIPDetailsUrl, getCreateCaseUrl, + useFormatUrl, } from '../link_to'; import { FlowTarget, FlowTargetSourceDest } from '../../../graphql/types'; -import { useUiSetting$ } from '../../lib/kibana'; +import { useUiSetting$, useKibana } from '../../lib/kibana'; import { isUrlInvalid } from '../../utils/validators'; import { ExternalLinkIcon } from '../external_link_icon'; -import { navTabs } from '../../../app/home/home_navigations'; -import { useGetUrlSearch } from '../navigation/use_get_url_search'; import * as i18n from './translations'; +import { SecurityPageName } from '../../../app/types'; export const DEFAULT_NUMBER_OF_LINK = 5; +export const LinkButton: React.FC< + PropsForButton<EuiButtonProps> | PropsForAnchor<EuiButtonProps> +> = ({ children, ...props }) => <EuiButton {...props}>{children}</EuiButton>; + +export const LinkAnchor: React.FC<EuiLinkProps> = ({ children, ...props }) => ( + <EuiLink {...props}>{children}</EuiLink> +); + // Internal Links const HostDetailsLinkComponent: React.FC<{ children?: React.ReactNode; hostName: string }> = ({ children, hostName, -}) => ( - <EuiLink href={getHostDetailsUrl(encodeURIComponent(hostName))}> - {children ? children : hostName} - </EuiLink> -); +}) => { + const { formatUrl, search } = useFormatUrl(SecurityPageName.hosts); + const { navigateToApp } = useKibana().services.application; + const goToHostDetails = useCallback( + (ev) => { + ev.preventDefault(); + navigateToApp(`${APP_ID}:${SecurityPageName.hosts}`, { + path: getHostDetailsUrl(encodeURIComponent(hostName), search), + }); + }, + [navigateToApp, search] + ); + + return ( + <LinkAnchor + onClick={goToHostDetails} + href={formatUrl(getHostDetailsUrl(encodeURIComponent(hostName)))} + > + {children ? children : hostName} + </LinkAnchor> + ); +}; +export const HostDetailsLink = React.memo(HostDetailsLinkComponent); const whitelistUrlSchemes = ['http://', 'https://']; export const ExternalLink = React.memo<{ @@ -75,17 +111,32 @@ export const ExternalLink = React.memo<{ ExternalLink.displayName = 'ExternalLink'; -export const HostDetailsLink = React.memo(HostDetailsLinkComponent); - const IPDetailsLinkComponent: React.FC<{ children?: React.ReactNode; ip: string; flowTarget?: FlowTarget | FlowTargetSourceDest; -}> = ({ children, ip, flowTarget = FlowTarget.source }) => ( - <EuiLink href={`${getIPDetailsUrl(encodeURIComponent(encodeIpv6(ip)), flowTarget)}`}> - {children ? children : ip} - </EuiLink> -); +}> = ({ children, ip, flowTarget = FlowTarget.source }) => { + const { formatUrl, search } = useFormatUrl(SecurityPageName.network); + const { navigateToApp } = useKibana().services.application; + const goToNetworkDetails = useCallback( + (ev) => { + ev.preventDefault(); + navigateToApp(`${APP_ID}:${SecurityPageName.network}`, { + path: getIPDetailsUrl(encodeURIComponent(encodeIpv6(ip)), flowTarget, search), + }); + }, + [navigateToApp, search] + ); + + return ( + <LinkAnchor + onClick={goToNetworkDetails} + href={formatUrl(getIPDetailsUrl(encodeURIComponent(encodeIpv6(ip))))} + > + {children ? children : ip} + </LinkAnchor> + ); +}; export const IPDetailsLink = React.memo(IPDetailsLinkComponent); @@ -94,24 +145,49 @@ const CaseDetailsLinkComponent: React.FC<{ detailName: string; title?: string; }> = ({ children, detailName, title }) => { - const search = useGetUrlSearch(navTabs.case); + const { formatUrl, search } = useFormatUrl(SecurityPageName.case); + const { navigateToApp } = useKibana().services.application; + const goToCaseDetails = useCallback( + (ev) => { + ev.preventDefault(); + navigateToApp(`${APP_ID}:${SecurityPageName.case}`, { + path: getCaseDetailsUrl({ id: detailName, search }), + }); + }, + [navigateToApp, search] + ); return ( - <EuiLink - href={getCaseDetailsUrl({ id: detailName, search })} + <LinkAnchor + onClick={goToCaseDetails} + href={formatUrl(getCaseDetailsUrl({ id: detailName }))} data-test-subj="case-details-link" aria-label={i18n.CASE_DETAILS_LINK_ARIA(title ?? detailName)} > {children ? children : detailName} - </EuiLink> + </LinkAnchor> ); }; export const CaseDetailsLink = React.memo(CaseDetailsLinkComponent); CaseDetailsLink.displayName = 'CaseDetailsLink'; export const CreateCaseLink = React.memo<{ children: React.ReactNode }>(({ children }) => { - const search = useGetUrlSearch(navTabs.case); - return <EuiLink href={getCreateCaseUrl(search)}>{children}</EuiLink>; + const { formatUrl, search } = useFormatUrl(SecurityPageName.case); + const { navigateToApp } = useKibana().services.application; + const goToCreateCase = useCallback( + (ev) => { + ev.preventDefault(); + navigateToApp(`${APP_ID}:${SecurityPageName.case}`, { + path: getCreateCaseUrl(search), + }); + }, + [navigateToApp, search] + ); + return ( + <LinkAnchor onClick={goToCreateCase} href={formatUrl(getCreateCaseUrl())}> + {children} + </LinkAnchor> + ); }); CreateCaseLink.displayName = 'CreateCaseLink'; diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.ts b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.ts index 50ce984e55525..a18d04fa65d7d 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.ts +++ b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.ts @@ -22,7 +22,7 @@ import { NetworkRouteSpyState, TimelineRouteSpyState, } from '../../../utils/route/types'; -import { getOverviewUrl } from '../../link_to'; +import { getAppOverviewUrl } from '../../link_to'; import { TabNavigationProps } from '../tab_navigation/types'; import { getSearch } from '../helpers'; @@ -41,7 +41,7 @@ export const setBreadcrumbs = ( export const siemRootBreadcrumb: ChromeBreadcrumb[] = [ { text: APP_NAME, - href: getOverviewUrl(), + href: getAppOverviewUrl(), }, ]; @@ -57,8 +57,8 @@ const isTimelinesRoutes = (spyState: RouteSpyState): spyState is TimelineRouteSp const isCaseRoutes = (spyState: RouteSpyState): spyState is RouteSpyState => spyState != null && spyState.pageName === SecurityPageName.case; -const isDetectionsRoutes = (spyState: RouteSpyState) => - spyState != null && spyState.pageName === SecurityPageName.detections; +const isAlertsRoutes = (spyState: RouteSpyState) => + spyState != null && spyState.pageName === SecurityPageName.alerts; export const getBreadcrumbsForRoute = ( object: RouteSpyState & TabNavigationProps @@ -98,8 +98,8 @@ export const getBreadcrumbsForRoute = ( ), ]; } - if (isDetectionsRoutes(spyState) && object.navTabs) { - const tempNav: SearchNavTab = { urlKey: 'detections', isDetailPage: false }; + if (isAlertsRoutes(spyState) && object.navTabs) { + const tempNav: SearchNavTab = { urlKey: 'alerts', isDetailPage: false }; let urlStateKeys = [getOr(tempNav, spyState.pageName, object.navTabs)]; if (spyState.tabName != null) { urlStateKeys = [...urlStateKeys, getOr(tempNav, spyState.tabName, object.navTabs)]; diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.tsx index 15dff1ca82d81..351fa810ca0e6 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.tsx @@ -6,30 +6,49 @@ import { EuiTab, EuiTabs } from '@elastic/eui'; import { getOr } from 'lodash/fp'; import React, { useEffect, useState, useCallback, useMemo } from 'react'; +import { useHistory } from 'react-router-dom'; +import { APP_ID } from '../../../../../common/constants'; import { track, METRIC_TYPE, TELEMETRY_EVENT } from '../../../lib/telemetry'; import { getSearch } from '../helpers'; import { TabNavigationProps, TabNavigationItemProps } from './types'; +import { useKibana } from '../../../lib/kibana'; +import { SecurityPageName } from '../../../../app/types'; +import { useFormatUrl } from '../../link_to'; const TabNavigationItemComponent = ({ + disabled, href, hrefWithSearch, id, - disabled, name, isSelected, + pageId, + urlSearch, }: TabNavigationItemProps) => { - const handleClick = useCallback(() => { - track(METRIC_TYPE.CLICK, `${TELEMETRY_EVENT.TAB_CLICKED}${id}`); - }, [id]); - + const history = useHistory(); + const { navigateToApp } = useKibana().services.application; + const { formatUrl } = useFormatUrl(((pageId ?? id) as unknown) as SecurityPageName); + const handleClick = useCallback( + (ev) => { + ev.preventDefault(); + if (id in SecurityPageName) { + navigateToApp(`${APP_ID}:${id}`, { path: urlSearch }); + } else { + history.push(hrefWithSearch); + } + track(METRIC_TYPE.CLICK, `${TELEMETRY_EVENT.TAB_CLICKED}${id}`); + }, + [history, hrefWithSearch, id, navigateToApp, urlSearch] + ); + const appHref = formatUrl(pageId != null ? href : ''); return ( <EuiTab - data-href={href} + data-href={appHref} data-test-subj={`navigation-${id}`} disabled={disabled} - href={hrefWithSearch} isSelected={isSelected} + href={appHref} onClick={handleClick} > {name} @@ -67,6 +86,7 @@ export const TabNavigationComponent = (props: TabNavigationProps) => { Object.values(navTabs).map((tab) => { const isSelected = selectedTabId === tab.id; const { query, filters, savedQuery, timerange, timeline } = props; + const search = getSearch(tab, { query, filters, savedQuery, timerange, timeline }); const hrefWithSearch = tab.href + getSearch(tab, { query, filters, savedQuery, timerange, timeline }); @@ -75,10 +95,12 @@ export const TabNavigationComponent = (props: TabNavigationProps) => { key={`navigation-${tab.id}`} id={tab.id} href={tab.href} + hrefWithSearch={hrefWithSearch} name={tab.name} disabled={tab.disabled} - hrefWithSearch={hrefWithSearch} + pageId={tab.pageId} isSelected={isSelected} + urlSearch={search} /> ); }), diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/types.ts b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/types.ts index a283691cfe0df..7f72e37c74d56 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/types.ts @@ -30,4 +30,6 @@ export interface TabNavigationItemProps { disabled: boolean; name: string; isSelected: boolean; + urlSearch: string; + pageId?: string; } 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 f0256813c29e7..07aca1e6c9c6e 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 @@ -9,6 +9,7 @@ import { HostsTableType } from '../../../hosts/store/model'; import { UrlInputsModel } from '../../store/inputs/model'; import { TimelineUrl } from '../../../timelines/store/timeline/model'; import { CONSTANTS, UrlStateType } from '../url_state/constants'; +import { SecurityPageName } from '../../../app/types'; export interface SiemNavigationProps { display?: 'default' | 'condensed'; @@ -37,4 +38,5 @@ export interface NavTab { disabled: boolean; urlKey: UrlStateType; isDetailPage?: boolean; + pageId?: SecurityPageName; } diff --git a/x-pack/plugins/security_solution/public/common/components/news_feed/no_news/index.tsx b/x-pack/plugins/security_solution/public/common/components/news_feed/no_news/index.tsx index 8061a8f9799e3..d626433de1b63 100644 --- a/x-pack/plugins/security_solution/public/common/components/news_feed/no_news/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/news_feed/no_news/index.tsx @@ -8,17 +8,21 @@ import { EuiLink, EuiText } from '@elastic/eui'; import React from 'react'; import * as i18n from '../translations'; +import { useBasePath } from '../../../lib/kibana'; -export const NoNews = React.memo(() => ( - <> - <EuiText color="subdued" size="s"> - {i18n.NO_NEWS_MESSAGE}{' '} - <EuiLink href={'/app/management/kibana/settings'}> - {i18n.ADVANCED_SETTINGS_LINK_TITLE} - </EuiLink> - {'.'} - </EuiText> - </> -)); +export const NoNews = React.memo(() => { + const basePath = useBasePath(); + return ( + <> + <EuiText color="subdued" size="s"> + {i18n.NO_NEWS_MESSAGE}{' '} + <EuiLink href={`${basePath}/app/management/kibana/settings`}> + {i18n.ADVANCED_SETTINGS_LINK_TITLE} + </EuiLink> + {'.'} + </EuiText> + </> + ); +}); NoNews.displayName = 'NoNews'; 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 index 1faff2594ce80..71faec88e85a0 100644 --- 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 @@ -8,7 +8,7 @@ export enum CONSTANTS { appQuery = 'query', caseDetails = 'case.details', casePage = 'case.page', - detectionsPage = 'detections.page', + alertsPage = 'alerts.page', filters = 'filters', hostsDetails = 'hosts.details', hostsPage = 'hosts.page', @@ -25,7 +25,7 @@ export enum CONSTANTS { export type UrlStateType = | 'case' - | 'detections' + | 'alerts' | 'host' | 'network' | 'overview' diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts b/x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts index 338b3cd088665..c270a99d3c51e 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts +++ b/x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts @@ -90,8 +90,8 @@ export const getUrlType = (pageName: string): UrlStateType => { return 'host'; } else if (pageName === SecurityPageName.network) { return 'network'; - } else if (pageName === SecurityPageName.detections) { - return 'detections'; + } else if (pageName === SecurityPageName.alerts) { + return 'alerts'; } else if (pageName === SecurityPageName.timelines) { return 'timeline'; } else if (pageName === SecurityPageName.case) { @@ -114,19 +114,20 @@ export const makeMapStateToProps = () => { const getGlobalQuerySelector = inputsSelectors.globalQuerySelector(); const getGlobalFiltersQuerySelector = inputsSelectors.globalFiltersQuerySelector(); const getGlobalSavedQuerySelector = inputsSelectors.globalSavedQuerySelector(); - const getTimelines = timelineSelectors.getTimelines(); + const getTimeline = timelineSelectors.getTimelineByIdSelector(); const mapStateToProps = (state: State) => { const inputState = getInputsSelector(state); const { linkTo: globalLinkTo, timerange: globalTimerange } = inputState.global; const { linkTo: timelineLinkTo, timerange: timelineTimerange } = inputState.timeline; - const timeline = Object.entries(getTimelines(state)).reduce( - (obj, [timelineId, timelineObj]) => ({ - id: timelineObj.savedObjectId != null ? timelineObj.savedObjectId : '', - isOpen: timelineObj.show, - }), - { id: '', isOpen: false } - ); + const flyoutTimeline = getTimeline(state, 'timeline-1'); + const timeline = + flyoutTimeline != null + ? { + id: flyoutTimeline.savedObjectId != null ? flyoutTimeline.savedObjectId : '', + isOpen: flyoutTimeline.show, + } + : { id: '', isOpen: false }; let searchAttr: { [CONSTANTS.appQuery]?: Query; 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 index eeeaacc25a15e..20374affbdf89 100644 --- 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 @@ -193,7 +193,7 @@ describe('UrlStateContainer', () => { wrapper.update(); await wait(); - if (CONSTANTS.detectionsPage === page) { + if (CONSTANTS.alertsPage === page) { expect(mockSetRelativeRangeDatePicker.mock.calls[3][0]).toEqual({ from: 11223344556677, fromStr: 'now-1d/d', 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 index 8881a82e5cd1c..8ca43cb576d32 100644 --- 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 @@ -32,7 +32,7 @@ export const ALL_URL_STATE_KEYS: KeyUrlState[] = [ ]; export const URL_STATE_KEYS: Record<UrlStateType, KeyUrlState[]> = { - detections: [ + alerts: [ CONSTANTS.appQuery, CONSTANTS.filters, CONSTANTS.savedQuery, @@ -80,7 +80,7 @@ export const URL_STATE_KEYS: Record<UrlStateType, KeyUrlState[]> = { export type LocationTypes = | CONSTANTS.caseDetails | CONSTANTS.casePage - | CONSTANTS.detectionsPage + | CONSTANTS.alertsPage | CONSTANTS.hostsDetails | CONSTANTS.hostsPage | CONSTANTS.networkDetails diff --git a/x-pack/plugins/security_solution/public/common/utils/route/index.test.tsx b/x-pack/plugins/security_solution/public/common/utils/route/index.test.tsx index 95e40b0f66301..7246259f5afa1 100644 --- a/x-pack/plugins/security_solution/public/common/utils/route/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/utils/route/index.test.tsx @@ -71,13 +71,13 @@ describe('Spy Routes', () => { path: pathname, url: pathname, params: { - pageName: undefined, detailName: '', tabName: HostsTableType.hosts, search: '', flowTarget: undefined, }, }} + pageName={undefined} /> </ManageRoutesSpy> ); @@ -103,13 +103,13 @@ describe('Spy Routes', () => { path: pathname, url: pathname, params: { - pageName: 'hosts', detailName: undefined, tabName: HostsTableType.hosts, search: '?IdoNotWantToSeeYou="true"', flowTarget: undefined, }, }} + pageName="hosts" /> </ManageRoutesSpy> ); @@ -124,9 +124,9 @@ describe('Spy Routes', () => { expect(dispatchMock.mock.calls[1]).toEqual([ { route: { + pageName: 'hosts', detailName: undefined, history: mockHistory, - pageName: 'hosts', pathName: pathname, tabName: HostsTableType.hosts, }, @@ -154,13 +154,13 @@ describe('Spy Routes', () => { path: pathname, url: pathname, params: { - pageName: 'hosts', detailName: undefined, tabName: HostsTableType.hosts, search: '?IdoNotWantToSeeYou="true"', flowTarget: undefined, }, }} + pageName="hosts" /> ); From 5042843c43c9bd6e7e5f5782443088ac8e2f9e42 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Wed, 10 Jun 2020 22:21:49 -0400 Subject: [PATCH 11/43] some fixing from the first commit --- .../security_solution/common/constants.ts | 8 +++ .../public/app/home/home_navigations.tsx | 45 +++++++++-------- .../public/app/home/index.tsx | 8 --- .../public/app/home/translations.ts | 4 ++ .../security_solution/public/plugin.tsx | 50 ++++++++++++------- 5 files changed, 66 insertions(+), 49 deletions(-) diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index b5cd8f2dec0a3..aa4a4f2d41f24 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -31,6 +31,14 @@ export const DEFAULT_INTERVAL_TYPE = 'manual'; export const DEFAULT_INTERVAL_VALUE = 300000; // ms export const DEFAULT_TIMEPICKER_QUICK_RANGES = 'timepicker:quickRanges'; +export const APP_OVERVIEW_PATH = `${APP_PATH}/overview`; +export const APP_ALERTS_PATH = `${APP_PATH}/alerts`; +export const APP_HOSTS_PATH = `${APP_PATH}/hosts`; +export const APP_NETWORK_PATH = `${APP_PATH}/network`; +export const APP_TIMELINES_PATH = `${APP_PATH}/timelines`; +export const APP_CASES_PATH = `${APP_PATH}/cases`; +export const APP_MANAGEMENT_PATH = `${APP_PATH}/management`; + /** The comma-delimited list of Elasticsearch indices from which the SIEM app collects events */ export const DEFAULT_INDEX_PATTERN = [ 'apm-*-transaction*', diff --git a/x-pack/plugins/security_solution/public/app/home/home_navigations.tsx b/x-pack/plugins/security_solution/public/app/home/home_navigations.tsx index 03bb2c4d90418..501e371a97b70 100644 --- a/x-pack/plugins/security_solution/public/app/home/home_navigations.tsx +++ b/x-pack/plugins/security_solution/public/app/home/home_navigations.tsx @@ -4,65 +4,66 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - getDetectionEngineUrl, - getOverviewUrl, - getNetworkUrl, - getTimelinesUrl, - getHostsUrl, - getCaseUrl, -} from '../../common/components/link_to'; import * as i18n from './translations'; import { SecurityPageName, SiemNavTab } from '../types'; -import { getManagementUrl } from '../../management'; +import { + APP_OVERVIEW_PATH, + APP_ALERTS_PATH, + APP_HOSTS_PATH, + APP_NETWORK_PATH, + APP_TIMELINES_PATH, + APP_CASES_PATH, + APP_MANAGEMENT_PATH, +} from '../../../common/constants'; export const navTabs: SiemNavTab = { [SecurityPageName.overview]: { id: SecurityPageName.overview, name: i18n.OVERVIEW, - href: getOverviewUrl(), + href: APP_OVERVIEW_PATH, disabled: false, urlKey: 'overview', }, + [SecurityPageName.alerts]: { + id: SecurityPageName.alerts, + name: i18n.Alerts, + href: APP_ALERTS_PATH, + disabled: false, + urlKey: 'alerts', + }, [SecurityPageName.hosts]: { id: SecurityPageName.hosts, name: i18n.HOSTS, - href: getHostsUrl(), + href: APP_HOSTS_PATH, disabled: false, urlKey: 'host', }, [SecurityPageName.network]: { id: SecurityPageName.network, name: i18n.NETWORK, - href: getNetworkUrl(), + href: APP_NETWORK_PATH, disabled: false, urlKey: 'network', }, - [SecurityPageName.detections]: { - id: SecurityPageName.detections, - name: i18n.DETECTION_ENGINE, - href: getDetectionEngineUrl(), - disabled: false, - urlKey: 'detections', - }, + [SecurityPageName.timelines]: { id: SecurityPageName.timelines, name: i18n.TIMELINES, - href: getTimelinesUrl(), + href: APP_TIMELINES_PATH, disabled: false, urlKey: 'timeline', }, [SecurityPageName.case]: { id: SecurityPageName.case, name: i18n.CASE, - href: getCaseUrl(null), + href: APP_CASES_PATH, disabled: false, urlKey: 'case', }, [SecurityPageName.management]: { id: SecurityPageName.management, name: i18n.MANAGEMENT, - href: getManagementUrl({ name: 'default' }), + href: APP_MANAGEMENT_PATH, disabled: false, urlKey: SecurityPageName.management, }, diff --git a/x-pack/plugins/security_solution/public/app/home/index.tsx b/x-pack/plugins/security_solution/public/app/home/index.tsx index 40e0ef7ab328a..3eeb33a763911 100644 --- a/x-pack/plugins/security_solution/public/app/home/index.tsx +++ b/x-pack/plugins/security_solution/public/app/home/index.tsx @@ -5,7 +5,6 @@ */ import React, { useMemo } from 'react'; -import { Redirect, Route, Switch } from 'react-router-dom'; import styled from 'styled-components'; import { useThrottledResizeObserver } from '../../common/components/utils'; @@ -13,7 +12,6 @@ import { DragDropContextWrapper } from '../../common/components/drag_and_drop/dr import { Flyout } from '../../timelines/components/flyout'; import { HeaderGlobal } from '../../common/components/header_global'; import { HelpMenu } from '../../common/components/help_menu'; -import { LinkToPage } from '../../common/components/link_to'; import { MlHostConditionalContainer } from '../../common/components/ml/conditional_links/ml_host_conditional_container'; import { MlNetworkConditionalContainer } from '../../common/components/ml/conditional_links/ml_network_conditional_container'; import { AutoSaveWarningMsg } from '../../timelines/components/timeline/auto_save_warning'; @@ -22,9 +20,7 @@ import { WithSource, indicesExistOrDataTemporarilyUnavailable, } from '../../common/containers/source'; -import { SpyRoute } from '../../common/utils/route/spy_routes'; import { useShowTimeline } from '../../common/utils/timeline/use_show_timeline'; -import { NotFoundPage } from '../404'; import { navTabs } from './home_navigations'; import { SecurityPageName } from '../types'; @@ -90,8 +86,6 @@ export const HomePage: React.FC<HomePageProps> = ({ children }) => { {children} {/* <Switch> - <Redirect exact from="/" to={`/${SecurityPageName.overview}`} /> - <Route path="/link-to" render={(props) => <LinkToPage {...props} />} /> <Route path="/ml-hosts" render={({ location, match }) => ( @@ -112,8 +106,6 @@ export const HomePage: React.FC<HomePageProps> = ({ children }) => { </Main> <HelpMenu /> - - <SpyRoute /> </WrappedByAutoSizer> ); }; diff --git a/x-pack/plugins/security_solution/public/app/home/translations.ts b/x-pack/plugins/security_solution/public/app/home/translations.ts index ccf927eba20c9..f5a08e6395f1f 100644 --- a/x-pack/plugins/security_solution/public/app/home/translations.ts +++ b/x-pack/plugins/security_solution/public/app/home/translations.ts @@ -25,6 +25,10 @@ export const DETECTION_ENGINE = i18n.translate( } ); +export const Alerts = i18n.translate('xpack.securitySolution.navigation.alerts', { + defaultMessage: 'Alerts', +}); + export const TIMELINES = i18n.translate('xpack.securitySolution.navigation.timelines', { defaultMessage: 'Timelines', }); diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index 4e959ebf61875..27dd2e219f5cc 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -22,10 +22,22 @@ import { initTelemetry } from './common/lib/telemetry'; import { KibanaServices } from './common/lib/kibana/services'; import { serviceNowActionType, jiraActionType } from './common/lib/connectors'; import { PluginSetup, PluginStart, SetupPlugins, StartPlugins, StartServices } from './types'; -import { APP_ID, APP_ICON, APP_PATH, APP_ALERT_PATH } from '../common/constants'; +import { + APP_ID, + APP_ICON, + APP_PATH, + APP_ALERTS_PATH, + APP_HOSTS_PATH, + APP_OVERVIEW_PATH, + APP_NETWORK_PATH, + APP_TIMELINES_PATH, + APP_MANAGEMENT_PATH, + APP_CASES_PATH, +} from '../common/constants'; import { ConfigureEndpointDatasource } from './management/pages/policy/view/ingest_manager_integration/configure_datasource'; import { State, createStore, createInitialState } from './common/store'; +import { SecurityPageName } from './app/types'; export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, StartPlugins> { private kibanaVersion: string; @@ -70,14 +82,14 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S }; core.application.register({ - id: `${APP_ID}:overview`, + id: `${APP_ID}:${SecurityPageName.overview}`, title: i18n.translate('xpack.securitySolution.overviewPage.title', { defaultMessage: 'Overview', }), order: 9000, euiIconType: APP_ICON, category: DEFAULT_APP_CATEGORIES.security, - appRoute: `${APP_PATH}/overview`, + appRoute: APP_OVERVIEW_PATH, mount: async (params: AppMountParameters) => { const [ { coreStart, store, services }, @@ -99,14 +111,14 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S }); core.application.register({ - id: `${APP_ID}:alerts`, + id: `${APP_ID}:${SecurityPageName.alerts}`, title: i18n.translate('xpack.securitySolution.alertsPage.title', { defaultMessage: 'Alerts', }), order: 9001, euiIconType: APP_ICON, category: DEFAULT_APP_CATEGORIES.security, - appRoute: APP_ALERT_PATH, + appRoute: APP_ALERTS_PATH, mount: async (params: AppMountParameters) => { const [ { coreStart, store, services }, @@ -128,12 +140,12 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S }); core.application.register({ - id: `${APP_ID}:hosts`, + id: `${APP_ID}:${SecurityPageName.hosts}`, title: 'Hosts', order: 9002, euiIconType: APP_ICON, category: DEFAULT_APP_CATEGORIES.security, - appRoute: `${APP_PATH}/hosts`, + appRoute: APP_HOSTS_PATH, mount: async (params: AppMountParameters) => { const [ { coreStart, store, services }, @@ -155,12 +167,12 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S }); core.application.register({ - id: `${APP_ID}:network`, + id: `${APP_ID}:${SecurityPageName.network}`, title: 'Network', order: 9002, euiIconType: APP_ICON, category: DEFAULT_APP_CATEGORIES.security, - appRoute: `${APP_PATH}/network`, + appRoute: APP_NETWORK_PATH, mount: async (params: AppMountParameters) => { const [ { coreStart, store, services }, @@ -182,17 +194,17 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S }); core.application.register({ - id: `${APP_ID}:timelines`, + id: `${APP_ID}:${SecurityPageName.timelines}`, title: 'Timelines', order: 9002, euiIconType: APP_ICON, category: DEFAULT_APP_CATEGORIES.security, - appRoute: `${APP_PATH}/timelines`, + appRoute: APP_TIMELINES_PATH, mount: async (params: AppMountParameters) => { const [ { coreStart, store, services }, { renderApp, composeLibs }, - { networkSubPlugin }, + { timelinesSubPlugin }, ] = await Promise.all([ mountSecurityFactory(), this.downloadAssets(), @@ -203,23 +215,23 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S ...params, services, store, - SubPluginRoutes: networkSubPlugin.start().SubPluginRoutes, + SubPluginRoutes: timelinesSubPlugin.start().SubPluginRoutes, }); }, }); core.application.register({ - id: `${APP_ID}:cases`, + id: `${APP_ID}:${SecurityPageName.case}`, title: 'Cases', order: 9002, euiIconType: APP_ICON, category: DEFAULT_APP_CATEGORIES.security, - appRoute: `${APP_PATH}/cases`, + appRoute: APP_CASES_PATH, mount: async (params: AppMountParameters) => { const [ { coreStart, store, services }, { renderApp, composeLibs }, - { networkSubPlugin }, + { casesSubPlugin }, ] = await Promise.all([ mountSecurityFactory(), this.downloadAssets(), @@ -230,18 +242,18 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S ...params, services, store, - SubPluginRoutes: networkSubPlugin.start().SubPluginRoutes, + SubPluginRoutes: casesSubPlugin.start().SubPluginRoutes, }); }, }); core.application.register({ - id: `${APP_ID}:management`, + id: `${APP_ID}:${SecurityPageName.management}`, title: 'Management', order: 9002, euiIconType: APP_ICON, category: DEFAULT_APP_CATEGORIES.security, - appRoute: `${APP_PATH}/management`, + appRoute: APP_MANAGEMENT_PATH, mount: async (params: AppMountParameters) => { const [ { coreStart, startPlugins, store, services }, From d59b7ddff8534e5b49aaee04c750fe50209e0020 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Thu, 11 Jun 2020 11:32:42 -0400 Subject: [PATCH 12/43] modify route for management --- .../public/management/common/routing.ts | 113 +++--------------- .../components/management_page_view.tsx | 35 +++++- 2 files changed, 47 insertions(+), 101 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/common/routing.ts b/x-pack/plugins/security_solution/public/management/common/routing.ts index 8eb97deb3e2fe..c137f8578cde2 100644 --- a/x-pack/plugins/security_solution/public/management/common/routing.ts +++ b/x-pack/plugins/security_solution/public/management/common/routing.ts @@ -6,104 +6,27 @@ import { generatePath } from 'react-router-dom'; // eslint-disable-next-line import/no-nodejs-modules -import querystring from 'querystring'; + import { MANAGEMENT_ROUTING_ENDPOINTS_PATH, MANAGEMENT_ROUTING_POLICIES_PATH, MANAGEMENT_ROUTING_POLICY_DETAILS_PATH, - MANAGEMENT_ROUTING_ROOT_PATH, } from './constants'; import { ManagementSubTab } from '../types'; -import { SecurityPageName } from '../../app/types'; -import { HostIndexUIQueryParams } from '../pages/endpoint_hosts/types'; - -// Taken from: https://github.com/microsoft/TypeScript/issues/12936#issuecomment-559034150 -type ExactKeys<T1, T2> = Exclude<keyof T1, keyof T2> extends never ? T1 : never; -type Exact<T, Shape> = T extends Shape ? ExactKeys<T, Shape> : never; - -/** - * Returns a string to be used in the URL as search query params. - * Ensures that when creating a URL query param string, that the given input strictly - * matches the expected interface (guards against possibly leaking internal state) - */ -const querystringStringify: <ExpectedType extends object, ArgType>( - params: Exact<ExpectedType, ArgType> -) => string = querystring.stringify; - -/** Make `selected_host` required */ -type EndpointDetailsUrlProps = Omit<HostIndexUIQueryParams, 'selected_host'> & - Required<Pick<HostIndexUIQueryParams, 'selected_host'>>; - -/** - * Input props for the `getManagementUrl()` method - */ -export type GetManagementUrlProps = { - /** - * Exclude the URL prefix (everything to the left of where the router was mounted. - * This may be needed when interacting with react-router (ex. to do `history.push()` or - * validations against matched path) - */ - excludePrefix?: boolean; -} & ( - | ({ name: 'default' | 'endpointList' } & HostIndexUIQueryParams) - | ({ name: 'endpointDetails' | 'endpointPolicyResponse' } & EndpointDetailsUrlProps) - | { name: 'policyList' } - | { name: 'policyDetails'; policyId: string } -); - -// Prefix is (almost) everything to the left of where the Router was mounted. In SIEM, since -// we're using Hash router, thats the `#`. -const URL_PREFIX = '#'; - -/** - * Returns a URL string for a given Management page view - * @param props - */ -export const getManagementUrl = (props: GetManagementUrlProps): string => { - let url = props.excludePrefix ? '' : URL_PREFIX; - - if (props.name === 'default' || props.name === 'endpointList') { - const { name, excludePrefix, ...queryParams } = props; - const urlQueryParams = querystringStringify<HostIndexUIQueryParams, typeof queryParams>( - queryParams - ); - - if (name === 'endpointList') { - url += generatePath(MANAGEMENT_ROUTING_ENDPOINTS_PATH, { - pageName: SecurityPageName.management, - tabName: ManagementSubTab.endpoints, - }); - } else { - url += generatePath(MANAGEMENT_ROUTING_ROOT_PATH, { - pageName: SecurityPageName.management, - }); - } - - if (urlQueryParams) { - url += `?${urlQueryParams}`; - } - } else if (props.name === 'endpointDetails' || props.name === 'endpointPolicyResponse') { - const { name, excludePrefix, ...queryParams } = props; - queryParams.show = (props.name === 'endpointPolicyResponse' - ? 'policy_response' - : '') as HostIndexUIQueryParams['show']; - - url += `${generatePath(MANAGEMENT_ROUTING_ENDPOINTS_PATH, { - pageName: SecurityPageName.management, - tabName: ManagementSubTab.endpoints, - })}?${querystringStringify<EndpointDetailsUrlProps, typeof queryParams>(queryParams)}`; - } else if (props.name === 'policyList') { - url += generatePath(MANAGEMENT_ROUTING_POLICIES_PATH, { - pageName: SecurityPageName.management, - tabName: ManagementSubTab.policies, - }); - } else if (props.name === 'policyDetails') { - url += generatePath(MANAGEMENT_ROUTING_POLICY_DETAILS_PATH, { - pageName: SecurityPageName.management, - tabName: ManagementSubTab.policies, - policyId: props.policyId, - }); - } - - return url; -}; +import { appendSearch } from '../../common/components/link_to/helpers'; + +export const getEndpointPath = (search?: string) => + `/${generatePath(MANAGEMENT_ROUTING_ENDPOINTS_PATH, { + tabName: ManagementSubTab.endpoints, + })}${appendSearch(search ?? undefined)}`; + +export const getPoliciesPath = (search?: string) => + `/${generatePath(MANAGEMENT_ROUTING_POLICIES_PATH, { + tabName: ManagementSubTab.policies, + })}${appendSearch(search ?? undefined)}`; + +export const getPolicyDetailPath = (policyId: string, search?: string) => + `/${generatePath(MANAGEMENT_ROUTING_POLICY_DETAILS_PATH, { + tabName: ManagementSubTab.policies, + policyId, + })}${appendSearch(search ?? undefined)}`; diff --git a/x-pack/plugins/security_solution/public/management/components/management_page_view.tsx b/x-pack/plugins/security_solution/public/management/components/management_page_view.tsx index 8f7e0e06fb7a1..c65b512c51811 100644 --- a/x-pack/plugins/security_solution/public/management/components/management_page_view.tsx +++ b/x-pack/plugins/security_solution/public/management/components/management_page_view.tsx @@ -4,15 +4,36 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { memo, useMemo } from 'react'; +import React, { memo, useCallback, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; -import { useParams } from 'react-router-dom'; +import { useParams, useHistory } from 'react-router-dom'; import { PageView, PageViewProps } from '../../common/components/endpoint/page_view'; import { ManagementSubTab } from '../types'; -import { getManagementUrl } from '..'; +import { SecurityPageName } from '../../app/types'; +import { useFormatUrl } from '../../common/components/link_to'; +import { getEndpointPath, getPoliciesPath } from '../common/routing'; export const ManagementPageView = memo<Omit<PageViewProps, 'tabs'>>((options) => { + const history = useHistory(); + const { formatUrl } = useFormatUrl(SecurityPageName.management); const { tabName } = useParams<{ tabName: ManagementSubTab }>(); + + const goToEndpoint = useCallback( + (ev) => { + ev.preventDefault(); + history.push(getEndpointPath()); + }, + [history] + ); + + const goToPolicies = useCallback( + (ev) => { + ev.preventDefault(); + history.push(getPoliciesPath()); + }, + [history] + ); + const tabs = useMemo((): PageViewProps['tabs'] | undefined => { if (options.viewType === 'details') { return undefined; @@ -24,7 +45,8 @@ export const ManagementPageView = memo<Omit<PageViewProps, 'tabs'>>((options) => }), id: ManagementSubTab.endpoints, isSelected: tabName === ManagementSubTab.endpoints, - href: getManagementUrl({ name: 'endpointList' }), + href: formatUrl(getEndpointPath()), + onClick: goToEndpoint, }, { name: i18n.translate('xpack.securitySolution.managementTabs.policies', { @@ -32,10 +54,11 @@ export const ManagementPageView = memo<Omit<PageViewProps, 'tabs'>>((options) => }), id: ManagementSubTab.policies, isSelected: tabName === ManagementSubTab.policies, - href: getManagementUrl({ name: 'policyList' }), + href: formatUrl(getPoliciesPath()), + onClick: goToPolicies, }, ]; - }, [options.viewType, tabName]); + }, [formatUrl, goToEndpoint, goToPolicies, options.viewType, tabName]); return <PageView {...options} tabs={tabs} />; }); From 83558d114789f5a40dfa01eb20b66be7e56c7fe3 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Thu, 11 Jun 2020 15:29:43 -0400 Subject: [PATCH 13/43] update url format hook to use history --- .../public/common/components/link_to/index.ts | 49 +++---------------- .../navigation/tab_navigation/index.tsx | 4 +- .../components/management_page_view.tsx | 10 ++-- 3 files changed, 14 insertions(+), 49 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/link_to/index.ts b/x-pack/plugins/security_solution/public/common/components/link_to/index.ts index 06ef26cef3743..c427a131443b0 100644 --- a/x-pack/plugins/security_solution/public/common/components/link_to/index.ts +++ b/x-pack/plugins/security_solution/public/common/components/link_to/index.ts @@ -5,19 +5,10 @@ */ import { useCallback } from 'react'; -import { useBasePath } from '../../lib/kibana'; +import { useHistory } from 'react-router-dom'; import { SecurityPageName } from '../../../app/types'; import { useGetUrlSearch } from '../navigation/use_get_url_search'; import { navTabs } from '../../../app/home/home_navigations'; -import { - APP_ALERTS_PATH, - APP_CASES_PATH, - APP_HOSTS_PATH, - APP_MANAGEMENT_PATH, - APP_NETWORK_PATH, - APP_OVERVIEW_PATH, - APP_TIMELINES_PATH, -} from '../../../../common/constants'; export { getDetectionEngineUrl } from './redirect_to_detection_engine'; export { getAppOverviewUrl } from './redirect_to_overview'; @@ -31,43 +22,17 @@ export { getConfigureCasesUrl, } from './redirect_to_case'; -const getSubAppPath = (page: string) => { - switch (page) { - case SecurityPageName.alerts: { - return APP_ALERTS_PATH; - } - case SecurityPageName.case: { - return APP_CASES_PATH; - } - case SecurityPageName.hosts: { - return APP_HOSTS_PATH; - } - case SecurityPageName.management: { - return APP_MANAGEMENT_PATH; - } - case SecurityPageName.network: { - return APP_NETWORK_PATH; - } - case SecurityPageName.overview: { - return APP_OVERVIEW_PATH; - } - case SecurityPageName.timelines: { - return APP_TIMELINES_PATH; - } - default: - return APP_OVERVIEW_PATH; - } -}; - export const useFormatUrl = (page: SecurityPageName) => { - const basePath = useBasePath(); + const history = useHistory(); const search = useGetUrlSearch(navTabs[page]); const formatUrl = useCallback( (path: string) => { - const hasSearch = path.includes('?'); - return `${basePath}${getSubAppPath(page)}${path}${!hasSearch ? search : ''}`; + return history.createHref({ + pathname: path.includes('?') ? path.substring(0, path.indexOf('?')) : path, + search, + }); }, - [basePath, page, search] + [history, search] ); return { formatUrl, search }; }; diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.tsx index 351fa810ca0e6..c899e8f5f2859 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.tsx @@ -32,14 +32,14 @@ const TabNavigationItemComponent = ({ const handleClick = useCallback( (ev) => { ev.preventDefault(); - if (id in SecurityPageName) { + if (id in SecurityPageName && pageId == null) { navigateToApp(`${APP_ID}:${id}`, { path: urlSearch }); } else { history.push(hrefWithSearch); } track(METRIC_TYPE.CLICK, `${TELEMETRY_EVENT.TAB_CLICKED}${id}`); }, - [history, hrefWithSearch, id, navigateToApp, urlSearch] + [history, hrefWithSearch, id, navigateToApp, pageId, urlSearch] ); const appHref = formatUrl(pageId != null ? href : ''); return ( diff --git a/x-pack/plugins/security_solution/public/management/components/management_page_view.tsx b/x-pack/plugins/security_solution/public/management/components/management_page_view.tsx index c65b512c51811..baaf67239a512 100644 --- a/x-pack/plugins/security_solution/public/management/components/management_page_view.tsx +++ b/x-pack/plugins/security_solution/public/management/components/management_page_view.tsx @@ -15,23 +15,23 @@ import { getEndpointPath, getPoliciesPath } from '../common/routing'; export const ManagementPageView = memo<Omit<PageViewProps, 'tabs'>>((options) => { const history = useHistory(); - const { formatUrl } = useFormatUrl(SecurityPageName.management); + const { formatUrl, search } = useFormatUrl(SecurityPageName.management); const { tabName } = useParams<{ tabName: ManagementSubTab }>(); const goToEndpoint = useCallback( (ev) => { ev.preventDefault(); - history.push(getEndpointPath()); + history.push(getEndpointPath(search)); }, - [history] + [history, search] ); const goToPolicies = useCallback( (ev) => { ev.preventDefault(); - history.push(getPoliciesPath()); + history.push(getPoliciesPath(search)); }, - [history] + [history, search] ); const tabs = useMemo((): PageViewProps['tabs'] | undefined => { From 0b2557c968471fef15874099208cd0a472fc4a7e Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Fri, 12 Jun 2020 10:34:51 -0400 Subject: [PATCH 14/43] bug when you click on external alerts from host or network --- .../common/components/navigation/tab_navigation/index.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.tsx index c899e8f5f2859..ef2ac9524baa9 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.tsx @@ -66,7 +66,11 @@ export const TabNavigationComponent = (props: TabNavigationProps) => { getOr( '', 'id', - Object.values(navTabs).find((item) => tabName === item.id || pageName === item.id) + Object.values(navTabs).find( + (item) => + (tabName === item.id && item.pageId != null) || + (pageName === item.id && item.pageId == null) + ) ), [pageName, tabName, navTabs] ); From f72ecdc0fe0b8ddae11f8cba42aa1b2d5351df06 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Fri, 12 Jun 2020 10:36:09 -0400 Subject: [PATCH 15/43] improvement from josh feedback --- .../security_solution/public/plugin.tsx | 47 ++++++++----------- .../security_solution/public/sub_plugins.ts | 34 ++++++++++++++ 2 files changed, 53 insertions(+), 28 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/sub_plugins.ts diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index fc622d6ab2758..c1c76be362ce9 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -59,7 +59,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S defaultMessage: 'Explore security metrics and logs for events and alerts', }), icon: APP_ICON, - path: APP_PATH, + path: APP_OVERVIEW_PATH, showOnHomePage: true, category: FeatureCatalogueCategory.DATA, }); @@ -301,35 +301,26 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S } private async downloadSubPlugins() { - const [ - AlertsSubPlugin, - CasesSubPlugin, - HostsSubPlugin, - NetworkSubPlugin, - OverviewSubPlugin, - TimelinesSubPlugin, - EndpointAlertsSubPlugin, - ManagementSubPlugin, - ] = await Promise.all([ - import('./alerts'), - import('./cases'), - import('./hosts'), - import('./network'), - import('./overview'), - import('./timelines'), - import('./endpoint_alerts'), - import('./management'), - ]); + const { + alertsSubPlugin, + casesSubPlugin, + hostsSubPlugin, + networkSubPlugin, + overviewSubPlugin, + timelinesSubPlugin, + endpointAlertsSubPlugin, + managementSubPlugin, + } = await import('./sub_plugins'); return { - alertsSubPlugin: new AlertsSubPlugin.Alerts(), - casesSubPlugin: new CasesSubPlugin.Cases(), - hostsSubPlugin: new HostsSubPlugin.Hosts(), - networkSubPlugin: new NetworkSubPlugin.Network(), - overviewSubPlugin: new OverviewSubPlugin.Overview(), - timelinesSubPlugin: new TimelinesSubPlugin.Timelines(), - endpointAlertsSubPlugin: new EndpointAlertsSubPlugin.EndpointAlerts(), - managementSubPlugin: new ManagementSubPlugin.Management(), + alertsSubPlugin, + casesSubPlugin, + hostsSubPlugin, + networkSubPlugin, + overviewSubPlugin, + timelinesSubPlugin, + endpointAlertsSubPlugin, + managementSubPlugin, }; } diff --git a/x-pack/plugins/security_solution/public/sub_plugins.ts b/x-pack/plugins/security_solution/public/sub_plugins.ts new file mode 100644 index 0000000000000..553184727db2b --- /dev/null +++ b/x-pack/plugins/security_solution/public/sub_plugins.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Alerts } from './alerts'; +import { Cases } from './cases'; +import { Hosts } from './hosts'; +import { Network } from './network'; +import { Overview } from './overview'; +import { Timelines } from './timelines'; +import { EndpointAlerts } from './endpoint_alerts'; +import { Management } from './management'; + +const alertsSubPlugin = new Alerts(); +const casesSubPlugin = new Cases(); +const hostsSubPlugin = new Hosts(); +const networkSubPlugin = new Network(); +const overviewSubPlugin = new Overview(); +const timelinesSubPlugin = new Timelines(); +const endpointAlertsSubPlugin = new EndpointAlerts(); +const managementSubPlugin = new Management(); + +export { + alertsSubPlugin, + casesSubPlugin, + hostsSubPlugin, + networkSubPlugin, + overviewSubPlugin, + timelinesSubPlugin, + endpointAlertsSubPlugin, + managementSubPlugin, +}; From e9ae95b552aa41a3bc66123c064c1d69e0cc6fcb Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Sun, 14 Jun 2020 20:54:43 -0400 Subject: [PATCH 16/43] redirect siem to security solution --- .../public/app/home/index.tsx | 18 --- .../public/common/components/link_to/index.ts | 3 +- .../ml_host_conditional_container.tsx | 16 +-- .../ml_network_conditional_container.tsx | 8 +- .../public/hosts/pages/index.tsx | 7 ++ .../security_solution/public/plugin.tsx | 113 +++++++++++++++++- 6 files changed, 129 insertions(+), 36 deletions(-) diff --git a/x-pack/plugins/security_solution/public/app/home/index.tsx b/x-pack/plugins/security_solution/public/app/home/index.tsx index 3eeb33a763911..d8bdbd6e7ef5f 100644 --- a/x-pack/plugins/security_solution/public/app/home/index.tsx +++ b/x-pack/plugins/security_solution/public/app/home/index.tsx @@ -12,8 +12,6 @@ import { DragDropContextWrapper } from '../../common/components/drag_and_drop/dr import { Flyout } from '../../timelines/components/flyout'; import { HeaderGlobal } from '../../common/components/header_global'; import { HelpMenu } from '../../common/components/help_menu'; -import { MlHostConditionalContainer } from '../../common/components/ml/conditional_links/ml_host_conditional_container'; -import { MlNetworkConditionalContainer } from '../../common/components/ml/conditional_links/ml_network_conditional_container'; import { AutoSaveWarningMsg } from '../../timelines/components/timeline/auto_save_warning'; import { UseUrlState } from '../../common/components/url_state'; import { @@ -22,7 +20,6 @@ import { } from '../../common/containers/source'; import { useShowTimeline } from '../../common/utils/timeline/use_show_timeline'; import { navTabs } from './home_navigations'; -import { SecurityPageName } from '../types'; const WrappedByAutoSizer = styled.div` height: 100%; @@ -85,21 +82,6 @@ export const HomePage: React.FC<HomePageProps> = ({ children }) => { )} {children} - {/* <Switch> - <Route - path="/ml-hosts" - render={({ location, match }) => ( - <MlHostConditionalContainer location={location} url={match.url} /> - )} - /> - <Route - path="/ml-network" - render={({ location, match }) => ( - <MlNetworkConditionalContainer location={location} url={match.url} /> - )} - /> - <Route render={() => <NotFoundPage />} /> - </Switch> */} </DragDropContextWrapper> )} </WithSource> diff --git a/x-pack/plugins/security_solution/public/common/components/link_to/index.ts b/x-pack/plugins/security_solution/public/common/components/link_to/index.ts index c427a131443b0..91399ebdfa285 100644 --- a/x-pack/plugins/security_solution/public/common/components/link_to/index.ts +++ b/x-pack/plugins/security_solution/public/common/components/link_to/index.ts @@ -27,8 +27,9 @@ export const useFormatUrl = (page: SecurityPageName) => { const search = useGetUrlSearch(navTabs[page]); const formatUrl = useCallback( (path: string) => { + const pathArr = path.split('?'); return history.createHref({ - pathname: path.includes('?') ? path.substring(0, path.indexOf('?')) : path, + pathname: pathArr[0], search, }); }, diff --git a/x-pack/plugins/security_solution/public/common/components/ml/conditional_links/ml_host_conditional_container.tsx b/x-pack/plugins/security_solution/public/common/components/ml/conditional_links/ml_host_conditional_container.tsx index e6f0f3b48072c..4eb86a1417e77 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/conditional_links/ml_host_conditional_container.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/conditional_links/ml_host_conditional_container.tsx @@ -42,7 +42,7 @@ export const MlHostConditionalContainer = React.memo<MlHostConditionalProps>(({ sort: false, encode: false, }); - return <Redirect to={`/${SecurityPageName.hosts}?${reEncoded}`} />; + return <Redirect to={`?${reEncoded}`} />; }} /> <Route @@ -66,9 +66,7 @@ export const MlHostConditionalContainer = React.memo<MlHostConditionalProps>(({ encode: false, }); - return ( - <Redirect to={`/${SecurityPageName.hosts}/${HostsTableType.anomalies}?${reEncoded}`} /> - ); + return <Redirect to={`/${HostsTableType.anomalies}?${reEncoded}`} />; } else if (multipleEntities(hostName)) { const hosts: string[] = getMultipleEntities(hostName); queryStringDecoded.query = addEntitiesToKql( @@ -81,20 +79,14 @@ export const MlHostConditionalContainer = React.memo<MlHostConditionalProps>(({ encode: false, }); - return ( - <Redirect to={`/${SecurityPageName.hosts}/${HostsTableType.anomalies}?${reEncoded}`} /> - ); + return <Redirect to={`/${HostsTableType.anomalies}?${reEncoded}`} />; } else { const reEncoded = stringify(urlUtils.encodeQuery(queryStringDecoded), { sort: false, encode: false, }); - return ( - <Redirect - to={`/${SecurityPageName.hosts}/${hostName}/${HostsTableType.anomalies}?${reEncoded}`} - /> - ); + return <Redirect to={`/${hostName}/${HostsTableType.anomalies}?${reEncoded}`} />; } }} /> diff --git a/x-pack/plugins/security_solution/public/common/components/ml/conditional_links/ml_network_conditional_container.tsx b/x-pack/plugins/security_solution/public/common/components/ml/conditional_links/ml_network_conditional_container.tsx index 889ea0bd7dca9..1f9926a152e08 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/conditional_links/ml_network_conditional_container.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/conditional_links/ml_network_conditional_container.tsx @@ -43,7 +43,7 @@ export const MlNetworkConditionalContainer = React.memo<MlNetworkConditionalProp encode: false, }); - return <Redirect to={`/${SecurityPageName.network}?${reEncoded}`} />; + return <Redirect to={`?${reEncoded}`} />; }} /> <Route @@ -68,7 +68,7 @@ export const MlNetworkConditionalContainer = React.memo<MlNetworkConditionalProp encode: false, }); - return <Redirect to={`/${SecurityPageName.network}?${reEncoded}`} />; + return <Redirect to={`?${reEncoded}`} />; } else if (multipleEntities(ip)) { const ips: string[] = getMultipleEntities(ip); queryStringDecoded.query = addEntitiesToKql( @@ -80,13 +80,13 @@ export const MlNetworkConditionalContainer = React.memo<MlNetworkConditionalProp sort: false, encode: false, }); - return <Redirect to={`/${SecurityPageName.network}?${reEncoded}`} />; + return <Redirect to={`?${reEncoded}`} />; } else { const reEncoded = stringify(urlUtils.encodeQuery(queryStringDecoded), { sort: false, encode: false, }); - return <Redirect to={`/${SecurityPageName.network}/ip/${ip}?${reEncoded}`} />; + return <Redirect to={`/ip/${ip}?${reEncoded}`} />; } }} /> diff --git a/x-pack/plugins/security_solution/public/hosts/pages/index.tsx b/x-pack/plugins/security_solution/public/hosts/pages/index.tsx index f205a0c578ba6..c96992f36bed8 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/index.tsx @@ -10,6 +10,7 @@ import { Route, Switch, RouteComponentProps, useHistory } from 'react-router-dom import { HostDetails } from './details'; import { HostsTableType } from '../store/model'; +import { MlHostConditionalContainer } from '../../common/components/ml/conditional_links/ml_host_conditional_container'; import { GlobalTime } from '../../common/containers/global_time'; import { Hosts } from './hosts'; import { hostsPagePath, hostDetailsPagePath } from './types'; @@ -82,6 +83,12 @@ export const HostsContainer = React.memo<Props>(({ url }) => { return null; }} /> + <Route + path="/ml-hosts" + render={({ location, match }) => ( + <MlHostConditionalContainer location={location} url={match.url} /> + )} + /> <Route exact strict diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index c1c76be362ce9..787169e921282 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -25,7 +25,6 @@ import { PluginSetup, PluginStart, SetupPlugins, StartPlugins, StartServices } f import { APP_ID, APP_ICON, - APP_PATH, APP_ALERTS_PATH, APP_HOSTS_PATH, APP_OVERVIEW_PATH, @@ -81,6 +80,18 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S return { coreStart, startPlugins, services, store: this.store }; }; + // core.application.register({ + // id: APP_ID, + // title: 'Security', + // appRoute: APP_PATH, + // navLinkStatus: AppNavLinkStatus.hidden, + // mount: async (params: AppMountParameters) => { + // const [{ application }] = await core.getStartServices(); + // application.navigateToApp(`${APP_ID}:${SecurityPageName.overview}`, { replace: true }); + // return () => true; + // }, + // }); + core.application.register({ id: `${APP_ID}:${SecurityPageName.overview}`, title: i18n.translate('xpack.securitySolution.overviewPage.title', { @@ -274,6 +285,106 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S }, }); + core.application.register({ + id: 'siem', + appRoute: 'app/siem', + title: 'SIEM', + navLinkStatus: 3, + mount: async (params: AppMountParameters) => { + const [{ application }] = await core.getStartServices(); + const hashPath = window.location.hash.split('?'); + const search = hashPath.length >= 1 ? hashPath[1] : ''; + const pageRoute = hashPath.length > 0 ? hashPath[0].split('/') : []; + const pageName = pageRoute.length >= 1 ? pageRoute[1] : ''; + const path = `/${pageRoute.slice(2).join('/') ?? ''}?${search}`; + + switch (pageName) { + case SecurityPageName.overview: + window.setTimeout( + () => + application.navigateToApp(`${APP_ID}:${SecurityPageName.overview}`, { + replace: true, + path, + }), + 0 + ); + case 'ml-hosts': + window.setTimeout( + () => + application.navigateToApp(`${APP_ID}:${SecurityPageName.hosts}`, { + replace: true, + path: `/ml-hosts${path}`, + }), + 0 + ); + case SecurityPageName.hosts: + window.setTimeout( + () => + application.navigateToApp(`${APP_ID}:${SecurityPageName.hosts}`, { + replace: true, + path, + }), + 0 + ); + case 'ml-network': + window.setTimeout( + () => + application.navigateToApp(`${APP_ID}:${SecurityPageName.network}`, { + replace: true, + path: `/ml-network${path}`, + }), + 0 + ); + case SecurityPageName.network: + window.setTimeout( + () => + application.navigateToApp(`${APP_ID}:${SecurityPageName.network}`, { + replace: true, + path, + }), + 0 + ); + case SecurityPageName.timelines: + window.setTimeout( + () => + application.navigateToApp(`${APP_ID}:${SecurityPageName.timelines}`, { + replace: true, + path, + }), + 0 + ); + case SecurityPageName.case: + window.setTimeout( + () => + application.navigateToApp(`${APP_ID}:${SecurityPageName.case}`, { + replace: true, + path, + }), + 0 + ); + case 'detections': + window.setTimeout( + () => + application.navigateToApp(`${APP_ID}:${SecurityPageName.alerts}`, { + replace: true, + path, + }), + 0 + ); + default: + window.setTimeout( + () => + application.navigateToApp(`${APP_ID}:${SecurityPageName.overview}`, { + replace: true, + path: `?${search}`, + }), + 0 + ); + } + return () => true; + }, + }); + return {}; } From 2b6a3c174a7770fa0a0914e66668d7ffaf17485f Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Mon, 15 Jun 2020 11:40:07 -0400 Subject: [PATCH 17/43] a little clean up --- .../security_solution/public/helpers.ts | 102 ++++++++++++++++++ .../security_solution/public/plugin.tsx | 94 +--------------- 2 files changed, 106 insertions(+), 90 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/helpers.ts diff --git a/x-pack/plugins/security_solution/public/helpers.ts b/x-pack/plugins/security_solution/public/helpers.ts new file mode 100644 index 0000000000000..226aaf13637cb --- /dev/null +++ b/x-pack/plugins/security_solution/public/helpers.ts @@ -0,0 +1,102 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { CoreStart } from '../../../../src/core/public'; +import { APP_ID } from '../common/constants'; +import { SecurityPageName } from './app/types'; + +export const manageOldSiemRoutes = async (coreStart: CoreStart) => { + const { application } = coreStart; + const hashPath = window.location.hash.split('?'); + const search = hashPath.length >= 1 ? hashPath[1] : ''; + const pageRoute = hashPath.length > 0 ? hashPath[0].split('/') : []; + const pageName = pageRoute.length >= 1 ? pageRoute[1] : ''; + const path = `/${pageRoute.slice(2).join('/') ?? ''}?${search}`; + + switch (pageName) { + case SecurityPageName.overview: + window.setTimeout( + () => + application.navigateToApp(`${APP_ID}:${SecurityPageName.overview}`, { + replace: true, + path, + }), + 0 + ); + case 'ml-hosts': + window.setTimeout( + () => + application.navigateToApp(`${APP_ID}:${SecurityPageName.hosts}`, { + replace: true, + path: `/ml-hosts${path}`, + }), + 0 + ); + case SecurityPageName.hosts: + window.setTimeout( + () => + application.navigateToApp(`${APP_ID}:${SecurityPageName.hosts}`, { + replace: true, + path, + }), + 0 + ); + case 'ml-network': + window.setTimeout( + () => + application.navigateToApp(`${APP_ID}:${SecurityPageName.network}`, { + replace: true, + path: `/ml-network${path}`, + }), + 0 + ); + case SecurityPageName.network: + window.setTimeout( + () => + application.navigateToApp(`${APP_ID}:${SecurityPageName.network}`, { + replace: true, + path, + }), + 0 + ); + case SecurityPageName.timelines: + window.setTimeout( + () => + application.navigateToApp(`${APP_ID}:${SecurityPageName.timelines}`, { + replace: true, + path, + }), + 0 + ); + case SecurityPageName.case: + window.setTimeout( + () => + application.navigateToApp(`${APP_ID}:${SecurityPageName.case}`, { + replace: true, + path, + }), + 0 + ); + case 'detections': + window.setTimeout( + () => + application.navigateToApp(`${APP_ID}:${SecurityPageName.alerts}`, { + replace: true, + path, + }), + 0 + ); + default: + window.setTimeout( + () => + application.navigateToApp(`${APP_ID}:${SecurityPageName.overview}`, { + replace: true, + path: `?${search}`, + }), + 0 + ); + } +}; diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index 787169e921282..ea92fbdc20eb8 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -37,6 +37,7 @@ import { ConfigureEndpointDatasource } from './management/pages/policy/view/inge import { State, createStore, createInitialState } from './common/store'; import { SecurityPageName } from './app/types'; +import { manageOldSiemRoutes } from './helpers'; export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, StartPlugins> { private kibanaVersion: string; @@ -80,6 +81,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S return { coreStart, startPlugins, services, store: this.store }; }; + // Waiting for https://github.com/elastic/kibana/issues/69110 // core.application.register({ // id: APP_ID, // title: 'Security', @@ -291,96 +293,8 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S title: 'SIEM', navLinkStatus: 3, mount: async (params: AppMountParameters) => { - const [{ application }] = await core.getStartServices(); - const hashPath = window.location.hash.split('?'); - const search = hashPath.length >= 1 ? hashPath[1] : ''; - const pageRoute = hashPath.length > 0 ? hashPath[0].split('/') : []; - const pageName = pageRoute.length >= 1 ? pageRoute[1] : ''; - const path = `/${pageRoute.slice(2).join('/') ?? ''}?${search}`; - - switch (pageName) { - case SecurityPageName.overview: - window.setTimeout( - () => - application.navigateToApp(`${APP_ID}:${SecurityPageName.overview}`, { - replace: true, - path, - }), - 0 - ); - case 'ml-hosts': - window.setTimeout( - () => - application.navigateToApp(`${APP_ID}:${SecurityPageName.hosts}`, { - replace: true, - path: `/ml-hosts${path}`, - }), - 0 - ); - case SecurityPageName.hosts: - window.setTimeout( - () => - application.navigateToApp(`${APP_ID}:${SecurityPageName.hosts}`, { - replace: true, - path, - }), - 0 - ); - case 'ml-network': - window.setTimeout( - () => - application.navigateToApp(`${APP_ID}:${SecurityPageName.network}`, { - replace: true, - path: `/ml-network${path}`, - }), - 0 - ); - case SecurityPageName.network: - window.setTimeout( - () => - application.navigateToApp(`${APP_ID}:${SecurityPageName.network}`, { - replace: true, - path, - }), - 0 - ); - case SecurityPageName.timelines: - window.setTimeout( - () => - application.navigateToApp(`${APP_ID}:${SecurityPageName.timelines}`, { - replace: true, - path, - }), - 0 - ); - case SecurityPageName.case: - window.setTimeout( - () => - application.navigateToApp(`${APP_ID}:${SecurityPageName.case}`, { - replace: true, - path, - }), - 0 - ); - case 'detections': - window.setTimeout( - () => - application.navigateToApp(`${APP_ID}:${SecurityPageName.alerts}`, { - replace: true, - path, - }), - 0 - ); - default: - window.setTimeout( - () => - application.navigateToApp(`${APP_ID}:${SecurityPageName.overview}`, { - replace: true, - path: `?${search}`, - }), - 0 - ); - } + const [coreStart] = await core.getStartServices(); + manageOldSiemRoutes(coreStart); return () => true; }, }); From a80574ee78c395a7e267f1236057a1c3f5b94121 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Mon, 15 Jun 2020 12:11:14 -0400 Subject: [PATCH 18/43] Fix types --- .../common/components/link_to/helpers.test.ts | 2 +- .../common/components/link_to/helpers.ts | 4 +- .../ml_host_conditional_container.tsx | 1 - .../ml_network_conditional_container.tsx | 1 - .../public/management/common/routing.ts | 57 ++++++++++++++++++- .../components/management_page_view.tsx | 6 +- .../public/management/index.ts | 2 - .../store/host_pagination.test.ts | 8 +-- .../endpoint_hosts/store/middleware.test.ts | 4 +- .../view/details/host_details.tsx | 7 +-- .../endpoint_hosts/view/details/index.tsx | 7 +-- .../pages/endpoint_hosts/view/index.test.tsx | 4 +- .../pages/endpoint_hosts/view/index.tsx | 41 +++++++------ .../public/management/pages/index.tsx | 4 +- .../policy/store/policy_list/index.test.ts | 4 +- .../configure_datasource.tsx | 7 +-- .../pages/policy/view/policy_details.test.tsx | 12 ++-- .../pages/policy/view/policy_details.tsx | 10 ++-- .../pages/policy/view/policy_list.tsx | 16 +++--- .../components/timeline/index.test.tsx | 1 + .../timeline/properties/index.test.tsx | 4 +- .../components/timeline/properties/index.tsx | 14 +++-- 22 files changed, 132 insertions(+), 84 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/link_to/helpers.test.ts b/x-pack/plugins/security_solution/public/common/components/link_to/helpers.test.ts index 14b367de674a2..97be9630c2198 100644 --- a/x-pack/plugins/security_solution/public/common/components/link_to/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/link_to/helpers.test.ts @@ -14,6 +14,6 @@ describe('appendSearch', () => { expect(appendSearch(undefined)).toEqual(''); }); test('should return parameter if parameter is defined', () => { - expect(appendSearch('helloWorld')).toEqual('helloWorld'); + expect(appendSearch('helloWorld')).toEqual('?helloWorld'); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/link_to/helpers.ts b/x-pack/plugins/security_solution/public/common/components/link_to/helpers.ts index 9d818ab3b6479..b086745bf8f76 100644 --- a/x-pack/plugins/security_solution/public/common/components/link_to/helpers.ts +++ b/x-pack/plugins/security_solution/public/common/components/link_to/helpers.ts @@ -4,4 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export const appendSearch = (search?: string) => (search != null ? `${search}` : ''); +import { isEmpty } from 'lodash/fp'; + +export const appendSearch = (search?: string) => (isEmpty(search) ? '' : `?${search}`); diff --git a/x-pack/plugins/security_solution/public/common/components/ml/conditional_links/ml_host_conditional_container.tsx b/x-pack/plugins/security_solution/public/common/components/ml/conditional_links/ml_host_conditional_container.tsx index 4eb86a1417e77..0f3e0f9171e6d 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/conditional_links/ml_host_conditional_container.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/conditional_links/ml_host_conditional_container.tsx @@ -11,7 +11,6 @@ import { Redirect, Route, Switch, RouteComponentProps } from 'react-router-dom'; import { addEntitiesToKql } from './add_entities_to_kql'; import { replaceKQLParts } from './replace_kql_parts'; import { emptyEntity, multipleEntities, getMultipleEntities } from './entity_helpers'; -import { SecurityPageName } from '../../../../app/types'; import { HostsTableType } from '../../../../hosts/store/model'; import { url as urlUtils } from '../../../../../../../../src/plugins/kibana_utils/public'; diff --git a/x-pack/plugins/security_solution/public/common/components/ml/conditional_links/ml_network_conditional_container.tsx b/x-pack/plugins/security_solution/public/common/components/ml/conditional_links/ml_network_conditional_container.tsx index 1f9926a152e08..60242276dcad7 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/conditional_links/ml_network_conditional_container.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/conditional_links/ml_network_conditional_container.tsx @@ -11,7 +11,6 @@ import { Redirect, Route, Switch, RouteComponentProps } from 'react-router-dom'; import { addEntitiesToKql } from './add_entities_to_kql'; import { replaceKQLParts } from './replace_kql_parts'; import { emptyEntity, getMultipleEntities, multipleEntities } from './entity_helpers'; -import { SecurityPageName } from '../../../../app/types'; import { url as urlUtils } from '../../../../../../../../src/plugins/kibana_utils/public'; diff --git a/x-pack/plugins/security_solution/public/management/common/routing.ts b/x-pack/plugins/security_solution/public/management/common/routing.ts index c137f8578cde2..3d5b1f5ca6193 100644 --- a/x-pack/plugins/security_solution/public/management/common/routing.ts +++ b/x-pack/plugins/security_solution/public/management/common/routing.ts @@ -4,8 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ +import { isEmpty } from 'lodash/fp'; import { generatePath } from 'react-router-dom'; // eslint-disable-next-line import/no-nodejs-modules +import querystring from 'querystring'; import { MANAGEMENT_ROUTING_ENDPOINTS_PATH, @@ -14,11 +16,60 @@ import { } from './constants'; import { ManagementSubTab } from '../types'; import { appendSearch } from '../../common/components/link_to/helpers'; +import { HostIndexUIQueryParams } from '../pages/endpoint_hosts/types'; -export const getEndpointPath = (search?: string) => - `/${generatePath(MANAGEMENT_ROUTING_ENDPOINTS_PATH, { +// Taken from: https://github.com/microsoft/TypeScript/issues/12936#issuecomment-559034150 +type ExactKeys<T1, T2> = Exclude<keyof T1, keyof T2> extends never ? T1 : never; +type Exact<T, Shape> = T extends Shape ? ExactKeys<T, Shape> : never; + +/** + * Returns a string to be used in the URL as search query params. + * Ensures that when creating a URL query param string, that the given input strictly + * matches the expected interface (guards against possibly leaking internal state) + */ +const querystringStringify: <ExpectedType extends object, ArgType>( + params: Exact<ExpectedType, ArgType> +) => string = querystring.stringify; + +/** Make `selected_host` required */ +type EndpointDetailsUrlProps = Omit<HostIndexUIQueryParams, 'selected_host'> & + Required<Pick<HostIndexUIQueryParams, 'selected_host'>>; + +export const getEndpointListPath = ( + props: { name: 'default' | 'endpointList' } & HostIndexUIQueryParams, + search?: string +) => { + const { name, ...queryParams } = props; + const urlQueryParams = querystringStringify<HostIndexUIQueryParams, typeof queryParams>( + queryParams + ); + const urlSearch = `${urlQueryParams && !isEmpty(search) ? '&' : ''}${search ?? ''}`; + + if (name === 'endpointList') { + return `/${generatePath(MANAGEMENT_ROUTING_ENDPOINTS_PATH, { + tabName: ManagementSubTab.endpoints, + })}${appendSearch(`${urlQueryParams ? `${urlQueryParams}${urlSearch}` : urlSearch}`)}`; + } + return `${appendSearch(`${urlQueryParams ? `${urlQueryParams}${urlSearch}` : urlSearch}`)}`; +}; + +export const getEndpointDetailsPath = ( + props: { name: 'endpointDetails' | 'endpointPolicyResponse' } & EndpointDetailsUrlProps, + search?: string +) => { + const { name, ...queryParams } = props; + queryParams.show = (props.name === 'endpointPolicyResponse' + ? 'policy_response' + : '') as HostIndexUIQueryParams['show']; + const urlQueryParams = querystringStringify<EndpointDetailsUrlProps, typeof queryParams>( + queryParams + ); + const urlSearch = `${urlQueryParams && !isEmpty(search) ? '&' : ''}${search ?? ''}`; + + return `${generatePath(MANAGEMENT_ROUTING_ENDPOINTS_PATH, { tabName: ManagementSubTab.endpoints, - })}${appendSearch(search ?? undefined)}`; + })}${appendSearch(`${urlQueryParams ? `${urlQueryParams}${urlSearch}` : urlSearch}`)}`; +}; export const getPoliciesPath = (search?: string) => `/${generatePath(MANAGEMENT_ROUTING_POLICIES_PATH, { diff --git a/x-pack/plugins/security_solution/public/management/components/management_page_view.tsx b/x-pack/plugins/security_solution/public/management/components/management_page_view.tsx index baaf67239a512..add7f64d2a313 100644 --- a/x-pack/plugins/security_solution/public/management/components/management_page_view.tsx +++ b/x-pack/plugins/security_solution/public/management/components/management_page_view.tsx @@ -11,7 +11,7 @@ import { PageView, PageViewProps } from '../../common/components/endpoint/page_v import { ManagementSubTab } from '../types'; import { SecurityPageName } from '../../app/types'; import { useFormatUrl } from '../../common/components/link_to'; -import { getEndpointPath, getPoliciesPath } from '../common/routing'; +import { getEndpointListPath, getPoliciesPath } from '../common/routing'; export const ManagementPageView = memo<Omit<PageViewProps, 'tabs'>>((options) => { const history = useHistory(); @@ -21,7 +21,7 @@ export const ManagementPageView = memo<Omit<PageViewProps, 'tabs'>>((options) => const goToEndpoint = useCallback( (ev) => { ev.preventDefault(); - history.push(getEndpointPath(search)); + history.push(getEndpointListPath({ name: 'endpointList' }, search)); }, [history, search] ); @@ -45,7 +45,7 @@ export const ManagementPageView = memo<Omit<PageViewProps, 'tabs'>>((options) => }), id: ManagementSubTab.endpoints, isSelected: tabName === ManagementSubTab.endpoints, - href: formatUrl(getEndpointPath()), + href: formatUrl(getEndpointListPath({ name: 'endpointList' })), onClick: goToEndpoint, }, { diff --git a/x-pack/plugins/security_solution/public/management/index.ts b/x-pack/plugins/security_solution/public/management/index.ts index 0fcba81821c4a..902ed085bd369 100644 --- a/x-pack/plugins/security_solution/public/management/index.ts +++ b/x-pack/plugins/security_solution/public/management/index.ts @@ -14,8 +14,6 @@ import { AppAction } from '../common/store/actions'; import { managementMiddlewareFactory } from './store/middleware'; import { ManagementState } from './types'; -export { getManagementUrl } from './common/routing'; - /** * Internally, our state is sometimes immutable, ignore that in our external * interface. diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/host_pagination.test.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/host_pagination.test.ts index b8eaa39c77752..ae2ce9facc837 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/host_pagination.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/host_pagination.test.ts @@ -24,7 +24,7 @@ import { MiddlewareActionSpyHelper, createSpyMiddleware, } from '../../../../common/store/test_utils'; -import { getManagementUrl } from '../../..'; +import { getEndpointListPath } from '../../../common/routing'; describe('host list pagination: ', () => { let fakeCoreStart: jest.Mocked<CoreStart>; @@ -56,9 +56,7 @@ describe('host list pagination: ', () => { queryParams = () => uiQueryParams(store.getState()); historyPush = (nextQueryParams: HostIndexUIQueryParams): void => { - return history.push( - getManagementUrl({ name: 'endpointList', excludePrefix: true, ...nextQueryParams }) - ); + return history.push(getEndpointListPath({ name: 'endpointList', ...nextQueryParams })); }; }); @@ -72,7 +70,7 @@ describe('host list pagination: ', () => { type: 'userChangedUrl', payload: { ...history.location, - pathname: getManagementUrl({ name: 'endpointList', excludePrefix: true }), + pathname: getEndpointListPath({ name: 'endpointList' }), }, }); await waitForAction('serverReturnedHostList'); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts index a6cd2ca3afac4..e62c53e061a33 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/middleware.test.ts @@ -21,7 +21,7 @@ import { listData } from './selectors'; import { HostState } from '../types'; import { hostListReducer } from './reducer'; import { hostMiddlewareFactory } from './middleware'; -import { getManagementUrl } from '../../..'; +import { getEndpointListPath } from '../../../common/routing'; describe('host list middleware', () => { let fakeCoreStart: jest.Mocked<CoreStart>; @@ -60,7 +60,7 @@ describe('host list middleware', () => { type: 'userChangedUrl', payload: { ...history.location, - pathname: getManagementUrl({ name: 'endpointList', excludePrefix: true }), + pathname: getEndpointListPath({ name: 'endpointList' }), }, }); await waitForAction('serverReturnedHostList'); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/host_details.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/host_details.tsx index a3862d4454c1d..66bf6259f70ba 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/host_details.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/host_details.tsx @@ -23,7 +23,7 @@ import { POLICY_STATUS_TO_HEALTH_COLOR } from '../host_constants'; import { FormattedDateAndTime } from '../../../../../common/components/endpoint/formatted_date_time'; import { useNavigateByRouterEventHandler } from '../../../../../common/hooks/endpoint/use_navigate_by_router_event_handler'; import { LinkToApp } from '../../../../../common/components/endpoint/link_to_app'; -import { getManagementUrl } from '../../../..'; +import { getEndpointDetailsPath } from '../../../../common/routing'; const HostIds = styled(EuiListGroupItem)` margin-top: 0; @@ -64,14 +64,13 @@ export const HostDetails = memo(({ details }: { details: HostMetadata }) => { const [policyResponseUri, policyResponseRoutePath] = useMemo(() => { const { selected_host, show, ...currentUrlParams } = queryParams; return [ - getManagementUrl({ + getEndpointDetailsPath({ name: 'endpointPolicyResponse', ...currentUrlParams, selected_host: details.host.id, }), - getManagementUrl({ + getEndpointDetailsPath({ name: 'endpointPolicyResponse', - excludePrefix: true, ...currentUrlParams, selected_host: details.host.id, }), diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/index.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/index.tsx index ed853e24d7c31..ec38e9c46cc45 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/index.tsx @@ -38,7 +38,7 @@ import { PolicyResponse } from './policy_response'; import { HostMetadata } from '../../../../../../common/endpoint/types'; import { FlyoutSubHeader, FlyoutSubHeaderProps } from './components/flyout_sub_header'; import { useNavigateByRouterEventHandler } from '../../../../../common/hooks/endpoint/use_navigate_by_router_event_handler'; -import { getManagementUrl } from '../../../..'; +import { getEndpointListPath } from '../../../../common/routing'; export const HostDetailsFlyout = memo(() => { const history = useHistory(); @@ -118,14 +118,13 @@ const PolicyResponseFlyoutPanel = memo<{ const error = useHostSelector(policyResponseError); const [detailsUri, detailsRoutePath] = useMemo( () => [ - getManagementUrl({ + getEndpointListPath({ name: 'endpointList', ...queryParams, selected_host: hostMeta.host.id, }), - getManagementUrl({ + getEndpointListPath({ name: 'endpointList', - excludePrefix: true, ...queryParams, selected_host: hostMeta.host.id, }), diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx index 7d84bb52238a2..57b6cd786352a 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx @@ -202,7 +202,7 @@ describe('when on the hosts page', () => { const policyStatusLink = await renderResult.findByTestId('policyStatusValue'); expect(policyStatusLink).not.toBeNull(); expect(policyStatusLink.getAttribute('href')).toEqual( - '#/management/endpoints?page_index=0&page_size=10&selected_host=1&show=policy_response' + '/endpoints?page_index=0&page_size=10&selected_host=1&show=policy_response' ); }); it('should update the URL when policy status link is clicked', async () => { @@ -381,7 +381,7 @@ describe('when on the hosts page', () => { const subHeaderBackLink = await renderResult.findByTestId('flyoutSubHeaderBackButton'); expect(subHeaderBackLink.textContent).toBe('Endpoint Details'); expect(subHeaderBackLink.getAttribute('href')).toBe( - '#/management/endpoints?page_index=0&page_size=10&selected_host=1' + '/endpoints?page_index=0&page_size=10&selected_host=1' ); }); it('should update URL when back to details link is clicked', async () => { diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx index 125723e9bcea6..f18d72a7b00c1 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx @@ -28,8 +28,10 @@ import { CreateStructuredSelector } from '../../../../common/store'; import { Immutable, HostInfo } from '../../../../../common/endpoint/types'; import { SpyRoute } from '../../../../common/utils/route/spy_routes'; import { ManagementPageView } from '../../../components/management_page_view'; -import { getManagementUrl } from '../../..'; import { FormattedDate } from '../../../../common/components/formatted_date'; +import { SecurityPageName } from '../../../../app/types'; +import { getEndpointListPath, getEndpointDetailsPath } from '../../../common/routing'; +import { useFormatUrl } from '../../../../common/components/link_to'; const HostLink = memo<{ name: string; @@ -65,6 +67,7 @@ export const HostList = () => { uiQueryParams: queryParams, hasSelectedHost, } = useHostSelector(selector); + const { formatUrl, search } = useFormatUrl(SecurityPageName.management); const paginationSetup = useMemo(() => { return { @@ -81,9 +84,8 @@ export const HostList = () => { const { index, size } = page; // FIXME: PT: if host details is open, table is not displaying correct number of rows history.push( - getManagementUrl({ + getEndpointListPath({ name: 'endpointList', - excludePrefix: true, ...queryParams, page_index: JSON.stringify(index), page_size: JSON.stringify(size), @@ -105,17 +107,24 @@ export const HostList = () => { defaultMessage: 'Hostname', }), render: ({ hostname, id }: HostInfo['metadata']['host']) => { - const toRoutePath = getManagementUrl({ - ...queryParams, - name: 'endpointDetails', - selected_host: id, - excludePrefix: true, - }); - const toRouteUrl = getManagementUrl({ - ...queryParams, - name: 'endpointDetails', - selected_host: id, - }); + const toRoutePath = getEndpointDetailsPath( + { + ...queryParams, + name: 'endpointDetails', + selected_host: id, + }, + search + ); + const toRouteUrl = formatUrl( + getEndpointDetailsPath( + { + ...queryParams, + name: 'endpointDetails', + selected_host: id, + }, + search + ) + ); return <HostLink name={hostname} href={toRouteUrl} route={toRoutePath} />; }, }, @@ -224,7 +233,7 @@ export const HostList = () => { }, }, ]; - }, [queryParams]); + }, [formatUrl, queryParams, search]); return ( <ManagementPageView @@ -252,7 +261,7 @@ export const HostList = () => { pagination={paginationSetup} onChange={onTableChange} /> - <SpyRoute /> + <SpyRoute pageName={SecurityPageName.management} /> </ManagementPageView> ); }; diff --git a/x-pack/plugins/security_solution/public/management/pages/index.tsx b/x-pack/plugins/security_solution/public/management/pages/index.tsx index 2701b2c0aba66..0e81b75d651ba 100644 --- a/x-pack/plugins/security_solution/public/management/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/index.tsx @@ -15,7 +15,7 @@ import { } from '../common/constants'; import { NotFoundPage } from '../../app/404'; import { EndpointsContainer } from './endpoint_hosts'; -import { getManagementUrl } from '..'; +import { getEndpointListPath } from '../common/routing'; export const ManagementContainer = memo(() => { const history = useHistory(); @@ -27,7 +27,7 @@ export const ManagementContainer = memo(() => { path={MANAGEMENT_ROUTING_ROOT_PATH} exact render={() => { - history.replace(getManagementUrl({ name: 'endpointList', excludePrefix: true })); + history.replace(getEndpointListPath({ name: 'endpointList' })); return null; }} /> diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/index.test.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/index.test.ts index ce81c58893a7b..c24c47becc0b5 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/index.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/index.test.ts @@ -26,10 +26,10 @@ import { createSpyMiddleware, MiddlewareActionSpyHelper, } from '../../../../../common/store/test_utils'; -import { getManagementUrl } from '../../../../common/routing'; +import { getPoliciesPath } from '../../../../common/routing'; describe('policy list store concerns', () => { - const policyListPathUrl = getManagementUrl({ name: 'policyList', excludePrefix: true }); + const policyListPathUrl = getPoliciesPath(); let fakeCoreStart: ReturnType<typeof coreMock.createStart>; let depsStart: DepsStartMock; let store: Store; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/configure_datasource.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/configure_datasource.tsx index db5196bfc4eb4..3803ab5bd635b 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/configure_datasource.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/configure_datasource.tsx @@ -13,7 +13,7 @@ import { CustomConfigureDatasourceContent, CustomConfigureDatasourceProps, } from '../../../../../../../ingest_manager/public'; -import { getManagementUrl } from '../../../..'; +import { getPolicyDetailPath } from '../../../../common/routing'; /** * Exports Endpoint-specific datasource configuration instructions @@ -24,10 +24,7 @@ export const ConfigureEndpointDatasource = memo<CustomConfigureDatasourceContent const { services } = useKibana(); let policyUrl = ''; if (from === 'edit' && datasourceId) { - policyUrl = getManagementUrl({ - name: 'policyDetails', - policyId: datasourceId, - }); + policyUrl = getPolicyDetailPath(datasourceId); } return ( diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx index 01e12e6c767a6..aa074d7076dff 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx @@ -10,18 +10,14 @@ import { mount } from 'enzyme'; import { PolicyDetails } from './policy_details'; import { EndpointDocGenerator } from '../../../../../common/endpoint/generate_data'; import { createAppRootMockRenderer } from '../../../../common/mock/endpoint'; -import { getManagementUrl } from '../../../common/routing'; +import { getPolicyDetailPath, getPoliciesPath } from '../../../common/routing'; describe('Policy Details', () => { type FindReactWrapperResponse = ReturnType<ReturnType<typeof render>['find']>; - const policyDetailsPathUrl = getManagementUrl({ - name: 'policyDetails', - policyId: '1', - excludePrefix: true, - }); - const policyListPathUrl = getManagementUrl({ name: 'policyList', excludePrefix: true }); - const policyListPathUrlWithPrefix = getManagementUrl({ name: 'policyList' }); + const policyDetailsPathUrl = getPolicyDetailPath('1'); + const policyListPathUrl = getPoliciesPath(); + const policyListPathUrlWithPrefix = getPoliciesPath(); const sleep = (ms = 100) => new Promise((wakeup) => setTimeout(wakeup, ms)); const generator = new EndpointDocGenerator(); const { history, AppWrapper, coreStart } = createAppRootMockRenderer(); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx index eb2e6b97038b0..6a48ae735180f 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.tsx @@ -38,12 +38,14 @@ import { useNavigateByRouterEventHandler } from '../../../../common/hooks/endpoi import { PageViewHeaderTitle } from '../../../../common/components/endpoint/page_view'; import { ManagementPageView } from '../../../components/management_page_view'; import { SpyRoute } from '../../../../common/utils/route/spy_routes'; -import { getManagementUrl } from '../../../common/routing'; import { SecurityPageName } from '../../../../app/types'; +import { getPoliciesPath } from '../../../common/routing'; +import { useFormatUrl } from '../../../../common/components/link_to'; export const PolicyDetails = React.memo(() => { const dispatch = useDispatch<(action: AppAction) => void>(); const { notifications } = useKibana(); + const { formatUrl, search } = useFormatUrl(SecurityPageName.management); // Store values const policyItem = usePolicyDetailsSelector(policyDetails); @@ -90,9 +92,7 @@ export const PolicyDetails = React.memo(() => { } }, [notifications.toasts, policyName, policyUpdateStatus]); - const handleBackToListOnClick = useNavigateByRouterEventHandler( - getManagementUrl({ name: 'policyList', excludePrefix: true }) - ); + const handleBackToListOnClick = useNavigateByRouterEventHandler(getPoliciesPath()); const handleSaveOnClick = useCallback(() => { setShowConfirm(true); @@ -134,7 +134,7 @@ export const PolicyDetails = React.memo(() => { iconType="arrowLeft" contentProps={{ style: { paddingLeft: '0' } }} onClick={handleBackToListOnClick} - href={getManagementUrl({ name: 'policyList' })} + href={formatUrl(getPoliciesPath(search))} > <FormattedMessage id="xpack.securitySolution.endpoint.policy.details.backToListTitle" diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.tsx index 918a0eec3fc7b..0a3cb591c4709 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.tsx @@ -38,9 +38,10 @@ import { useNavigateByRouterEventHandler } from '../../../../common/hooks/endpoi import { LinkToApp } from '../../../../common/components/endpoint/link_to_app'; import { ManagementPageView } from '../../../components/management_page_view'; import { SpyRoute } from '../../../../common/utils/route/spy_routes'; -import { getManagementUrl } from '../../../common/routing'; import { FormattedDateAndTime } from '../../../../common/components/endpoint/formatted_date_time'; import { SecurityPageName } from '../../../../app/types'; +import { useFormatUrl } from '../../../../common/components/link_to'; +import { getPolicyDetailPath } from '../../../common/routing'; interface TableChangeCallbackArguments { page: { index: number; size: number }; @@ -117,6 +118,7 @@ export const PolicyList = React.memo(() => { const { services, notifications } = useKibana(); const history = useHistory(); const location = useLocation(); + const { formatUrl, search } = useFormatUrl(SecurityPageName.management); const [showDelete, setShowDelete] = useState<boolean>(false); const [policyIdToDelete, setPolicyIdToDelete] = useState<string>(''); @@ -232,12 +234,8 @@ export const PolicyList = React.memo(() => { }), // eslint-disable-next-line react/display-name render: (name: string, item: Immutable<PolicyData>) => { - const routePath = getManagementUrl({ - name: 'policyDetails', - policyId: item.id, - excludePrefix: true, - }); - const routeUrl = getManagementUrl({ name: 'policyDetails', policyId: item.id }); + const routePath = getPolicyDetailPath(item.id, search); + const routeUrl = formatUrl(routePath); return ( <EuiFlexGroup gutterSize="s" alignItems="baseline" style={{ minWidth: 0 }}> <EuiFlexItem grow={false} style={NO_WRAP_TRUNCATE_STYLE}> @@ -349,7 +347,7 @@ export const PolicyList = React.memo(() => { ], }, ], - [services.application, handleDeleteOnClick] + [services.application, handleDeleteOnClick, formatUrl, search] ); return ( @@ -389,7 +387,7 @@ export const PolicyList = React.memo(() => { data-test-subj="policyTable" hasActions={false} /> - <SpyRoute /> + <SpyRoute pageName={SecurityPageName.management} /> </ManagementPageView> </> ); 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 3110129867628..b11f6dfdf9d87 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 @@ -70,6 +70,7 @@ describe('StatefulTimeline', () => { filters: [], id: 'foo', isLive: false, + isTimelineExists: false, itemsPerPage: 5, itemsPerPageOptions: [5, 10, 20], kqlMode: 'search', diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.test.tsx index 505d0b8cba854..5f75cd3189669 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.test.tsx @@ -16,7 +16,7 @@ import { import { createStore, State } from '../../../../common/store'; import { useThrottledResizeObserver } from '../../../../common/components/utils'; import { Properties, showDescriptionThreshold, showNotesThreshold } from '.'; -import { SiemPageName } from '../../../../app/types'; +import { SecurityPageName } from '../../../../app/types'; import { setInsertTimeline } from '../../../store/timeline/actions'; export { nextTick } from '../../../../../../../test_utils'; @@ -337,7 +337,7 @@ describe('Properties', () => { wrapper.find('[data-test-subj="settings-gear"]').at(0).simulate('click'); wrapper.find('[data-test-subj="attach-timeline-case"]').first().simulate('click'); - expect(mockHistoryPush).toBeCalledWith({ pathname: `/${SiemPageName.case}/create` }); + expect(mockHistoryPush).toBeCalledWith({ pathname: `/${SecurityPageName.case}/create` }); expect(mockDispatch).toBeCalledWith( setInsertTimeline({ timelineId: defaultProps.timelineId, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.tsx index be79c0773bf88..602a7c8191c7a 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.tsx @@ -7,7 +7,6 @@ import React, { useState, useCallback, useMemo } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { useHistory } from 'react-router-dom'; import { TimelineStatus, TimelineTypeLiteral } from '../../../../../common/types/timeline'; import { useThrottledResizeObserver } from '../../../../common/components/utils'; import { Note } from '../../../../common/lib/note'; @@ -19,11 +18,14 @@ import { TimelineProperties } from './styles'; import { PropertiesRight } from './properties_right'; import { PropertiesLeft } from './properties_left'; import { AllCasesModal } from '../../../../cases/components/all_cases_modal'; -import { SiemPageName } from '../../../../app/types'; +import { SecurityPageName } from '../../../../app/types'; import * as i18n from './translations'; import { State } from '../../../../common/store'; import { timelineSelectors } from '../../../store/timeline'; import { setInsertTimeline } from '../../../store/timeline/actions'; +import { useKibana } from '../../../../common/lib/kibana'; +import { APP_ID } from '../../../../../common/constants'; +import { getCaseDetailsUrl } from '../../../../common/components/link_to'; type CreateTimeline = ({ id, @@ -91,6 +93,7 @@ export const Properties = React.memo<Props>( updateTitle, usersViewing, }) => { + const { navigateToApp } = useKibana().services.application; const { ref, width = 0 } = useThrottledResizeObserver(300); const [showActions, setShowActions] = useState(false); const [showNotes, setShowNotes] = useState(false); @@ -110,7 +113,6 @@ export const Properties = React.memo<Props>( const [showCaseModal, setShowCaseModal] = useState(false); const onCloseCaseModal = useCallback(() => setShowCaseModal(false), []); const onOpenCaseModal = useCallback(() => setShowCaseModal(true), []); - const history = useHistory(); const currentTimeline = useSelector((state: State) => timelineSelectors.selectTimeline(state, timelineId) ); @@ -118,8 +120,8 @@ export const Properties = React.memo<Props>( const onRowClick = useCallback( (id: string) => { onCloseCaseModal(); - history.push({ - pathname: `/${SiemPageName.case}/${id}`, + navigateToApp(`${APP_ID}:${SecurityPageName.case}`, { + path: getCaseDetailsUrl({ id }), }); dispatch( setInsertTimeline({ @@ -129,7 +131,7 @@ export const Properties = React.memo<Props>( }) ); }, - [onCloseCaseModal, currentTimeline, dispatch, history, timelineId, title] + [navigateToApp, onCloseCaseModal, currentTimeline, dispatch, timelineId, title] ); const datePickerWidth = useMemo( From 6ebdb6a492a423d634d439a698632f50a2b8b992 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Mon, 15 Jun 2020 15:56:53 -0400 Subject: [PATCH 19/43] fix breadcrumbs --- .../public/common/components/navigation/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 0cbff9e70eff7..49a94a6dc21f7 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 @@ -34,7 +34,7 @@ export const SiemNavigationComponent: React.FC< const { chrome } = useKibana().services; useEffect(() => { - if (pathName) { + if (pathName || pageName) { setBreadcrumbs( { query: urlState.query, @@ -55,7 +55,7 @@ export const SiemNavigationComponent: React.FC< ); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [chrome, pathName, search, navTabs, urlState, state]); + }, [chrome, pageName, pathName, search, navTabs, urlState, state]); return ( <TabNavigation From a0fc9143c9d4d3e1cd08d6b0afc2b9e75761bf52 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Mon, 15 Jun 2020 23:53:49 -0400 Subject: [PATCH 20/43] fix unit test --- .../alerts_histogram_panel/index.test.tsx | 9 ++ .../alerts_histogram_panel/index.tsx | 2 +- .../load_empty_prompt.test.tsx | 9 ++ .../detection_engine.test.tsx | 2 + .../detection_engine/rules/all/index.test.tsx | 9 ++ .../rules/create/index.test.tsx | 8 ++ .../rules/details/index.test.tsx | 2 + .../rules/edit/index.test.tsx | 2 + .../detection_engine/rules/index.test.tsx | 8 ++ .../detection_engine/rules/utils.test.ts | 2 +- .../cases/components/all_cases/index.test.tsx | 12 ++- .../components/all_cases_modal/index.test.tsx | 9 ++ .../components/case_view/actions.test.tsx | 7 ++ .../configure_cases/button.test.tsx | 11 ++- .../components/configure_cases/button.tsx | 2 +- .../cases/components/create/index.test.tsx | 8 +- .../public/cases/components/create/index.tsx | 10 +-- .../use_push_to_service/index.test.tsx | 9 ++ .../draggable_wrapper_hover_content.test.tsx | 2 + .../event_details/event_details.test.tsx | 2 + .../event_fields_browser.test.tsx | 2 + .../__snapshots__/index.test.tsx.snap | 19 ---- .../components/header_global/index.test.tsx | 40 --------- .../common/components/header_global/index.tsx | 2 +- .../components/header_page/index.test.tsx | 9 ++ .../common/components/header_page/index.tsx | 2 +- .../components/link_to/__mocks__/index.ts | 24 +++++ .../common/components/links/index.test.tsx | 23 +++-- .../__mocks__/use_get_url_search.ts | 9 ++ .../navigation/breadcrumbs/index.test.ts | 60 ++++++------- .../components/navigation/index.test.tsx | 89 ++++++++++++------- .../navigation/tab_navigation/index.test.tsx | 21 ++--- .../common/components/top_n/index.test.tsx | 8 ++ .../common/components/top_n/top_n.test.tsx | 8 ++ .../components/hosts_table/index.test.tsx | 2 + .../uncommon_process_table/index.test.tsx | 2 + .../hosts/pages/details/details_tabs.test.tsx | 2 +- .../public/hosts/pages/details/nav_tabs.tsx | 6 ++ .../public/management/common/routing.ts | 6 +- .../pages/policy/view/policy_list.test.tsx | 2 +- .../network/components/ip/index.test.tsx | 4 +- .../network_http_table/index.test.tsx | 2 + .../network_top_n_flow_table/index.test.tsx | 2 + .../source_destination/index.test.tsx | 8 ++ .../source_destination_ip.test.tsx | 2 + .../__snapshots__/index.test.tsx.snap | 4 +- .../alerts_by_category/index.test.tsx | 2 +- .../components/event_counts/index.test.tsx | 2 + .../components/overview_host/index.test.tsx | 1 + .../overview_network/index.test.tsx | 1 + .../components/netflow/index.test.tsx | 2 + .../components/timeline/body/index.test.tsx | 2 + .../auditd/generic_row_renderer.test.tsx | 1 + .../body/renderers/formatted_field.test.tsx | 1 + .../body/renderers/get_row_renderer.test.tsx | 2 + .../netflow/netflow_row_renderer.test.tsx | 2 + .../renderers/plain_column_renderer.test.tsx | 2 + .../suricata/suricata_details.test.tsx | 2 + .../suricata/suricata_row_renderer.test.tsx | 2 + .../renderers/system/generic_details.test.tsx | 2 + .../system/generic_file_details.test.tsx | 8 ++ .../system/generic_row_renderer.test.tsx | 1 + .../body/renderers/zeek/zeek_details.test.tsx | 2 + .../renderers/zeek/zeek_row_renderer.test.tsx | 2 + .../timeline/properties/index.test.tsx | 3 + .../components/timeline/timeline.test.tsx | 10 +-- 66 files changed, 356 insertions(+), 177 deletions(-) delete mode 100644 x-pack/plugins/security_solution/public/common/components/header_global/__snapshots__/index.test.tsx.snap delete mode 100644 x-pack/plugins/security_solution/public/common/components/header_global/index.test.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/link_to/__mocks__/index.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/navigation/__mocks__/use_get_url_search.ts diff --git a/x-pack/plugins/security_solution/public/alerts/components/alerts_histogram_panel/index.test.tsx b/x-pack/plugins/security_solution/public/alerts/components/alerts_histogram_panel/index.test.tsx index 3376df76ac6ec..db783e6cc2f88 100644 --- a/x-pack/plugins/security_solution/public/alerts/components/alerts_histogram_panel/index.test.tsx +++ b/x-pack/plugins/security_solution/public/alerts/components/alerts_histogram_panel/index.test.tsx @@ -9,6 +9,15 @@ import { shallow } from 'enzyme'; import { AlertsHistogramPanel } from './index'; +jest.mock('react-router-dom', () => { + const originalModule = jest.requireActual('react-router-dom'); + return { + ...originalModule, + createHref: jest.fn(), + useHistory: jest.fn(), + }; +}); + jest.mock('../../../common/lib/kibana'); jest.mock('../../../common/components/navigation/use_get_url_search'); diff --git a/x-pack/plugins/security_solution/public/alerts/components/alerts_histogram_panel/index.tsx b/x-pack/plugins/security_solution/public/alerts/components/alerts_histogram_panel/index.tsx index fc933e5deaa92..e6eb8afc1658f 100644 --- a/x-pack/plugins/security_solution/public/alerts/components/alerts_histogram_panel/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/components/alerts_histogram_panel/index.tsx @@ -7,11 +7,11 @@ import { Position } from '@elastic/charts'; import { EuiFlexGroup, EuiFlexItem, EuiSelect, EuiPanel } from '@elastic/eui'; import numeral from '@elastic/numeral'; import React, { memo, useCallback, useMemo, useState, useEffect } from 'react'; +import { useHistory } from 'react-router-dom'; import styled from 'styled-components'; import { isEmpty } from 'lodash/fp'; import uuid from 'uuid'; -import { useHistory } from 'react-router-dom'; import { DEFAULT_NUMBER_FORMAT } from '../../../../common/constants'; import { UpdateDateRange } from '../../../common/components/charts/common'; import { LegendItem } from '../../../common/components/charts/draggable_legend_item'; diff --git a/x-pack/plugins/security_solution/public/alerts/components/rules/pre_packaged_rules/load_empty_prompt.test.tsx b/x-pack/plugins/security_solution/public/alerts/components/rules/pre_packaged_rules/load_empty_prompt.test.tsx index 8ace42fc5c3f9..77c7efec262fa 100644 --- a/x-pack/plugins/security_solution/public/alerts/components/rules/pre_packaged_rules/load_empty_prompt.test.tsx +++ b/x-pack/plugins/security_solution/public/alerts/components/rules/pre_packaged_rules/load_empty_prompt.test.tsx @@ -9,6 +9,15 @@ import { shallow } from 'enzyme'; import { PrePackagedRulesPrompt } from './load_empty_prompt'; +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useHistory: () => ({ + useHistory: jest.fn(), + }), +})); + +jest.mock('../../../../common/components/link_to'); + describe('PrePackagedRulesPrompt', () => { it('renders correctly', () => { const wrapper = shallow( diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/detection_engine.test.tsx b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/detection_engine.test.tsx index de8a732839728..62b942d03591c 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/detection_engine.test.tsx +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/detection_engine.test.tsx @@ -15,12 +15,14 @@ import { useUserInfo } from '../../components/user_info'; jest.mock('../../components/user_info'); jest.mock('../../../common/lib/kibana'); +jest.mock('../../../common/components/link_to'); jest.mock('react-router-dom', () => { const originalModule = jest.requireActual('react-router-dom'); return { ...originalModule, useParams: jest.fn(), + useHistory: jest.fn(), }; }); diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/all/index.test.tsx b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/all/index.test.tsx index 11909ae7d9c53..363550f1682e0 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/all/index.test.tsx +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/all/index.test.tsx @@ -13,6 +13,15 @@ import { TestProviders } from '../../../../../common/mock'; import { wait } from '../../../../../common/lib/helpers'; import { AllRules } from './index'; +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useHistory: () => ({ + useHistory: jest.fn(), + }), +})); + +jest.mock('../../../../../common/components/link_to'); + jest.mock('./reducer', () => { return { allRulesReducer: jest.fn().mockReturnValue(() => ({ diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/create/index.test.tsx b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/create/index.test.tsx index 7749e38578e90..cc39a26a8faca 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/create/index.test.tsx +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/create/index.test.tsx @@ -11,6 +11,14 @@ import { TestProviders } from '../../../../../common/mock'; import { CreateRulePage } from './index'; import { useUserInfo } from '../../../../components/user_info'; +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useHistory: () => ({ + useHistory: jest.fn(), + }), +})); + +jest.mock('../../../../../common/components/link_to'); jest.mock('../../../../components/user_info'); describe('CreateRulePage', () => { diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/details/index.test.tsx b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/details/index.test.tsx index d755f972f2950..df6ea65ba52ba 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/details/index.test.tsx +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/details/index.test.tsx @@ -14,6 +14,7 @@ import { setAbsoluteRangeDatePicker } from '../../../../../common/store/inputs/a import { useUserInfo } from '../../../../components/user_info'; import { useParams } from 'react-router-dom'; +jest.mock('../../../../../common/components/link_to'); jest.mock('../../../../components/user_info'); jest.mock('react-router-dom', () => { const originalModule = jest.requireActual('react-router-dom'); @@ -21,6 +22,7 @@ jest.mock('react-router-dom', () => { return { ...originalModule, useParams: jest.fn(), + useHistory: jest.fn(), }; }); diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/edit/index.test.tsx b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/edit/index.test.tsx index 91bc2ce7bce25..d754329bdd97f 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/edit/index.test.tsx +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/edit/index.test.tsx @@ -12,12 +12,14 @@ import { EditRulePage } from './index'; import { useUserInfo } from '../../../../components/user_info'; import { useParams } from 'react-router-dom'; +jest.mock('../../../../../common/components/link_to'); jest.mock('../../../../components/user_info'); jest.mock('react-router-dom', () => { const originalModule = jest.requireActual('react-router-dom'); return { ...originalModule, + useHistory: jest.fn(), useParams: jest.fn(), }; }); diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/index.test.tsx b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/index.test.tsx index 29f875d113a42..1c947deb95f97 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/index.test.tsx +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/index.test.tsx @@ -11,6 +11,14 @@ import { RulesPage } from './index'; import { useUserInfo } from '../../../components/user_info'; import { usePrePackagedRules } from '../../../../alerts/containers/detection_engine/rules'; +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useHistory: () => ({ + useHistory: jest.fn(), + }), +})); + +jest.mock('../../../../common/components/link_to'); jest.mock('../../../components/user_info'); jest.mock('../../../../alerts/containers/detection_engine/rules'); diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/utils.test.ts b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/utils.test.ts index 3d2f2dc03946a..3b703b5db4c8a 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/utils.test.ts +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/utils.test.ts @@ -19,6 +19,6 @@ describe('getBreadcrumbs', () => { }, [] ) - ).toEqual([{ href: '#/link-to/detections', text: 'Alerts' }]); + ).toEqual([{ href: '', text: 'Alerts' }]); }); }); diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx index bbb96f433d3c8..ed8ec432f7df5 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.test.tsx @@ -29,6 +29,16 @@ const useGetCasesMock = useGetCases as jest.Mock; const useGetCasesStatusMock = useGetCasesStatus as jest.Mock; const useUpdateCasesMock = useUpdateCases as jest.Mock; +jest.mock('react-router-dom', () => { + const originalModule = jest.requireActual('react-router-dom'); + return { + ...originalModule, + useHistory: jest.fn(), + }; +}); + +jest.mock('../../../common/components/link_to'); + describe('AllCases', () => { const dispatchResetIsDeleted = jest.fn(); const dispatchResetIsUpdated = jest.fn(); @@ -98,7 +108,7 @@ describe('AllCases', () => { </TestProviders> ); expect(wrapper.find(`a[data-test-subj="case-details-link"]`).first().prop('href')).toEqual( - `#/link-to/case/${useGetCasesMockState.data.cases[0].id}?timerange=(global:(linkTo:!(timeline),timerange:(from:0,fromStr:now-24h,kind:relative,to:1,toStr:now)),timeline:(linkTo:!(global),timerange:(from:0,fromStr:now-24h,kind:relative,to:1,toStr:now)))` + `/${useGetCasesMockState.data.cases[0].id}` ); expect(wrapper.find(`a[data-test-subj="case-details-link"]`).first().text()).toEqual( useGetCasesMockState.data.cases[0].title diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases_modal/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases_modal/index.test.tsx index a24cb6a87de74..67b0f56367462 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases_modal/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases_modal/index.test.tsx @@ -15,6 +15,15 @@ import { useGetCasesStatus } from '../../containers/use_get_cases_status'; import { useUpdateCases } from '../../containers/use_bulk_update_case'; import { EuiTableRow } from '@elastic/eui'; +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useHistory: () => ({ + useHistory: jest.fn(), + }), +})); + +jest.mock('../../../common/components/link_to'); + jest.mock('../../containers/use_bulk_update_case'); jest.mock('../../containers/use_delete_cases'); jest.mock('../../containers/use_get_cases'); diff --git a/x-pack/plugins/security_solution/public/cases/components/case_view/actions.test.tsx b/x-pack/plugins/security_solution/public/cases/components/case_view/actions.test.tsx index 2641ac68cf952..1721cb5f819f0 100644 --- a/x-pack/plugins/security_solution/public/cases/components/case_view/actions.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/case_view/actions.test.tsx @@ -15,6 +15,13 @@ import * as i18n from './translations'; jest.mock('../../containers/use_delete_cases'); const useDeleteCasesMock = useDeleteCases as jest.Mock; +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useHistory: () => ({ + useHistory: jest.fn(), + }), +})); + describe('CaseView actions', () => { const handleOnDeleteConfirm = jest.fn(); const handleToggleModal = jest.fn(); diff --git a/x-pack/plugins/security_solution/public/cases/components/configure_cases/button.test.tsx b/x-pack/plugins/security_solution/public/cases/components/configure_cases/button.test.tsx index aefe515f4bd4d..1315cf1c962e7 100644 --- a/x-pack/plugins/security_solution/public/cases/components/configure_cases/button.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/configure_cases/button.test.tsx @@ -12,6 +12,15 @@ import { ConfigureCaseButton, ConfigureCaseButtonProps } from './button'; import { TestProviders } from '../../../common/mock'; import { searchURL } from './__mock__'; +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useHistory: () => ({ + useHistory: jest.fn(), + }), +})); + +jest.mock('../../../common/components/link_to'); + describe('Configuration button', () => { let wrapper: ReactWrapper; const props: ConfigureCaseButtonProps = { @@ -35,7 +44,7 @@ describe('Configuration button', () => { test('it pass the correct props to the button', () => { expect(wrapper.find('[data-test-subj="configure-case-button"]').first().props()).toMatchObject({ - href: `#/link-to/case/configure${searchURL}`, + href: `/configure`, iconType: 'controlsHorizontal', isDisabled: false, 'aria-label': 'My label', diff --git a/x-pack/plugins/security_solution/public/cases/components/configure_cases/button.tsx b/x-pack/plugins/security_solution/public/cases/components/configure_cases/button.tsx index 69c9e2c30f930..44767471dd9e7 100644 --- a/x-pack/plugins/security_solution/public/cases/components/configure_cases/button.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/configure_cases/button.tsx @@ -51,7 +51,7 @@ const ConfigureCaseButtonComponent: React.FC<ConfigureCaseButtonProps> = ({ {label} </LinkButton> ), - [label, isDisabled, urlSearch] + [label, isDisabled, formatUrl, goToCaseConfigure] ); return showToolTip ? ( <EuiToolTip diff --git a/x-pack/plugins/security_solution/public/cases/components/create/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/create/index.test.tsx index 0336f97b135d7..25c12a53f2f5b 100644 --- a/x-pack/plugins/security_solution/public/cases/components/create/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/create/index.test.tsx @@ -20,7 +20,7 @@ jest.mock('../../../timelines/components/timeline/insert_timeline_popover/use_in jest.mock('../../containers/use_post_case'); import { useForm } from '../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form'; import { wait } from '../../../common/lib/helpers'; -import { SecurityPageName } from '../../../app/types'; + jest.mock( '../../../../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form' ); @@ -110,7 +110,7 @@ describe('Create case', () => { </TestProviders> ); wrapper.find(`[data-test-subj="create-case-cancel"]`).first().simulate('click'); - expect(mockHistory.replace.mock.calls[0][0].pathname).toEqual(`/${SecurityPageName.case}`); + expect(mockHistory.push).toHaveBeenCalledWith('/'); }); it('should redirect to new case when caseData is there', () => { const sampleId = '777777'; @@ -122,9 +122,7 @@ describe('Create case', () => { </Router> </TestProviders> ); - expect(mockHistory.replace.mock.calls[0][0].pathname).toEqual( - `/${SecurityPageName.case}/${sampleId}` - ); + expect(mockHistory.push).toHaveBeenNthCalledWith(1, '/777777'); }); it('should render spinner when loading', () => { diff --git a/x-pack/plugins/security_solution/public/cases/components/create/index.tsx b/x-pack/plugins/security_solution/public/cases/components/create/index.tsx index f0ec4fd9422d8..9f078c725c3cf 100644 --- a/x-pack/plugins/security_solution/public/cases/components/create/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/create/index.tsx @@ -63,7 +63,6 @@ const initialCaseValue: CasePostRequest = { export const Create = React.memo(() => { const history = useHistory(); const { caseData, isLoading, postCase } = usePostCase(); - const [isCancel, setIsCancel] = useState(false); const { form } = useForm<CasePostRequest>({ defaultValue: initialCaseValue, options: { stripEmptyFields: false }, @@ -99,19 +98,14 @@ export const Create = React.memo(() => { }, [form]); const handleSetIsCancel = useCallback(() => { - setIsCancel(true); - }, []); + history.push('/'); + }, [history]); if (caseData != null && caseData.id) { history.push(getCaseDetailsUrl({ id: caseData.id })); return null; } - if (isCancel) { - history.push('/'); - return null; - } - return ( <EuiPanel> {isLoading && <MySpinner data-test-subj="create-case-loading-spinner" size="xl" />} diff --git a/x-pack/plugins/security_solution/public/cases/components/use_push_to_service/index.test.tsx b/x-pack/plugins/security_solution/public/cases/components/use_push_to_service/index.test.tsx index 4391db1a0a0a1..9dfeec3b26ba9 100644 --- a/x-pack/plugins/security_solution/public/cases/components/use_push_to_service/index.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/use_push_to_service/index.test.tsx @@ -14,6 +14,15 @@ import * as i18n from './translations'; import { useGetActionLicense } from '../../containers/use_get_action_license'; import { getKibanaConfigError, getLicenseError } from './helpers'; import { connectorsMock } from '../../containers/configure/mock'; + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useHistory: () => ({ + useHistory: jest.fn(), + }), +})); + +jest.mock('../../../common/components/link_to'); jest.mock('../../containers/use_get_action_license'); jest.mock('../../containers/use_post_push_to_service'); jest.mock('../../containers/configure/api'); diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx index aa5efe3ccfe6a..e60d876617dca 100644 --- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx @@ -22,6 +22,8 @@ import { timelineDefaults, } from '../../../timelines/components/manage_timeline'; +jest.mock('../link_to'); + jest.mock('../../lib/kibana'); jest.mock('uuid', () => { diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx index cb2cbcc62aba1..30c7d2a742b57 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_details.test.tsx @@ -15,6 +15,8 @@ import { mockBrowserFields } from '../../containers/source/mock'; import { defaultHeaders } from '../../mock/header'; import { useMountAppended } from '../../utils/use_mount_appended'; +jest.mock('../link_to'); + describe('EventDetails', () => { const mount = useMountAppended(); diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.test.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.test.tsx index 9f60d16a6f671..c0cc1c04488b2 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.test.tsx @@ -14,6 +14,8 @@ import { mockBrowserFields } from '../../containers/source/mock'; import { defaultHeaders } from '../../mock/header'; import { useMountAppended } from '../../utils/use_mount_appended'; +jest.mock('../link_to'); + describe('EventFieldsBrowser', () => { const mount = useMountAppended(); diff --git a/x-pack/plugins/security_solution/public/common/components/header_global/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/header_global/__snapshots__/index.test.tsx.snap deleted file mode 100644 index 25374c63fa897..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/header_global/__snapshots__/index.test.tsx.snap +++ /dev/null @@ -1,19 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`HeaderGlobal it renders 1`] = ` -<Wrapper - className="siemHeaderGlobal" -> - <EuiFlexGroup - alignItems="center" - justifyContent="spaceBetween" - wrap={true} - > - <WithSource - sourceId="default" - > - <Component /> - </WithSource> - </EuiFlexGroup> -</Wrapper> -`; diff --git a/x-pack/plugins/security_solution/public/common/components/header_global/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/header_global/index.test.tsx deleted file mode 100644 index 809f0eeb811f4..0000000000000 --- a/x-pack/plugins/security_solution/public/common/components/header_global/index.test.tsx +++ /dev/null @@ -1,40 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { shallow } from 'enzyme'; -import React from 'react'; - -import '../../mock/match_media'; -import { HeaderGlobal } from './index'; - -jest.mock('react-router-dom', () => ({ - useLocation: () => ({ - pathname: '/app/siem#/hosts/allHosts', - hash: '', - search: '', - state: '', - }), - withRouter: () => jest.fn(), - generatePath: jest.fn(), -})); - -// Test will fail because we will to need to mock some core services to make the test work -// For now let's forget about SiemSearchBar -jest.mock('../search_bar', () => ({ - SiemSearchBar: () => null, -})); - -describe('HeaderGlobal', () => { - beforeEach(() => { - jest.resetAllMocks(); - }); - - test('it renders', () => { - const wrapper = shallow(<HeaderGlobal />); - - expect(wrapper).toMatchSnapshot(); - }); -}); diff --git a/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx b/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx index 82737a66f1c5f..19944aabf0dc2 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx @@ -7,9 +7,9 @@ import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui'; import { pickBy } from 'lodash/fp'; import React, { useCallback } from 'react'; +import { useLocation } from 'react-router-dom'; import styled, { css } from 'styled-components'; -import { useLocation } from 'react-router-dom'; import { gutterTimeline } from '../../lib/helpers'; import { navTabs } from '../../../app/home/home_navigations'; import { SecurityPageName } from '../../../app/types'; diff --git a/x-pack/plugins/security_solution/public/common/components/header_page/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/header_page/index.test.tsx index db88e0e07db5c..ef838fc50d108 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_page/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/header_page/index.test.tsx @@ -13,6 +13,15 @@ import { HeaderPage } from './index'; import { useMountAppended } from '../../utils/use_mount_appended'; import { SecurityPageName } from '../../../app/types'; +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useHistory: () => ({ + useHistory: jest.fn(), + }), +})); + +jest.mock('../link_to'); + describe('HeaderPage', () => { const mount = useMountAppended(); diff --git a/x-pack/plugins/security_solution/public/common/components/header_page/index.tsx b/x-pack/plugins/security_solution/public/common/components/header_page/index.tsx index 78138183d010d..62880e7510cd2 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_page/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/header_page/index.tsx @@ -6,9 +6,9 @@ import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiProgress } from '@elastic/eui'; import React, { useCallback } from 'react'; +import { useHistory } from 'react-router-dom'; import styled, { css } from 'styled-components'; -import { useHistory } from 'react-router-dom'; import { LinkIcon, LinkIconProps } from '../link_icon'; import { Subtitle, SubtitleProps } from '../subtitle'; import { Title } from './title'; diff --git a/x-pack/plugins/security_solution/public/common/components/link_to/__mocks__/index.ts b/x-pack/plugins/security_solution/public/common/components/link_to/__mocks__/index.ts new file mode 100644 index 0000000000000..6c9620e27fabf --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/link_to/__mocks__/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SecurityPageName } from '../../../../app/types'; + +export { getDetectionEngineUrl } from '../redirect_to_detection_engine'; +export { getAppOverviewUrl } from '../redirect_to_overview'; +export { getHostDetailsUrl, getHostsUrl } from '../redirect_to_hosts'; +export { getNetworkUrl, getIPDetailsUrl } from '../redirect_to_network'; +export { getTimelinesUrl, getTimelineTabsUrl } from '../redirect_to_timelines'; +export { + getCaseDetailsUrl, + getCaseUrl, + getCreateCaseUrl, + getConfigureCasesUrl, +} from '../redirect_to_case'; + +export const useFormatUrl = (page: SecurityPageName) => ({ + formatUrl: (path: string) => path, + search: '', +}); diff --git a/x-pack/plugins/security_solution/public/common/components/links/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/links/index.test.tsx index 9eff86bffb369..b6817c4cab1f2 100644 --- a/x-pack/plugins/security_solution/public/common/components/links/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/links/index.test.tsx @@ -24,11 +24,20 @@ import { ExternalLink, } from '.'; +jest.mock('../link_to'); + jest.mock('../../../overview/components/events_by_dataset'); jest.mock('../../lib/kibana', () => { return { useUiSetting$: jest.fn(), + useKibana: () => ({ + services: { + application: { + navigateToApp: jest.fn(), + }, + }, + }), }; }); @@ -41,17 +50,13 @@ describe('Custom Links', () => { describe('HostDetailsLink', () => { test('should render valid link to Host Details with hostName as the display text', () => { const wrapper = mount(<HostDetailsLink hostName={hostName} />); - expect(wrapper.find('EuiLink').prop('href')).toEqual( - `#/link-to/hosts/${encodeURIComponent(hostName)}` - ); + expect(wrapper.find('EuiLink').prop('href')).toEqual(`/${encodeURIComponent(hostName)}`); expect(wrapper.text()).toEqual(hostName); }); test('should render valid link to Host Details with child text as the display text', () => { const wrapper = mount(<HostDetailsLink hostName={hostName}>{hostName}</HostDetailsLink>); - expect(wrapper.find('EuiLink').prop('href')).toEqual( - `#/link-to/hosts/${encodeURIComponent(hostName)}` - ); + expect(wrapper.find('EuiLink').prop('href')).toEqual(`/${encodeURIComponent(hostName)}`); expect(wrapper.text()).toEqual(hostName); }); }); @@ -60,7 +65,7 @@ describe('Custom Links', () => { test('should render valid link to IP Details with ipv4 as the display text', () => { const wrapper = mount(<IPDetailsLink ip={ipv4} />); expect(wrapper.find('EuiLink').prop('href')).toEqual( - `#/link-to/network/ip/${encodeURIComponent(ipv4)}/source` + `/ip/${encodeURIComponent(ipv4)}/source` ); expect(wrapper.text()).toEqual(ipv4); }); @@ -68,7 +73,7 @@ describe('Custom Links', () => { test('should render valid link to IP Details with child text as the display text', () => { const wrapper = mount(<IPDetailsLink ip={ipv4}>{hostName}</IPDetailsLink>); expect(wrapper.find('EuiLink').prop('href')).toEqual( - `#/link-to/network/ip/${encodeURIComponent(ipv4)}/source` + `/ip/${encodeURIComponent(ipv4)}/source` ); expect(wrapper.text()).toEqual(hostName); }); @@ -76,7 +81,7 @@ describe('Custom Links', () => { test('should render valid link to IP Details with ipv6 as the display text', () => { const wrapper = mount(<IPDetailsLink ip={ipv6} />); expect(wrapper.find('EuiLink').prop('href')).toEqual( - `#/link-to/network/ip/${encodeURIComponent(ipv6Encoded)}/source` + `/ip/${encodeURIComponent(ipv6Encoded)}/source` ); expect(wrapper.text()).toEqual(ipv6); }); diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/__mocks__/use_get_url_search.ts b/x-pack/plugins/security_solution/public/common/components/navigation/__mocks__/use_get_url_search.ts new file mode 100644 index 0000000000000..d8a1db0aae70d --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/navigation/__mocks__/use_get_url_search.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SearchNavTab } from '../types'; + +export const useGetUrlSearch = (tab: SearchNavTab) => ''; diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts index 8f1318aea3763..75ac189ff784e 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts @@ -38,28 +38,28 @@ const getMockObject = ( navTabs: { hosts: { disabled: false, - href: '#/link-to/hosts', + href: '/app/security/hosts', id: 'hosts', name: 'Hosts', urlKey: 'host', }, network: { disabled: false, - href: '#/link-to/network', + href: '/app/security/network', id: 'network', name: 'Network', urlKey: 'network', }, overview: { disabled: false, - href: '#/link-to/overview', + href: '/app/security/overview', id: 'overview', name: 'Overview', urlKey: 'overview', }, timelines: { disabled: false, - href: '#/link-to/timelines', + href: '/app/security/timelines', id: 'timelines', name: 'Timelines', urlKey: 'timeline', @@ -108,15 +108,15 @@ describe('Navigation Breadcrumbs', () => { describe('getBreadcrumbsForRoute', () => { test('should return Host breadcrumbs when supplied host pathname', () => { - const breadcrumbs = getBreadcrumbsForRoute(getMockObject('hosts', '/hosts', undefined)); + const breadcrumbs = getBreadcrumbsForRoute(getMockObject('hosts', '/', undefined)); expect(breadcrumbs).toEqual([ { - href: '#/link-to/overview', + href: '/app/security/overview', text: 'Security', }, { href: - '#/link-to/hosts?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', + '?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', text: 'Hosts', }, { @@ -127,13 +127,13 @@ describe('Navigation Breadcrumbs', () => { }); test('should return Network breadcrumbs when supplied network pathname', () => { - const breadcrumbs = getBreadcrumbsForRoute(getMockObject('network', '/network', undefined)); + const breadcrumbs = getBreadcrumbsForRoute(getMockObject('network', '/', undefined)); expect(breadcrumbs).toEqual([ - { text: 'Security', href: '#/link-to/overview' }, + { text: 'Security', href: '/app/security/overview' }, { text: 'Network', href: - '#/link-to/network?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', + '?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', }, { text: 'Flows', @@ -143,62 +143,60 @@ describe('Navigation Breadcrumbs', () => { }); test('should return Timelines breadcrumbs when supplied timelines pathname', () => { - const breadcrumbs = getBreadcrumbsForRoute( - getMockObject('timelines', '/timelines', undefined) - ); + const breadcrumbs = getBreadcrumbsForRoute(getMockObject('timelines', '/', undefined)); expect(breadcrumbs).toEqual([ - { text: 'Security', href: '#/link-to/overview' }, - { text: 'Timelines', href: '#/link-to/timelines' }, + { text: 'Security', href: '/app/security/overview' }, + { text: 'Timelines', href: '' }, ]); }); test('should return Host Details breadcrumbs when supplied a pathname with hostName', () => { - const breadcrumbs = getBreadcrumbsForRoute(getMockObject('hosts', '/hosts', hostName)); + const breadcrumbs = getBreadcrumbsForRoute(getMockObject('hosts', '/', hostName)); expect(breadcrumbs).toEqual([ - { text: 'Security', href: '#/link-to/overview' }, + { text: 'Security', href: '/app/security/overview' }, { text: 'Hosts', href: - '#/link-to/hosts?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', + '?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', }, { text: 'siem-kibana', href: - '#/link-to/hosts/siem-kibana?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', + '/siem-kibana?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', }, { text: 'Authentications', href: '' }, ]); }); test('should return IP Details breadcrumbs when supplied pathname with ipv4', () => { - const breadcrumbs = getBreadcrumbsForRoute(getMockObject('network', '/network', ipv4)); + const breadcrumbs = getBreadcrumbsForRoute(getMockObject('network', '/', ipv4)); expect(breadcrumbs).toEqual([ - { text: 'Security', href: '#/link-to/overview' }, + { text: 'Security', href: '/app/security/overview' }, { text: 'Network', href: - '#/link-to/network?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', + '?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', }, { text: ipv4, - href: `#/link-to/network/ip/${ipv4}/source?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))`, + href: `/ip/${ipv4}/source?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))`, }, { text: 'Flows', href: '' }, ]); }); test('should return IP Details breadcrumbs when supplied pathname with ipv6', () => { - const breadcrumbs = getBreadcrumbsForRoute(getMockObject('network', '/network', ipv6Encoded)); + const breadcrumbs = getBreadcrumbsForRoute(getMockObject('network', '/', ipv6Encoded)); expect(breadcrumbs).toEqual([ - { text: 'Security', href: '#/link-to/overview' }, + { text: 'Security', href: '/app/security/overview' }, { text: 'Network', href: - '#/link-to/network?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', + '?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', }, { text: ipv6, - href: `#/link-to/network/ip/${ipv6Encoded}/source?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))`, + href: `/ip/${ipv6Encoded}/source?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))`, }, { text: 'Flows', href: '' }, ]); @@ -207,18 +205,18 @@ describe('Navigation Breadcrumbs', () => { describe('setBreadcrumbs()', () => { test('should call chrome breadcrumb service with correct breadcrumbs', () => { - setBreadcrumbs(getMockObject('hosts', '/hosts', hostName), chromeMock); + setBreadcrumbs(getMockObject('hosts', '/', hostName), chromeMock); expect(setBreadcrumbsMock).toBeCalledWith([ - { text: 'Security', href: '#/link-to/overview' }, + { text: 'Security', href: '/app/security/overview' }, { text: 'Hosts', href: - '#/link-to/hosts?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', + '?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', }, { text: 'siem-kibana', href: - '#/link-to/hosts/siem-kibana?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', + '/siem-kibana?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', }, { text: 'Authentications', href: '' }, ]); diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/index.test.tsx index fd96885e5bc10..b4b37e22057de 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/index.test.tsx @@ -15,14 +15,34 @@ import { HostsTableType } from '../../../hosts/store/model'; import { RouteSpyState } from '../../utils/route/types'; import { SiemNavigationProps, SiemNavigationComponentProps } from './types'; +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useHistory: () => ({ + useHistory: jest.fn(), + }), +})); + jest.mock('./breadcrumbs', () => ({ setBreadcrumbs: jest.fn(), })); +jest.mock('../../lib/kibana', () => { + return { + useKibana: () => ({ + services: { + chrome: undefined, + application: { + navigateToApp: jest.fn(), + }, + }, + }), + }; +}); +jest.mock('../link_to'); describe('SIEM Navigation', () => { const mockProps: SiemNavigationComponentProps & SiemNavigationProps & RouteSpyState = { pageName: 'hosts', - pathName: '/hosts', + pathName: '/', detailName: undefined, search: '', tabName: HostsTableType.authentications, @@ -65,63 +85,64 @@ describe('SIEM Navigation', () => { { detailName: undefined, navTabs: { + alerts: { + disabled: false, + href: '/app/security/alerts', + id: 'alerts', + name: 'Alerts', + urlKey: 'alerts', + }, case: { disabled: false, - href: '#/link-to/case', + href: '/app/security/cases', id: 'case', name: 'Cases', urlKey: 'case', }, management: { disabled: false, - href: '#/management', + href: '/app/security/management', id: 'management', name: 'Management', urlKey: 'management', }, - detections: { - disabled: false, - href: '#/link-to/detections', - id: 'detections', - name: 'Detections', - urlKey: 'detections', - }, hosts: { disabled: false, - href: '#/link-to/hosts', + href: '/app/security/hosts', id: 'hosts', name: 'Hosts', urlKey: 'host', }, network: { disabled: false, - href: '#/link-to/network', + href: '/app/security/network', id: 'network', name: 'Network', urlKey: 'network', }, overview: { disabled: false, - href: '#/link-to/overview', + href: '/app/security/overview', id: 'overview', name: 'Overview', urlKey: 'overview', }, timelines: { disabled: false, - href: '#/link-to/timelines', + href: '/app/security/timelines', id: 'timelines', name: 'Timelines', urlKey: 'timeline', }, }, pageName: 'hosts', - pathName: '/hosts', + pathName: '/', search: '', state: undefined, tabName: 'authentications', query: { query: '', language: 'kuery' }, filters: [], + flowTarget: undefined, savedQuery: undefined, timeline: { id: '', @@ -156,73 +177,75 @@ describe('SIEM Navigation', () => { test('it calls setBreadcrumbs with correct path on update', () => { wrapper.setProps({ pageName: 'network', - pathName: '/network', + pathName: '/', tabName: undefined, }); wrapper.update(); expect(setBreadcrumbs).toHaveBeenNthCalledWith( - 1, + 2, { detailName: undefined, filters: [], + flowTarget: undefined, navTabs: { + alerts: { + disabled: false, + href: '/app/security/alerts', + id: 'alerts', + name: 'Alerts', + urlKey: 'alerts', + }, case: { disabled: false, - href: '#/link-to/case', + href: '/app/security/cases', id: 'case', name: 'Cases', urlKey: 'case', }, - detections: { - disabled: false, - href: '#/link-to/detections', - id: 'detections', - name: 'Detections', - urlKey: 'detections', - }, + hosts: { disabled: false, - href: '#/link-to/hosts', + href: '/app/security/hosts', id: 'hosts', name: 'Hosts', urlKey: 'host', }, management: { disabled: false, - href: '#/management', + href: '/app/security/management', id: 'management', name: 'Management', urlKey: 'management', }, network: { disabled: false, - href: '#/link-to/network', + href: '/app/security/network', id: 'network', name: 'Network', urlKey: 'network', }, overview: { disabled: false, - href: '#/link-to/overview', + href: '/app/security/overview', id: 'overview', name: 'Overview', urlKey: 'overview', }, timelines: { disabled: false, - href: '#/link-to/timelines', + href: '/app/security/timelines', id: 'timelines', name: 'Timelines', urlKey: 'timeline', }, }, - pageName: 'hosts', - pathName: '/hosts', + pageName: 'network', + pathName: '/', query: { language: 'kuery', query: '' }, savedQuery: undefined, search: '', state: undefined, - tabName: 'authentications', + tabName: undefined, timeline: { id: '', isOpen: false }, timerange: { global: { diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx index a8dde7d2c41c9..99d6f7840a7d5 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.test.tsx @@ -16,6 +16,16 @@ import { CONSTANTS } from '../../url_state/constants'; import { TabNavigationComponent } from './'; import { TabNavigationProps } from './types'; +jest.mock('../../../lib/kibana'); +jest.mock('../../link_to'); + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useHistory: () => ({ + push: jest.fn(), + }), +})); + describe('Tab Navigation', () => { const pageName = SecurityPageName.hosts; const hostName = 'siem-window'; @@ -76,13 +86,6 @@ describe('Tab Navigation', () => { wrapper.update(); expect(networkTab().prop('isSelected')).toBeTruthy(); }); - test('it carries the url state in the link', () => { - const wrapper = mount(<TabNavigationComponent {...mockProps} />); - const firstTab = wrapper.find('EuiTab[data-test-subj="navigation-network"]'); - expect(firstTab.props().href).toBe( - "#/link-to/network?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))" - ); - }); }); describe('Table Navigation', () => { @@ -149,9 +152,7 @@ describe('Tab Navigation', () => { const firstTab = wrapper.find( `EuiTab[data-test-subj="navigation-${HostsTableType.authentications}"]` ); - expect(firstTab.props().href).toBe( - `#/${pageName}/${hostName}/${HostsTableType.authentications}?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))` - ); + expect(firstTab.props().href).toBe('/siem-window/authentications'); }); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx index aa310d63ab283..66224dc8c84df 100644 --- a/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx @@ -25,6 +25,14 @@ import { timelineDefaults, } from '../../../timelines/components/manage_timeline'; +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useHistory: () => ({ + useHistory: jest.fn(), + }), +})); + +jest.mock('../link_to'); jest.mock('../../lib/kibana'); jest.mock('../../../timelines/store/timeline/actions'); diff --git a/x-pack/plugins/security_solution/public/common/components/top_n/top_n.test.tsx b/x-pack/plugins/security_solution/public/common/components/top_n/top_n.test.tsx index 5e1476f62216b..62960d4fae71b 100644 --- a/x-pack/plugins/security_solution/public/common/components/top_n/top_n.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/top_n/top_n.test.tsx @@ -13,7 +13,15 @@ import { setAbsoluteRangeDatePicker } from '../../store/inputs/actions'; import { allEvents, defaultOptions } from './helpers'; import { TopN } from './top_n'; +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useHistory: () => ({ + useHistory: jest.fn(), + }), +})); + jest.mock('../../lib/kibana'); +jest.mock('../link_to'); jest.mock('uuid', () => { return { diff --git a/x-pack/plugins/security_solution/public/hosts/components/hosts_table/index.test.tsx b/x-pack/plugins/security_solution/public/hosts/components/hosts_table/index.test.tsx index 45779bf37c77f..1c02c4dd32b85 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/hosts_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/hosts_table/index.test.tsx @@ -32,6 +32,8 @@ jest.mock('../../../common/components/query_bar', () => ({ QueryBar: () => null, })); +jest.mock('../../../common/components/link_to'); + describe('Hosts Table', () => { const loadPage = jest.fn(); const state: State = mockGlobalState; diff --git a/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/index.test.tsx b/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/index.test.tsx index 7528c148f2456..a21d932e837df 100644 --- a/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/hosts/components/uncommon_process_table/index.test.tsx @@ -18,6 +18,8 @@ import { mockData } from './mock'; import { HostsType } from '../../store/model'; import * as i18n from './translations'; +jest.mock('../../../common/components/link_to'); + describe('Uncommon Process Table Component', () => { const loadPage = jest.fn(); const mount = useMountAppended(); 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 fa76dc93375e0..936789625a4dd 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 @@ -71,7 +71,7 @@ describe('body', () => { test(`it should pass expected object properties to ${componentName}`, () => { const wrapper = mount( <TestProviders> - <MemoryRouter initialEntries={[`/hosts/host-1/${path}`]}> + <MemoryRouter initialEntries={[`/host-1/${path}`]}> <HostDetailsTabs from={0} isInitializing={false} diff --git a/x-pack/plugins/security_solution/public/hosts/pages/details/nav_tabs.tsx b/x-pack/plugins/security_solution/public/hosts/pages/details/nav_tabs.tsx index c3438aca93b60..0d30ef32e5d98 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/details/nav_tabs.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/details/nav_tabs.tsx @@ -8,6 +8,7 @@ import { omit } from 'lodash/fp'; import * as i18n from '../translations'; import { HostDetailsNavTab } from './types'; import { HostsTableType } from '../../store/model'; +import { SecurityPageName } from '../../../app/types'; const getTabsOnHostDetailsUrl = (hostName: string, tabName: HostsTableType) => `/${hostName}/${tabName}`; @@ -24,6 +25,7 @@ export const navTabsHostDetails = ( disabled: false, urlKey: 'host', isDetailPage: true, + pageId: SecurityPageName.hosts, }, [HostsTableType.uncommonProcesses]: { id: HostsTableType.uncommonProcesses, @@ -32,6 +34,7 @@ export const navTabsHostDetails = ( disabled: false, urlKey: 'host', isDetailPage: true, + pageId: SecurityPageName.hosts, }, [HostsTableType.anomalies]: { id: HostsTableType.anomalies, @@ -40,6 +43,7 @@ export const navTabsHostDetails = ( disabled: false, urlKey: 'host', isDetailPage: true, + pageId: SecurityPageName.hosts, }, [HostsTableType.events]: { id: HostsTableType.events, @@ -48,6 +52,7 @@ export const navTabsHostDetails = ( disabled: false, urlKey: 'host', isDetailPage: true, + pageId: SecurityPageName.hosts, }, [HostsTableType.alerts]: { id: HostsTableType.alerts, @@ -55,6 +60,7 @@ export const navTabsHostDetails = ( href: getTabsOnHostDetailsUrl(hostName, HostsTableType.alerts), disabled: false, urlKey: 'host', + pageId: SecurityPageName.hosts, }, }; diff --git a/x-pack/plugins/security_solution/public/management/common/routing.ts b/x-pack/plugins/security_solution/public/management/common/routing.ts index 3d5b1f5ca6193..0fc6b413ff85c 100644 --- a/x-pack/plugins/security_solution/public/management/common/routing.ts +++ b/x-pack/plugins/security_solution/public/management/common/routing.ts @@ -46,7 +46,7 @@ export const getEndpointListPath = ( const urlSearch = `${urlQueryParams && !isEmpty(search) ? '&' : ''}${search ?? ''}`; if (name === 'endpointList') { - return `/${generatePath(MANAGEMENT_ROUTING_ENDPOINTS_PATH, { + return `${generatePath(MANAGEMENT_ROUTING_ENDPOINTS_PATH, { tabName: ManagementSubTab.endpoints, })}${appendSearch(`${urlQueryParams ? `${urlQueryParams}${urlSearch}` : urlSearch}`)}`; } @@ -72,12 +72,12 @@ export const getEndpointDetailsPath = ( }; export const getPoliciesPath = (search?: string) => - `/${generatePath(MANAGEMENT_ROUTING_POLICIES_PATH, { + `${generatePath(MANAGEMENT_ROUTING_POLICIES_PATH, { tabName: ManagementSubTab.policies, })}${appendSearch(search ?? undefined)}`; export const getPolicyDetailPath = (policyId: string, search?: string) => - `/${generatePath(MANAGEMENT_ROUTING_POLICY_DETAILS_PATH, { + `${generatePath(MANAGEMENT_ROUTING_POLICY_DETAILS_PATH, { tabName: ManagementSubTab.policies, policyId, })}${appendSearch(search ?? undefined)}`; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.test.tsx index a2901ab6bfe5c..35462e3a18b90 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.test.tsx @@ -33,7 +33,7 @@ describe('when on the policies page', () => { let firstPolicyID: string; beforeEach(() => { reactTestingLibrary.act(() => { - history.push('/management/policy'); + history.push('/policy'); reactTestingLibrary.act(() => { const policyListData = mockPolicyResultList({ total: 3 }); firstPolicyID = policyListData.items[0].id; diff --git a/x-pack/plugins/security_solution/public/network/components/ip/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/ip/index.test.tsx index 8d53e75b81afc..d949610264883 100644 --- a/x-pack/plugins/security_solution/public/network/components/ip/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/ip/index.test.tsx @@ -12,6 +12,8 @@ import { useMountAppended } from '../../../common/utils/use_mount_appended'; import { Ip } from '.'; +jest.mock('../../../common/components/link_to'); + describe('Port', () => { const mount = useMountAppended(); @@ -42,6 +44,6 @@ describe('Port', () => { expect( wrapper.find('[data-test-subj="draggable-content-destination.ip"]').find('a').first().props() .href - ).toEqual('#/link-to/network/ip/10.1.2.3/source'); + ).toEqual('/ip/10.1.2.3/source'); }); }); diff --git a/x-pack/plugins/security_solution/public/network/components/network_http_table/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/network_http_table/index.test.tsx index e9020421a411e..9f4fdcf71327c 100644 --- a/x-pack/plugins/security_solution/public/network/components/network_http_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/network_http_table/index.test.tsx @@ -23,6 +23,8 @@ import { networkModel } from '../../store'; import { NetworkHttpTable } from '.'; import { mockData } from './mock'; +jest.mock('../../../common/components/link_to'); + describe('NetworkHttp Table Component', () => { const loadPage = jest.fn(); const state: State = mockGlobalState; diff --git a/x-pack/plugins/security_solution/public/network/components/network_top_n_flow_table/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/network_top_n_flow_table/index.test.tsx index e40bbd40f4cd2..462a35ee17b1d 100644 --- a/x-pack/plugins/security_solution/public/network/components/network_top_n_flow_table/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/network_top_n_flow_table/index.test.tsx @@ -23,6 +23,8 @@ import { networkModel } from '../../store'; import { NetworkTopNFlowTable } from '.'; import { mockData } from './mock'; +jest.mock('../../../common/components/link_to'); + describe('NetworkTopNFlow Table Component', () => { const loadPage = jest.fn(); const state: State = mockGlobalState; diff --git a/x-pack/plugins/security_solution/public/network/components/source_destination/index.test.tsx b/x-pack/plugins/security_solution/public/network/components/source_destination/index.test.tsx index 3016e34dfd73c..cefe2821b5244 100644 --- a/x-pack/plugins/security_solution/public/network/components/source_destination/index.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/source_destination/index.test.tsx @@ -95,6 +95,14 @@ const getSourceDestinationInstance = () => ( /> ); +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useHistory: () => ({ + createHref: jest.fn(), + push: jest.fn(), + }), +})); + describe('SourceDestination', () => { const mount = useMountAppended(); diff --git a/x-pack/plugins/security_solution/public/network/components/source_destination/source_destination_ip.test.tsx b/x-pack/plugins/security_solution/public/network/components/source_destination/source_destination_ip.test.tsx index 3c1668bbb3419..d9d926fb52d10 100644 --- a/x-pack/plugins/security_solution/public/network/components/source_destination/source_destination_ip.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/source_destination/source_destination_ip.test.tsx @@ -35,6 +35,8 @@ import { SOURCE_GEO_REGION_NAME_FIELD_NAME, } from './geo_fields'; +jest.mock('../../../common/components/link_to'); + describe('SourceDestinationIp', () => { const mount = useMountAppended(); diff --git a/x-pack/plugins/security_solution/public/network/pages/ip_details/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/network/pages/ip_details/__snapshots__/index.test.tsx.snap index e7598ef03d786..6e76ff00a8141 100644 --- a/x-pack/plugins/security_solution/public/network/pages/ip_details/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/network/pages/ip_details/__snapshots__/index.test.tsx.snap @@ -8,6 +8,8 @@ exports[`Ip Details it matches the snapshot 1`] = ` > <Component /> </WithSource> - <withRouter(undefined) /> + <withRouter(undefined) + pageName="network" + /> </Fragment> `; diff --git a/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.test.tsx index ba82c7f92855d..1aa114608b479 100644 --- a/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/alerts_by_category/index.test.tsx @@ -17,8 +17,8 @@ import { mockIndexPattern, TestProviders } from '../../../common/mock'; import { AlertsByCategory } from '.'; +jest.mock('../../../common/components/link_to'); jest.mock('../../../common/lib/kibana'); - jest.mock('../../../common/containers/matrix_histogram', () => { return { useQuery: jest.fn(), diff --git a/x-pack/plugins/security_solution/public/overview/components/event_counts/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/event_counts/index.test.tsx index 49fe609e0cf91..95dd65f559470 100644 --- a/x-pack/plugins/security_solution/public/overview/components/event_counts/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/event_counts/index.test.tsx @@ -13,6 +13,8 @@ import { mockIndexPattern, TestProviders } from '../../../common/mock'; import { EventCounts } from '.'; +jest.mock('../../../common/components/link_to'); + describe('EventCounts', () => { const from = 1579553397080; const to = 1579639797080; diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_host/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_host/index.test.tsx index 49347ab810547..d2824fd5222cf 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_host/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_host/index.test.tsx @@ -24,6 +24,7 @@ import { GetOverviewHostQuery } from '../../../graphql/types'; import { wait } from '../../../common/lib/helpers'; jest.mock('../../../common/lib/kibana'); +jest.mock('../../../common/components/link_to'); const startDate = 1579553397080; const endDate = 1579639797080; diff --git a/x-pack/plugins/security_solution/public/overview/components/overview_network/index.test.tsx b/x-pack/plugins/security_solution/public/overview/components/overview_network/index.test.tsx index 4451135c608ce..f55246200342f 100644 --- a/x-pack/plugins/security_solution/public/overview/components/overview_network/index.test.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/overview_network/index.test.tsx @@ -21,6 +21,7 @@ import { overviewNetworkQuery } from '../../containers/overview_network/index.gq import { GetOverviewHostQuery } from '../../../graphql/types'; import { wait } from '../../../common/lib/helpers'; +jest.mock('../../../common/components/link_to'); jest.mock('../../../common/lib/kibana'); const startDate = 1579553397080; diff --git a/x-pack/plugins/security_solution/public/timelines/components/netflow/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/netflow/index.test.tsx index 2615d5057347e..24f8d910b4feb 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/netflow/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/netflow/index.test.tsx @@ -120,6 +120,8 @@ const getNetflowInstance = () => ( /> ); +jest.mock('../../../common/components/link_to'); + describe('Netflow', () => { const mount = useMountAppended(); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx index 67a13b2f1ff15..9ced194aceb35 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx @@ -24,6 +24,8 @@ const mockSort: Sort = { sortDirection: Direction.desc, }; +jest.mock('../../../../common/components/link_to'); + jest.mock( 'react-visibility-sensor', () => ({ children }: { children: (args: { isVisible: boolean }) => React.ReactNode }) => diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/generic_row_renderer.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/generic_row_renderer.test.tsx index ae5e7e2ef789b..aec463f531448 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/generic_row_renderer.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/auditd/generic_row_renderer.test.tsx @@ -19,6 +19,7 @@ import { createGenericFileRowRenderer, } from './generic_row_renderer'; +jest.mock('../../../../../../common/components/link_to'); jest.mock('../../../../../../overview/components/events_by_dataset'); describe('GenericRowRenderer', () => { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.test.tsx index 64f4656e7e790..3e055682d27a4 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field.test.tsx @@ -16,6 +16,7 @@ import { FormattedFieldValue } from './formatted_field'; import { HOST_NAME_FIELD_NAME } from './constants'; jest.mock('../../../../../common/lib/kibana'); +jest.mock('../../../../../common/components/link_to'); describe('Events', () => { const mount = useMountAppended(); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/get_row_renderer.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/get_row_renderer.test.tsx index 3222f8a2362db..0b3ea0ce6e430 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/get_row_renderer.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/get_row_renderer.test.tsx @@ -17,6 +17,8 @@ import { useMountAppended } from '../../../../../common/utils/use_mount_appended import { rowRenderers } from '.'; import { getRowRenderer } from './get_row_renderer'; +jest.mock('../../../../../common/components/link_to'); + describe('get_column_renderer', () => { let nonSuricata: Ecs; let suricata: Ecs; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/netflow/netflow_row_renderer.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/netflow/netflow_row_renderer.test.tsx index 7e51c717ebe8c..5140b9abc60ef 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/netflow/netflow_row_renderer.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/netflow/netflow_row_renderer.test.tsx @@ -24,6 +24,8 @@ export const justIdAndTimestamp: Ecs = { timestamp: '2018-11-12T19:03:25.936Z', }; +jest.mock('../../../../../../common/components/link_to'); + describe('netflowRowRenderer', () => { const mount = useMountAppended(); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/plain_column_renderer.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/plain_column_renderer.test.tsx index 2448540f27e23..b7c2cb7032cc2 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/plain_column_renderer.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/plain_column_renderer.test.tsx @@ -16,6 +16,8 @@ import { useMountAppended } from '../../../../../common/utils/use_mount_appended import { plainColumnRenderer } from './plain_column_renderer'; import { getValues, deleteItemIdx, findItem } from './helpers'; +jest.mock('../../../../../common/components/link_to'); + describe('plain_column_renderer', () => { const mount = useMountAppended(); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_details.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_details.test.tsx index d5040cb252370..14f147c61fca3 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_details.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_details.test.tsx @@ -13,6 +13,8 @@ import { TestProviders } from '../../../../../../common/mock/test_providers'; import { useMountAppended } from '../../../../../../common/utils/use_mount_appended'; import { SuricataDetails } from './suricata_details'; +jest.mock('../../../../../../common/components/link_to'); + describe('SuricataDetails', () => { const mount = useMountAppended(); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_row_renderer.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_row_renderer.test.tsx index a10cd9dc97f6d..d36d24f41224c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_row_renderer.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_row_renderer.test.tsx @@ -15,6 +15,8 @@ import { TestProviders } from '../../../../../../common/mock/test_providers'; import { suricataRowRenderer } from './suricata_row_renderer'; import { useMountAppended } from '../../../../../../common/utils/use_mount_appended'; +jest.mock('../../../../../../common/components/link_to'); + describe('suricata_row_renderer', () => { const mount = useMountAppended(); let nonSuricata: Ecs; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_details.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_details.test.tsx index e622c91e8b870..8efd8e1944331 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_details.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_details.test.tsx @@ -13,6 +13,8 @@ import { mockTimelineData, TestProviders } from '../../../../../../common/mock'; import { SystemGenericDetails, SystemGenericLine } from './generic_details'; import { useMountAppended } from '../../../../../../common/utils/use_mount_appended'; +jest.mock('../../../../../../common/components/link_to'); + describe('SystemGenericDetails', () => { const mount = useMountAppended(); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_file_details.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_file_details.test.tsx index 61cc7f32567ef..5f4f5a5da352e 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_file_details.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_file_details.test.tsx @@ -13,6 +13,14 @@ import { mockTimelineData, TestProviders } from '../../../../../../common/mock'; import { SystemGenericFileDetails, SystemGenericFileLine } from './generic_file_details'; import { useMountAppended } from '../../../../../../common/utils/use_mount_appended'; +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useHistory: () => ({ + createHref: jest.fn(), + push: jest.fn(), + }), +})); + describe('SystemGenericFileDetails', () => { const mount = useMountAppended(); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_row_renderer.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_row_renderer.test.tsx index 26cccc82896ea..bb997b9689270 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_row_renderer.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/system/generic_row_renderer.test.tsx @@ -48,6 +48,7 @@ import { } from './generic_row_renderer'; import * as i18n from './translations'; +jest.mock('../../../../../../common/components/link_to'); jest.mock('../../../../../../overview/components/events_by_dataset'); describe('GenericRowRenderer', () => { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_details.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_details.test.tsx index d82a0fcb6e644..04b0e6e5fcfae 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_details.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_details.test.tsx @@ -11,6 +11,8 @@ import { mockTimelineData, TestProviders } from '../../../../../../common/mock'; import { useMountAppended } from '../../../../../../common/utils/use_mount_appended'; import { ZeekDetails } from './zeek_details'; +jest.mock('../../../../../../common/components/link_to'); + describe('ZeekDetails', () => { const mount = useMountAppended(); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_row_renderer.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_row_renderer.test.tsx index 2197ccb0ce2e0..2eed6aaf20335 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_row_renderer.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_row_renderer.test.tsx @@ -14,6 +14,8 @@ import { mockTimelineData, TestProviders } from '../../../../../../common/mock'; import { useMountAppended } from '../../../../../../common/utils/use_mount_appended'; import { zeekRowRenderer } from './zeek_row_renderer'; +jest.mock('../../../../../../common/components/link_to'); + describe('zeek_row_renderer', () => { const mount = useMountAppended(); let nonZeek: Ecs; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.test.tsx index 5f75cd3189669..8f63ca13b56e5 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/index.test.tsx @@ -22,6 +22,8 @@ export { nextTick } from '../../../../../../../test_utils'; import { act } from 'react-dom/test-utils'; +jest.mock('../../../../common/components/link_to'); + jest.mock('../../../../common/lib/kibana', () => { const originalModule = jest.requireActual('../../../../common/lib/kibana'); return { @@ -34,6 +36,7 @@ jest.mock('../../../../common/lib/kibana', () => { crud: true, }, }, + navigateToApp: jest.fn(), }, }, }), diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.test.tsx index 2447ebf47463f..96703941f616e 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.test.tsx @@ -32,19 +32,15 @@ jest.mock('use-resize-observer/polyfilled'); mockUseResizeObserver.mockImplementation(() => ({})); -jest.mock('react-router-dom', () => { - const originalModule = jest.requireActual('react-router-dom'); - return { - ...originalModule, - useHistory: jest.fn(), - }; -}); jest.mock('../../../common/lib/kibana', () => { const originalModule = jest.requireActual('../../../common/lib/kibana'); return { ...originalModule, useKibana: jest.fn().mockReturnValue({ services: { + application: { + navigateToApp: jest.fn(), + }, uiSettings: { get: jest.fn(), }, From 86012f51659921fe03a35b5ba83370301a75386d Mon Sep 17 00:00:00 2001 From: patrykkopycinski <contact@patrykkopycinski.com> Date: Tue, 16 Jun 2020 06:36:23 +0200 Subject: [PATCH 21/43] Update index.tsx --- .../public/overview/components/recent_cases/no_cases/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/overview/components/recent_cases/no_cases/index.tsx b/x-pack/plugins/security_solution/public/overview/components/recent_cases/no_cases/index.tsx index 4fa41507f244a..40969a6e1df4a 100644 --- a/x-pack/plugins/security_solution/public/overview/components/recent_cases/no_cases/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/recent_cases/no_cases/index.tsx @@ -34,7 +34,7 @@ const NoCasesComponent = () => { href={formatUrl(getCreateCaseUrl())} >{` ${i18n.START_A_NEW_CASE}`}</LinkAnchor> ), - [goToCreateCase] + [formatUrl, goToCreateCase] ); return ( From efba7dddeda1497f634b57b4d0901ad768542266 Mon Sep 17 00:00:00 2001 From: Patryk Kopycinski <contact@patrykkopycinski.com> Date: Tue, 16 Jun 2020 15:11:40 +0200 Subject: [PATCH 22/43] Fix cypress --- .../integration/ml_conditional_links.spec.ts | 26 +++++++++---------- .../cypress/integration/navigation.spec.ts | 10 +++---- .../cypress/integration/url_state.spec.ts | 2 +- .../cypress/urls/ml_conditional_links.ts | 26 +++++++++---------- .../cypress/urls/navigation.ts | 22 ++++++++-------- .../security_solution/cypress/urls/state.ts | 16 ++++++------ .../public/app/home/home_navigations.tsx | 3 ++- .../security_solution/public/app/types.ts | 13 ---------- .../user_action_markdown.test.tsx | 2 +- .../components/exceptions/viewer/index.tsx | 2 +- .../common/components/navigation/types.ts | 11 ++++++++ .../use_insert_timeline.tsx | 2 +- 12 files changed, 67 insertions(+), 68 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/ml_conditional_links.spec.ts b/x-pack/plugins/security_solution/cypress/integration/ml_conditional_links.spec.ts index 785df4a0c2f9e..aacbd1a4296eb 100644 --- a/x-pack/plugins/security_solution/cypress/integration/ml_conditional_links.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/ml_conditional_links.spec.ts @@ -101,7 +101,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlNetworkSingleIpNullKqlQuery); cy.url().should( 'include', - '/app/security#/network/ip/127.0.0.1/source?timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))' + '/app/security/network/ip/127.0.0.1/source?timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))' ); }); @@ -109,7 +109,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlNetworkSingleIpKqlQuery); cy.url().should( 'include', - "/app/security#/network/ip/127.0.0.1/source?query=(language:kuery,query:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" + "/app/security/network/ip/127.0.0.1/source?query=(language:kuery,query:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" ); }); @@ -117,7 +117,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlNetworkMultipleIpNullKqlQuery); cy.url().should( 'include', - "app/security#/network/flows?query=(language:kuery,query:'((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))')&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999))" + "app/security/network/flows?query=(language:kuery,query:'((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))')&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999))" ); }); @@ -125,7 +125,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlNetworkMultipleIpKqlQuery); cy.url().should( 'include', - "/app/security#/network/flows?query=(language:kuery,query:'((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))')&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" + "/app/security/network/flows?query=(language:kuery,query:'((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))')&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" ); }); @@ -133,7 +133,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlNetworkNullKqlQuery); cy.url().should( 'include', - '/app/security#/network/flows?timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))' + '/app/security/network/flows?timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))' ); }); @@ -141,7 +141,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlNetworkKqlQuery); cy.url().should( 'include', - "/app/security#/network/flows?query=(language:kuery,query:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" + "/app/security/network/flows?query=(language:kuery,query:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" ); }); @@ -149,7 +149,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostSingleHostNullKqlQuery); cy.url().should( 'include', - '/app/security#/hosts/siem-windows/anomalies?timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))' + '/app/security/hosts/siem-windows/anomalies?timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))' ); }); @@ -157,7 +157,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostSingleHostKqlQueryVariable); cy.url().should( 'include', - '/app/security#/hosts/siem-windows/anomalies?timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))' + '/app/security/hosts/siem-windows/anomalies?timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))' ); }); @@ -165,7 +165,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostSingleHostKqlQuery); cy.url().should( 'include', - "/app/security#/hosts/siem-windows/anomalies?query=(language:kuery,query:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" + "/app/security/hosts/siem-windows/anomalies?query=(language:kuery,query:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" ); }); @@ -173,7 +173,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostMultiHostNullKqlQuery); cy.url().should( 'include', - "/app/security#/hosts/anomalies?query=(language:kuery,query:'(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" + "/app/security/hosts/anomalies?query=(language:kuery,query:'(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" ); }); @@ -181,7 +181,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostMultiHostKqlQuery); cy.url().should( 'include', - "/app/security#/hosts/anomalies?query=(language:kuery,query:'(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))')&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" + "/app/security/hosts/anomalies?query=(language:kuery,query:'(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))')&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" ); }); @@ -189,7 +189,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostVariableHostNullKqlQuery); cy.url().should( 'include', - '/app/security#/hosts/anomalies?timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))' + '/app/security/hosts/anomalies?timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))' ); }); @@ -197,7 +197,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostVariableHostKqlQuery); cy.url().should( 'include', - "/app/security#/hosts/anomalies?query=(language:kuery,query:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" + "/app/security/hosts/anomalies?query=(language:kuery,query:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" ); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/navigation.spec.ts b/x-pack/plugins/security_solution/cypress/integration/navigation.spec.ts index 2014e34c11886..51743a2d3422f 100644 --- a/x-pack/plugins/security_solution/cypress/integration/navigation.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/navigation.spec.ts @@ -16,26 +16,26 @@ describe('top-level navigation common to all pages in the Security app', () => { }); it('navigates to the Overview page', () => { navigateFromHeaderTo(OVERVIEW); - cy.url().should('include', '/security#/overview'); + cy.url().should('include', '/security/overview'); }); it('navigates to the Hosts page', () => { navigateFromHeaderTo(HOSTS); - cy.url().should('include', '/security#/hosts'); + cy.url().should('include', '/security/hosts'); }); it('navigates to the Network page', () => { navigateFromHeaderTo(NETWORK); - cy.url().should('include', '/security#/network'); + cy.url().should('include', '/security/network'); }); it('navigates to the Detections page', () => { navigateFromHeaderTo(DETECTIONS); - cy.url().should('include', '/security#/detections'); + cy.url().should('include', '/security/detections'); }); it('navigates to the Timelines page', () => { navigateFromHeaderTo(TIMELINES); - cy.url().should('include', '/security#/timelines'); + cy.url().should('include', '/security/timelines'); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts b/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts index 425ed23bdafec..e75cb61eb5f79 100644 --- a/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts @@ -255,7 +255,7 @@ describe('url state', () => { expect(matched).to.have.lengthOf(1); closeTimeline(); cy.visit('/app/kibana'); - cy.visit(`/app/security#/overview?timeline\=(id:'${newTimelineId}',isOpen:!t)`); + cy.visit(`/app/security/overview?timeline\=(id:'${newTimelineId}',isOpen:!t)`); cy.contains('a', 'Security'); cy.get(DATE_PICKER_APPLY_BUTTON_TIMELINE).invoke('text').should('not.equal', 'Updating'); cy.get(TIMELINE_TITLE).should('have.attr', 'value', timelineName); diff --git a/x-pack/plugins/security_solution/cypress/urls/ml_conditional_links.ts b/x-pack/plugins/security_solution/cypress/urls/ml_conditional_links.ts index cfa18099e5888..e2454a6994866 100644 --- a/x-pack/plugins/security_solution/cypress/urls/ml_conditional_links.ts +++ b/x-pack/plugins/security_solution/cypress/urls/ml_conditional_links.ts @@ -25,52 +25,52 @@ // Single IP with a null for the Query: export const mlNetworkSingleIpNullKqlQuery = - "/app/security#/ml-network/ip/127.0.0.1?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; + "/app/security/ml-network/ip/127.0.0.1?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; // Single IP with a value for the Query: export const mlNetworkSingleIpKqlQuery = - "/app/security#/ml-network/ip/127.0.0.1?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; + "/app/security/ml-network/ip/127.0.0.1?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; // Multiple IPs with a null for the Query: export const mlNetworkMultipleIpNullKqlQuery = - "/app/security#/ml-network/ip/127.0.0.1,127.0.0.2?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; + "/app/security/ml-network/ip/127.0.0.1,127.0.0.2?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; // Multiple IPs with a value for the Query: export const mlNetworkMultipleIpKqlQuery = - "/app/security#/ml-network/ip/127.0.0.1,127.0.0.2?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; + "/app/security/ml-network/ip/127.0.0.1,127.0.0.2?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; // $ip$ with a null Query: export const mlNetworkNullKqlQuery = - "/app/security#/ml-network/ip/$ip$?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; + "/app/security/ml-network/ip/$ip$?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; // $ip$ with a value for the Query: export const mlNetworkKqlQuery = - "/app/security#/ml-network/ip/$ip$?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; + "/app/security/ml-network/ip/$ip$?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; // Single host name with a null for the Query: export const mlHostSingleHostNullKqlQuery = - "/app/security#/ml-hosts/siem-windows?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + "/app/security/ml-hosts/siem-windows?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; // Single host name with a variable in the Query: export const mlHostSingleHostKqlQueryVariable = - "/app/security#/ml-hosts/siem-windows?query=(language:kuery,query:'process.name%20:%20%22$process.name$%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + "/app/security/ml-hosts/siem-windows?query=(language:kuery,query:'process.name%20:%20%22$process.name$%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; // Single host name with a value for Query: export const mlHostSingleHostKqlQuery = - "/app/security#/ml-hosts/siem-windows?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + "/app/security/ml-hosts/siem-windows?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; // Multiple host names with null for Query: export const mlHostMultiHostNullKqlQuery = - "/app/security#/ml-hosts/siem-windows,siem-suricata?query=!n&&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + "/app/security/ml-hosts/siem-windows,siem-suricata?query=!n&&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; // Multiple host names with a value for Query: export const mlHostMultiHostKqlQuery = - "/app/security#/ml-hosts/siem-windows,siem-suricata?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + "/app/security/ml-hosts/siem-windows,siem-suricata?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; // Undefined/null host name with a null for the KQL: export const mlHostVariableHostNullKqlQuery = - "/app/security#/ml-hosts/$host.name$?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + "/app/security/ml-hosts/$host.name$?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; // Undefined/null host name but with a value for Query: export const mlHostVariableHostKqlQuery = - "/app/security#/ml-hosts/$host.name$?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + "/app/security/ml-hosts/$host.name$?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; diff --git a/x-pack/plugins/security_solution/cypress/urls/navigation.ts b/x-pack/plugins/security_solution/cypress/urls/navigation.ts index 9bfe2e9e5102e..a6064056d1197 100644 --- a/x-pack/plugins/security_solution/cypress/urls/navigation.ts +++ b/x-pack/plugins/security_solution/cypress/urls/navigation.ts @@ -4,16 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -export const CASES = '/app/security#/case'; -export const DETECTIONS = 'app/security#/detections'; -export const HOSTS_PAGE = '/app/security#/hosts/allHosts'; +export const CASES = '/app/security/case'; +export const DETECTIONS = 'app/security/detections'; +export const HOSTS_PAGE = '/app/security/hosts/allHosts'; export const HOSTS_PAGE_TAB_URLS = { - allHosts: '/app/security#/hosts/allHosts', - anomalies: '/app/security#/hosts/anomalies', - authentications: '/app/security#/hosts/authentications', - events: '/app/security#/hosts/events', - uncommonProcesses: '/app/security#/hosts/uncommonProcesses', + allHosts: '/app/security/hosts/allHosts', + anomalies: '/app/security/hosts/anomalies', + authentications: '/app/security/hosts/authentications', + events: '/app/security/hosts/events', + uncommonProcesses: '/app/security/hosts/uncommonProcesses', }; -export const NETWORK_PAGE = '/app/security#/network'; -export const OVERVIEW_PAGE = '/app/security#/overview'; -export const TIMELINES_PAGE = '/app/security#/timelines'; +export const NETWORK_PAGE = '/app/security/network'; +export const OVERVIEW_PAGE = '/app/security/overview'; +export const TIMELINES_PAGE = '/app/security/timelines'; diff --git a/x-pack/plugins/security_solution/cypress/urls/state.ts b/x-pack/plugins/security_solution/cypress/urls/state.ts index 6de30fdafdaf8..9c6d77035bdf9 100644 --- a/x-pack/plugins/security_solution/cypress/urls/state.ts +++ b/x-pack/plugins/security_solution/cypress/urls/state.ts @@ -6,16 +6,16 @@ export const ABSOLUTE_DATE_RANGE = { url: - '/app/security#/network/?timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))', + '/app/security/network/?timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))', urlUnlinked: - '/app/security#/network/?timerange=(global:(linkTo:!(),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(),timerange:(from:1564776209186,kind:absolute,to:1564779809186)))', - urlKqlNetworkNetwork: `/app/security#/network/?query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`, - urlKqlNetworkHosts: `/app/security#/network/?query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`, - urlKqlHostsNetwork: `/app/security#/hosts/allHosts?query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`, - urlKqlHostsHosts: `/app/security#/hosts/allHosts?query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`, + '/app/security/network/?timerange=(global:(linkTo:!(),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(),timerange:(from:1564776209186,kind:absolute,to:1564779809186)))', + urlKqlNetworkNetwork: `/app/security/network/?query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`, + urlKqlNetworkHosts: `/app/security/network/?query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`, + urlKqlHostsNetwork: `/app/security/hosts/allHosts?query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`, + urlKqlHostsHosts: `/app/security/hosts/allHosts?query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`, urlHost: - '/app/security#/hosts/authentications?timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))', + '/app/security/hosts/authentications?timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))', urlHostNew: - '/app/security#/hosts/authentications?timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))', + '/app/security/hosts/authentications?timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))', }; diff --git a/x-pack/plugins/security_solution/public/app/home/home_navigations.tsx b/x-pack/plugins/security_solution/public/app/home/home_navigations.tsx index 501e371a97b70..88e9d4179a971 100644 --- a/x-pack/plugins/security_solution/public/app/home/home_navigations.tsx +++ b/x-pack/plugins/security_solution/public/app/home/home_navigations.tsx @@ -5,7 +5,8 @@ */ import * as i18n from './translations'; -import { SecurityPageName, SiemNavTab } from '../types'; +import { SecurityPageName } from '../types'; +import { SiemNavTab } from '../../common/components/navigation/types'; import { APP_OVERVIEW_PATH, APP_ALERTS_PATH, diff --git a/x-pack/plugins/security_solution/public/app/types.ts b/x-pack/plugins/security_solution/public/app/types.ts index 860cd121b9795..f74ad0de8beef 100644 --- a/x-pack/plugins/security_solution/public/app/types.ts +++ b/x-pack/plugins/security_solution/public/app/types.ts @@ -14,7 +14,6 @@ import { CombinedState, } from 'redux'; -import { NavTab } from '../common/components/navigation/types'; import { State, SubPluginsInitReducer } from '../common/store'; import { Immutable } from '../../common/endpoint/types'; import { AppAction } from '../common/store/actions'; @@ -28,18 +27,6 @@ export enum SecurityPageName { case = 'case', management = 'management', } - -export type SiemNavTabKey = - | SecurityPageName.overview - | SecurityPageName.hosts - | SecurityPageName.network - | SecurityPageName.alerts - | SecurityPageName.timelines - | SecurityPageName.case - | SecurityPageName.management; - -export type SiemNavTab = Record<SiemNavTabKey, NavTab>; - export interface SecuritySubPluginStore<K extends SecuritySubPluginKeyStore, T> { initialState: Record<K, T | undefined>; reducer: Record<K, Reducer<T, AnyAction>>; diff --git a/x-pack/plugins/security_solution/public/cases/components/user_action_tree/user_action_markdown.test.tsx b/x-pack/plugins/security_solution/public/cases/components/user_action_tree/user_action_markdown.test.tsx index ae9f1ec7469e4..b2d8ce9c891bc 100644 --- a/x-pack/plugins/security_solution/public/cases/components/user_action_tree/user_action_markdown.test.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/user_action_tree/user_action_markdown.test.tsx @@ -18,7 +18,7 @@ const onSaveContent = jest.fn(); const timelineId = '1e10f150-949b-11ea-b63c-2bc51864784c'; const defaultProps = { - content: `A link to a timeline [timeline](http://localhost:5601/app/security#/timelines?timeline=(id:'${timelineId}',isOpen:!t))`, + content: `A link to a timeline [timeline](http://localhost:5601/app/security/timelines?timeline=(id:'${timelineId}',isOpen:!t))`, id: 'markdown-id', isEditable: false, onChangeEditable, diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx index 1051962852219..617fd6a3509fd 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/index.tsx @@ -247,7 +247,7 @@ const ExceptionsViewerComponent = ({ // Used in utility bar info text const ruleSettingsUrl = useMemo((): string => { return services.application.getUrlForApp( - `security#/detections/rules/id/${encodeURI(ruleId)}/edit` + `security/detections/rules/id/${encodeURI(ruleId)}/edit` ); }, [ruleId, services.application]); 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 07aca1e6c9c6e..7be1af54d0a53 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 @@ -40,3 +40,14 @@ export interface NavTab { isDetailPage?: boolean; pageId?: SecurityPageName; } + +export type SiemNavTabKey = + | SecurityPageName.overview + | SecurityPageName.hosts + | SecurityPageName.network + | SecurityPageName.alerts + | SecurityPageName.timelines + | SecurityPageName.case + | SecurityPageName.management; + +export type SiemNavTab = Record<SiemNavTabKey, NavTab>; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/insert_timeline_popover/use_insert_timeline.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/insert_timeline_popover/use_insert_timeline.tsx index 6269bc1b4a1a3..c3def9c4cbb29 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/insert_timeline_popover/use_insert_timeline.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/insert_timeline_popover/use_insert_timeline.tsx @@ -17,7 +17,7 @@ export const useInsertTimeline = <T extends FormData>(form: FormHook<T>, fieldNa }); const handleOnTimelineChange = useCallback( (title: string, id: string | null) => { - const builtLink = `${basePath}/app/security#/timelines?timeline=(id:'${id}',isOpen:!t)`; + const builtLink = `${basePath}/app/security/timelines?timeline=(id:'${id}',isOpen:!t)`; const currentValue = form.getFormData()[fieldName]; const newValue: string = [ currentValue.slice(0, cursorPosition.start), From 654101937050342abe2e1c8b68da93f53a7f50ab Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Tue, 16 Jun 2020 11:43:37 -0400 Subject: [PATCH 23/43] bug remove timeline when you are in case configure --- .../common/utils/timeline/use_show_timeline.tsx | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/utils/timeline/use_show_timeline.tsx b/x-pack/plugins/security_solution/public/common/utils/timeline/use_show_timeline.tsx index e88b8a1d6b42e..fde3f6f8b222d 100644 --- a/x-pack/plugins/security_solution/public/common/utils/timeline/use_show_timeline.tsx +++ b/x-pack/plugins/security_solution/public/common/utils/timeline/use_show_timeline.tsx @@ -4,21 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ -import { useLocation } from 'react-router-dom'; - import { useState, useEffect } from 'react'; -import { SecurityPageName } from '../../../app/types'; +import { useRouteSpy } from '../route/use_route_spy'; -const hideTimelineForRoutes = [`/${SecurityPageName.case}/configure`]; +const hideTimelineForRoutes = [`/cases/configure`]; export const useShowTimeline = () => { - const currentLocation = useLocation(); + const [{ pageName, pathName }] = useRouteSpy(); + const [showTimeline, setShowTimeline] = useState( - !hideTimelineForRoutes.includes(currentLocation.pathname) + !hideTimelineForRoutes.includes(window.location.pathname) ); useEffect(() => { - if (hideTimelineForRoutes.includes(currentLocation.pathname)) { + if ( + hideTimelineForRoutes.filter((route) => window.location.pathname.includes(route)).length > 0 + ) { if (showTimeline) { setShowTimeline(false); } @@ -26,7 +27,7 @@ export const useShowTimeline = () => { setShowTimeline(true); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [currentLocation.pathname]); + }, [pageName, pathName]); return [showTimeline]; }; From 807cde2ed4e655cc185ef6f6c403a93a5f0594c6 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Tue, 16 Jun 2020 16:52:20 -0400 Subject: [PATCH 24/43] Fix functionel test for management --- .../page_objects/endpoint_page.ts | 8 +++++--- .../page_objects/policy_page.ts | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/x-pack/test/security_solution_endpoint/page_objects/endpoint_page.ts b/x-pack/test/security_solution_endpoint/page_objects/endpoint_page.ts index 830c5ec5a42d1..aae9bff96ba31 100644 --- a/x-pack/test/security_solution_endpoint/page_objects/endpoint_page.ts +++ b/x-pack/test/security_solution_endpoint/page_objects/endpoint_page.ts @@ -17,9 +17,11 @@ export function EndpointPageProvider({ getService, getPageObjects }: FtrProvider * Navigate to the Endpoints list page */ async navigateToEndpointList(searchParams?: string) { - await pageObjects.common.navigateToApp('securitySolution', { - hash: `/management/endpoints${searchParams ? `?${searchParams}` : ''}`, - }); + await pageObjects.common.navigateToUrl( + 'securitySolution:management', + `/endpoints${searchParams ? `?${searchParams}` : ''}`, + { shouldUseHashForSubUrl: false } + ); await pageObjects.header.waitUntilLoadingHasFinished(); }, diff --git a/x-pack/test/security_solution_endpoint/page_objects/policy_page.ts b/x-pack/test/security_solution_endpoint/page_objects/policy_page.ts index eb481cdfc99c4..462d19c1b6d25 100644 --- a/x-pack/test/security_solution_endpoint/page_objects/policy_page.ts +++ b/x-pack/test/security_solution_endpoint/page_objects/policy_page.ts @@ -15,7 +15,9 @@ export function EndpointPolicyPageProvider({ getService, getPageObjects }: FtrPr * Navigates to the Endpoint Policy List */ async navigateToPolicyList() { - await pageObjects.common.navigateToApp('securitySolution', { hash: '/management/policy' }); + await pageObjects.common.navigateToUrl('securitySolution:management', '/policy', { + shouldUseHashForSubUrl: false, + }); await pageObjects.header.waitUntilLoadingHasFinished(); }, @@ -51,8 +53,8 @@ export function EndpointPolicyPageProvider({ getService, getPageObjects }: FtrPr * @param policyId */ async navigateToPolicyDetails(policyId: string) { - await pageObjects.common.navigateToApp('securitySolution', { - hash: `/management/policy/${policyId}`, + await pageObjects.common.navigateToUrl('securitySolution:management', `/policy/${policyId}`, { + shouldUseHashForSubUrl: false, }); await pageObjects.header.waitUntilLoadingHasFinished(); }, From 9886f4e78ae4b2b9097dd0147681e8674d144b2b Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Tue, 16 Jun 2020 16:53:02 -0400 Subject: [PATCH 25/43] Fix redirect siem + ml --- .../integration/ml_conditional_links.spec.ts | 16 +-- .../cypress/urls/ml_conditional_links.ts | 26 ++-- .../security_solution/public/helpers.ts | 117 +++++++----------- .../public/hosts/pages/index.tsx | 13 +- .../public/network/pages/index.tsx | 7 ++ 5 files changed, 80 insertions(+), 99 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/ml_conditional_links.spec.ts b/x-pack/plugins/security_solution/cypress/integration/ml_conditional_links.spec.ts index aacbd1a4296eb..0c3424576e4cf 100644 --- a/x-pack/plugins/security_solution/cypress/integration/ml_conditional_links.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/ml_conditional_links.spec.ts @@ -109,7 +109,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlNetworkSingleIpKqlQuery); cy.url().should( 'include', - "/app/security/network/ip/127.0.0.1/source?query=(language:kuery,query:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" + '/app/security/network/ip/127.0.0.1/source?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))' ); }); @@ -117,7 +117,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlNetworkMultipleIpNullKqlQuery); cy.url().should( 'include', - "app/security/network/flows?query=(language:kuery,query:'((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))')&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999))" + 'app/security/network/flows?query=(language:kuery,query:%27((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999))' ); }); @@ -125,7 +125,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlNetworkMultipleIpKqlQuery); cy.url().should( 'include', - "/app/security/network/flows?query=(language:kuery,query:'((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))')&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" + '/app/security/network/flows?query=(language:kuery,query:%27((source.ip:%20%22127.0.0.1%22%20or%20destination.ip:%20%22127.0.0.1%22)%20or%20(source.ip:%20%22127.0.0.2%22%20or%20destination.ip:%20%22127.0.0.2%22))%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))' ); }); @@ -141,7 +141,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlNetworkKqlQuery); cy.url().should( 'include', - "/app/security/network/flows?query=(language:kuery,query:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))" + '/app/security/network/flows?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:1566990000000,kind:absolute,to:1567000799999)),timeline:(linkTo:!(global),timerange:(from:1566990000000,kind:absolute,to:1567000799999)))' ); }); @@ -165,7 +165,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostSingleHostKqlQuery); cy.url().should( 'include', - "/app/security/hosts/siem-windows/anomalies?query=(language:kuery,query:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" + '/app/security/hosts/siem-windows/anomalies?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))' ); }); @@ -173,7 +173,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostMultiHostNullKqlQuery); cy.url().should( 'include', - "/app/security/hosts/anomalies?query=(language:kuery,query:'(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" + '/app/security/hosts/anomalies?query=(language:kuery,query:%27(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))' ); }); @@ -181,7 +181,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostMultiHostKqlQuery); cy.url().should( 'include', - "/app/security/hosts/anomalies?query=(language:kuery,query:'(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))')&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" + '/app/security/hosts/anomalies?query=(language:kuery,query:%27(host.name:%20%22siem-windows%22%20or%20host.name:%20%22siem-suricata%22)%20and%20((process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22))%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))' ); }); @@ -197,7 +197,7 @@ describe('ml conditional links', () => { loginAndWaitForPageWithoutDateRange(mlHostVariableHostKqlQuery); cy.url().should( 'include', - "/app/security/hosts/anomalies?query=(language:kuery,query:'(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)')&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))" + '/app/security/hosts/anomalies?query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:1559800800000,kind:absolute,to:1559887199999)),timeline:(linkTo:!(global),timerange:(from:1559800800000,kind:absolute,to:1559887199999)))' ); }); }); diff --git a/x-pack/plugins/security_solution/cypress/urls/ml_conditional_links.ts b/x-pack/plugins/security_solution/cypress/urls/ml_conditional_links.ts index e2454a6994866..655418fc98bf8 100644 --- a/x-pack/plugins/security_solution/cypress/urls/ml_conditional_links.ts +++ b/x-pack/plugins/security_solution/cypress/urls/ml_conditional_links.ts @@ -25,52 +25,52 @@ // Single IP with a null for the Query: export const mlNetworkSingleIpNullKqlQuery = - "/app/security/ml-network/ip/127.0.0.1?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; + "/app/siem#/ml-network/ip/127.0.0.1?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; // Single IP with a value for the Query: export const mlNetworkSingleIpKqlQuery = - "/app/security/ml-network/ip/127.0.0.1?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; + "/app/siem#/ml-network/ip/127.0.0.1?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; // Multiple IPs with a null for the Query: export const mlNetworkMultipleIpNullKqlQuery = - "/app/security/ml-network/ip/127.0.0.1,127.0.0.2?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; + "/app/siem#/ml-network/ip/127.0.0.1,127.0.0.2?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; // Multiple IPs with a value for the Query: export const mlNetworkMultipleIpKqlQuery = - "/app/security/ml-network/ip/127.0.0.1,127.0.0.2?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; + "/app/siem#/ml-network/ip/127.0.0.1,127.0.0.2?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; // $ip$ with a null Query: export const mlNetworkNullKqlQuery = - "/app/security/ml-network/ip/$ip$?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; + "/app/siem#/ml-network/ip/$ip$?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; // $ip$ with a value for the Query: export const mlNetworkKqlQuery = - "/app/security/ml-network/ip/$ip$?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; + "/app/siem#/ml-network/ip/$ip$?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-28T11:00:00.000Z',kind:absolute,to:'2019-08-28T13:59:59.999Z')))"; // Single host name with a null for the Query: export const mlHostSingleHostNullKqlQuery = - "/app/security/ml-hosts/siem-windows?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + "/app/siem#/ml-hosts/siem-windows?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; // Single host name with a variable in the Query: export const mlHostSingleHostKqlQueryVariable = - "/app/security/ml-hosts/siem-windows?query=(language:kuery,query:'process.name%20:%20%22$process.name$%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + "/app/siem#/ml-hosts/siem-windows?query=(language:kuery,query:'process.name%20:%20%22$process.name$%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; // Single host name with a value for Query: export const mlHostSingleHostKqlQuery = - "/app/security/ml-hosts/siem-windows?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + "/app/siem#/ml-hosts/siem-windows?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; // Multiple host names with null for Query: export const mlHostMultiHostNullKqlQuery = - "/app/security/ml-hosts/siem-windows,siem-suricata?query=!n&&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + "/app/siem#/ml-hosts/siem-windows,siem-suricata?query=!n&&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; // Multiple host names with a value for Query: export const mlHostMultiHostKqlQuery = - "/app/security/ml-hosts/siem-windows,siem-suricata?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + "/app/siem#/ml-hosts/siem-windows,siem-suricata?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; // Undefined/null host name with a null for the KQL: export const mlHostVariableHostNullKqlQuery = - "/app/security/ml-hosts/$host.name$?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + "/app/siem#/ml-hosts/$host.name$?query=!n&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; // Undefined/null host name but with a value for Query: export const mlHostVariableHostKqlQuery = - "/app/security/ml-hosts/$host.name$?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; + "/app/siem#/ml-hosts/$host.name$?query=(language:kuery,query:'process.name%20:%20%22conhost.exe,sc.exe%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')),timeline:(linkTo:!(global),timerange:(from:'2019-06-06T06:00:00.000Z',kind:absolute,to:'2019-06-07T05:59:59.999Z')))"; diff --git a/x-pack/plugins/security_solution/public/helpers.ts b/x-pack/plugins/security_solution/public/helpers.ts index 226aaf13637cb..0dd66d06b78be 100644 --- a/x-pack/plugins/security_solution/public/helpers.ts +++ b/x-pack/plugins/security_solution/public/helpers.ts @@ -18,85 +18,58 @@ export const manageOldSiemRoutes = async (coreStart: CoreStart) => { switch (pageName) { case SecurityPageName.overview: - window.setTimeout( - () => - application.navigateToApp(`${APP_ID}:${SecurityPageName.overview}`, { - replace: true, - path, - }), - 0 - ); + application.navigateToApp(`${APP_ID}:${SecurityPageName.overview}`, { + replace: true, + path, + }); + break; case 'ml-hosts': - window.setTimeout( - () => - application.navigateToApp(`${APP_ID}:${SecurityPageName.hosts}`, { - replace: true, - path: `/ml-hosts${path}`, - }), - 0 - ); + application.navigateToApp(`${APP_ID}:${SecurityPageName.hosts}`, { + replace: true, + path: `/ml-hosts${path}`, + }); + break; case SecurityPageName.hosts: - window.setTimeout( - () => - application.navigateToApp(`${APP_ID}:${SecurityPageName.hosts}`, { - replace: true, - path, - }), - 0 - ); + application.navigateToApp(`${APP_ID}:${SecurityPageName.hosts}`, { + replace: true, + path, + }); + break; case 'ml-network': - window.setTimeout( - () => - application.navigateToApp(`${APP_ID}:${SecurityPageName.network}`, { - replace: true, - path: `/ml-network${path}`, - }), - 0 - ); + application.navigateToApp(`${APP_ID}:${SecurityPageName.network}`, { + replace: true, + path: `/ml-network${path}`, + }); + break; case SecurityPageName.network: - window.setTimeout( - () => - application.navigateToApp(`${APP_ID}:${SecurityPageName.network}`, { - replace: true, - path, - }), - 0 - ); + application.navigateToApp(`${APP_ID}:${SecurityPageName.network}`, { + replace: true, + path, + }); + break; case SecurityPageName.timelines: - window.setTimeout( - () => - application.navigateToApp(`${APP_ID}:${SecurityPageName.timelines}`, { - replace: true, - path, - }), - 0 - ); + application.navigateToApp(`${APP_ID}:${SecurityPageName.timelines}`, { + replace: true, + path, + }); + break; case SecurityPageName.case: - window.setTimeout( - () => - application.navigateToApp(`${APP_ID}:${SecurityPageName.case}`, { - replace: true, - path, - }), - 0 - ); + application.navigateToApp(`${APP_ID}:${SecurityPageName.case}`, { + replace: true, + path, + }); + break; case 'detections': - window.setTimeout( - () => - application.navigateToApp(`${APP_ID}:${SecurityPageName.alerts}`, { - replace: true, - path, - }), - 0 - ); + application.navigateToApp(`${APP_ID}:${SecurityPageName.alerts}`, { + replace: true, + path, + }); + break; default: - window.setTimeout( - () => - application.navigateToApp(`${APP_ID}:${SecurityPageName.overview}`, { - replace: true, - path: `?${search}`, - }), - 0 - ); + application.navigateToApp(`${APP_ID}:${SecurityPageName.overview}`, { + replace: true, + path: `?${search}`, + }); + break; } }; diff --git a/x-pack/plugins/security_solution/public/hosts/pages/index.tsx b/x-pack/plugins/security_solution/public/hosts/pages/index.tsx index c96992f36bed8..c2285cf0a97e1 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/index.tsx @@ -40,6 +40,12 @@ export const HostsContainer = React.memo<Props>(({ url }) => { <GlobalTime> {({ to, from, setQuery, deleteQuery, isInitializing }) => ( <Switch> + <Route + path="/ml-hosts" + render={({ location, match }) => ( + <MlHostConditionalContainer location={location} url={match.url} /> + )} + /> <Route path={getHostsTabPath()} render={() => ( @@ -83,12 +89,7 @@ export const HostsContainer = React.memo<Props>(({ url }) => { return null; }} /> - <Route - path="/ml-hosts" - render={({ location, match }) => ( - <MlHostConditionalContainer location={location} url={match.url} /> - )} - /> + <Route exact strict diff --git a/x-pack/plugins/security_solution/public/network/pages/index.tsx b/x-pack/plugins/security_solution/public/network/pages/index.tsx index 0184252b1f23c..c7a8a5f705dfe 100644 --- a/x-pack/plugins/security_solution/public/network/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/index.tsx @@ -16,6 +16,7 @@ import { Network } from './network'; import { GlobalTime } from '../../common/containers/global_time'; import { getNetworkRoutePath } from './navigation'; import { NetworkRouteType } from './navigation/types'; +import { MlNetworkConditionalContainer } from '../../common/components/ml/conditional_links/ml_network_conditional_container'; type Props = Partial<RouteComponentProps<{}>> & { url: string }; @@ -38,6 +39,12 @@ const NetworkContainerComponent: React.FC<Props> = () => { <GlobalTime> {({ to, from, setQuery, deleteQuery, isInitializing }) => ( <Switch> + <Route + path="/ml-network" + render={({ location, match }) => ( + <MlNetworkConditionalContainer location={location} url={match.url} /> + )} + /> <Route strict path={networkRoutePath} From 89d6d5c43278cb9ab2f2df92516c3f5cab0fc762 Mon Sep 17 00:00:00 2001 From: Gloria Hornero <snootchie.boochies@gmail.com> Date: Wed, 17 Jun 2020 09:13:45 +0200 Subject: [PATCH 26/43] fixes some cypress tests --- .../cypress/integration/detections.spec.ts | 8 ++++---- .../cypress/integration/detections_timeline.spec.ts | 4 ++-- .../cypress/integration/navigation.spec.ts | 8 ++++---- .../cypress/integration/signal_detection_rules.spec.ts | 4 ++-- .../integration/signal_detection_rules_custom.spec.ts | 6 +++--- .../integration/signal_detection_rules_export.spec.ts | 4 ++-- .../cypress/integration/signal_detection_rules_ml.spec.ts | 4 ++-- .../integration/signal_detection_rules_prebuilt.spec.ts | 6 +++--- .../security_solution/cypress/screens/security_header.ts | 4 ++-- .../plugins/security_solution/cypress/urls/navigation.ts | 4 ++-- x-pack/plugins/security_solution/cypress/urls/state.ts | 8 ++++---- .../public/network/pages/navigation/types.ts | 2 +- 12 files changed, 31 insertions(+), 31 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/detections.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detections.spec.ts index 43acdd40dadcc..2e727be1fc9b4 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detections.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detections.spec.ts @@ -28,13 +28,13 @@ import { import { esArchiverLoad } from '../tasks/es_archiver'; import { loginAndWaitForPage } from '../tasks/login'; -import { DETECTIONS } from '../urls/navigation'; +import { ALERTS_URL } from '../urls/navigation'; describe('Detections', () => { context('Closing alerts', () => { beforeEach(() => { esArchiverLoad('alerts'); - loginAndWaitForPage(DETECTIONS); + loginAndWaitForPage(ALERTS_URL); }); it('Closes and opens alerts', () => { @@ -161,7 +161,7 @@ describe('Detections', () => { context('Opening alerts', () => { beforeEach(() => { esArchiverLoad('closed_alerts'); - loginAndWaitForPage(DETECTIONS); + loginAndWaitForPage(ALERTS_URL); }); it('Open one alert when more than one closed alerts are selected', () => { @@ -207,7 +207,7 @@ describe('Detections', () => { context('Marking alerts as in-progress', () => { beforeEach(() => { esArchiverLoad('alerts'); - loginAndWaitForPage(DETECTIONS); + loginAndWaitForPage(ALERTS_URL); }); it('Mark one alert in progress when more than one open alerts are selected', () => { diff --git a/x-pack/plugins/security_solution/cypress/integration/detections_timeline.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detections_timeline.spec.ts index d3ddb2ad71e30..91617981ab14c 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detections_timeline.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detections_timeline.spec.ts @@ -15,12 +15,12 @@ import { import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; import { loginAndWaitForPage } from '../tasks/login'; -import { DETECTIONS } from '../urls/navigation'; +import { ALERTS_URL } from '../urls/navigation'; describe('Detections timeline', () => { beforeEach(() => { esArchiverLoad('timeline_alerts'); - loginAndWaitForPage(DETECTIONS); + loginAndWaitForPage(ALERTS_URL); }); afterEach(() => { diff --git a/x-pack/plugins/security_solution/cypress/integration/navigation.spec.ts b/x-pack/plugins/security_solution/cypress/integration/navigation.spec.ts index 51743a2d3422f..67b72982f44e0 100644 --- a/x-pack/plugins/security_solution/cypress/integration/navigation.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/navigation.spec.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { DETECTIONS, HOSTS, NETWORK, OVERVIEW, TIMELINES } from '../screens/security_header'; +import { ALERTS, HOSTS, NETWORK, OVERVIEW, TIMELINES } from '../screens/security_header'; import { loginAndWaitForPage } from '../tasks/login'; import { navigateFromHeaderTo } from '../tasks/security_header'; @@ -29,9 +29,9 @@ describe('top-level navigation common to all pages in the Security app', () => { cy.url().should('include', '/security/network'); }); - it('navigates to the Detections page', () => { - navigateFromHeaderTo(DETECTIONS); - cy.url().should('include', '/security/detections'); + it('navigates to the Alerts page', () => { + navigateFromHeaderTo(ALERTS); + cy.url().should('include', '/security/alerts'); }); it('navigates to the Timelines page', () => { diff --git a/x-pack/plugins/security_solution/cypress/integration/signal_detection_rules.spec.ts b/x-pack/plugins/security_solution/cypress/integration/signal_detection_rules.spec.ts index e8f9411c149d4..72a86e3ffffc5 100644 --- a/x-pack/plugins/security_solution/cypress/integration/signal_detection_rules.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/signal_detection_rules.spec.ts @@ -26,7 +26,7 @@ import { waitForRuleToBeActivated, } from '../tasks/alert_detection_rules'; -import { DETECTIONS } from '../urls/navigation'; +import { ALERTS_URL } from '../urls/navigation'; describe('Detection rules', () => { before(() => { @@ -38,7 +38,7 @@ describe('Detection rules', () => { }); it('Sorts by activated rules', () => { - loginAndWaitForPageWithoutDateRange(DETECTIONS); + loginAndWaitForPageWithoutDateRange(ALERTS_URL); waitForAlertsPanelToBeLoaded(); waitForAlertsIndexToBeCreated(); goToManageAlertDetectionRules(); diff --git a/x-pack/plugins/security_solution/cypress/integration/signal_detection_rules_custom.spec.ts b/x-pack/plugins/security_solution/cypress/integration/signal_detection_rules_custom.spec.ts index e5cec16c48a37..48d0c2e7238cd 100644 --- a/x-pack/plugins/security_solution/cypress/integration/signal_detection_rules_custom.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/signal_detection_rules_custom.spec.ts @@ -62,7 +62,7 @@ import { import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; -import { DETECTIONS } from '../urls/navigation'; +import { ALERTS_URL } from '../urls/navigation'; describe('Detection rules, custom', () => { before(() => { @@ -74,7 +74,7 @@ describe('Detection rules, custom', () => { }); it('Creates and activates a new custom rule', () => { - loginAndWaitForPageWithoutDateRange(DETECTIONS); + loginAndWaitForPageWithoutDateRange(ALERTS_URL); waitForAlertsPanelToBeLoaded(); waitForAlertsIndexToBeCreated(); goToManageAlertDetectionRules(); @@ -169,7 +169,7 @@ describe('Detection rules, custom', () => { describe('Deletes custom rules', () => { beforeEach(() => { esArchiverLoad('custom_rules'); - loginAndWaitForPageWithoutDateRange(DETECTIONS); + loginAndWaitForPageWithoutDateRange(ALERTS_URL); waitForAlertsPanelToBeLoaded(); waitForAlertsIndexToBeCreated(); goToManageAlertDetectionRules(); diff --git a/x-pack/plugins/security_solution/cypress/integration/signal_detection_rules_export.spec.ts b/x-pack/plugins/security_solution/cypress/integration/signal_detection_rules_export.spec.ts index 4a12990438999..edb559bf6a279 100644 --- a/x-pack/plugins/security_solution/cypress/integration/signal_detection_rules_export.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/signal_detection_rules_export.spec.ts @@ -13,7 +13,7 @@ import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; import { exportFirstRule } from '../tasks/alert_detection_rules'; -import { DETECTIONS } from '../urls/navigation'; +import { ALERTS_URL } from '../urls/navigation'; const EXPECTED_EXPORTED_RULE_FILE_PATH = 'cypress/test_files/expected_rules_export.ndjson'; @@ -32,7 +32,7 @@ describe('Export rules', () => { }); it('Exports a custom rule', () => { - loginAndWaitForPageWithoutDateRange(DETECTIONS); + loginAndWaitForPageWithoutDateRange(ALERTS_URL); waitForAlertsPanelToBeLoaded(); waitForAlertsIndexToBeCreated(); goToManageAlertDetectionRules(); diff --git a/x-pack/plugins/security_solution/cypress/integration/signal_detection_rules_ml.spec.ts b/x-pack/plugins/security_solution/cypress/integration/signal_detection_rules_ml.spec.ts index fd2dff27ad359..3e0fc2e1b37fd 100644 --- a/x-pack/plugins/security_solution/cypress/integration/signal_detection_rules_ml.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/signal_detection_rules_ml.spec.ts @@ -58,7 +58,7 @@ import { import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; -import { DETECTIONS } from '../urls/navigation'; +import { ALERTS_URL } from '../urls/navigation'; describe('Detection rules, machine learning', () => { before(() => { @@ -70,7 +70,7 @@ describe('Detection rules, machine learning', () => { }); it('Creates and activates a new ml rule', () => { - loginAndWaitForPageWithoutDateRange(DETECTIONS); + loginAndWaitForPageWithoutDateRange(ALERTS_URL); waitForAlertsPanelToBeLoaded(); waitForAlertsIndexToBeCreated(); goToManageAlertDetectionRules(); diff --git a/x-pack/plugins/security_solution/cypress/integration/signal_detection_rules_prebuilt.spec.ts b/x-pack/plugins/security_solution/cypress/integration/signal_detection_rules_prebuilt.spec.ts index 2cd087b2ca5e1..f819c91a77374 100644 --- a/x-pack/plugins/security_solution/cypress/integration/signal_detection_rules_prebuilt.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/signal_detection_rules_prebuilt.spec.ts @@ -31,7 +31,7 @@ import { import { esArchiverLoadEmptyKibana, esArchiverUnloadEmptyKibana } from '../tasks/es_archiver'; import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; -import { DETECTIONS } from '../urls/navigation'; +import { ALERTS_URL } from '../urls/navigation'; import { totalNumberOfPrebuiltRules } from '../objects/rule'; @@ -48,7 +48,7 @@ describe('Detection rules, prebuilt rules', () => { const expectedNumberOfRules = totalNumberOfPrebuiltRules; const expectedElasticRulesBtnText = `Elastic rules (${expectedNumberOfRules})`; - loginAndWaitForPageWithoutDateRange(DETECTIONS); + loginAndWaitForPageWithoutDateRange(ALERTS_URL); waitForAlertsPanelToBeLoaded(); waitForAlertsIndexToBeCreated(); goToManageAlertDetectionRules(); @@ -73,7 +73,7 @@ describe('Deleting prebuilt rules', () => { const expectedElasticRulesBtnText = `Elastic rules (${expectedNumberOfRules})`; esArchiverLoadEmptyKibana(); - loginAndWaitForPageWithoutDateRange(DETECTIONS); + loginAndWaitForPageWithoutDateRange(ALERTS_URL); waitForAlertsPanelToBeLoaded(); waitForAlertsIndexToBeCreated(); goToManageAlertDetectionRules(); diff --git a/x-pack/plugins/security_solution/cypress/screens/security_header.ts b/x-pack/plugins/security_solution/cypress/screens/security_header.ts index c2dab051793c1..89deeee0426d0 100644 --- a/x-pack/plugins/security_solution/cypress/screens/security_header.ts +++ b/x-pack/plugins/security_solution/cypress/screens/security_header.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -export const BREADCRUMBS = '[data-test-subj="breadcrumbs"] a'; +export const ALERTS = '[data-test-subj="navigation-alerts"]'; -export const DETECTIONS = '[data-test-subj="navigation-detections"]'; +export const BREADCRUMBS = '[data-test-subj="breadcrumbs"] a'; export const HOSTS = '[data-test-subj="navigation-hosts"]'; diff --git a/x-pack/plugins/security_solution/cypress/urls/navigation.ts b/x-pack/plugins/security_solution/cypress/urls/navigation.ts index a6064056d1197..e64a5d468e550 100644 --- a/x-pack/plugins/security_solution/cypress/urls/navigation.ts +++ b/x-pack/plugins/security_solution/cypress/urls/navigation.ts @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -export const CASES = '/app/security/case'; -export const DETECTIONS = 'app/security/detections'; +export const CASES = '/app/security/cases'; +export const ALERTS_URL = 'app/security/alerts'; export const HOSTS_PAGE = '/app/security/hosts/allHosts'; export const HOSTS_PAGE_TAB_URLS = { allHosts: '/app/security/hosts/allHosts', diff --git a/x-pack/plugins/security_solution/cypress/urls/state.ts b/x-pack/plugins/security_solution/cypress/urls/state.ts index 9c6d77035bdf9..bdd90c21fbedf 100644 --- a/x-pack/plugins/security_solution/cypress/urls/state.ts +++ b/x-pack/plugins/security_solution/cypress/urls/state.ts @@ -6,12 +6,12 @@ export const ABSOLUTE_DATE_RANGE = { url: - '/app/security/network/?timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))', + '/app/security/network/flows/?timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))', urlUnlinked: - '/app/security/network/?timerange=(global:(linkTo:!(),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(),timerange:(from:1564776209186,kind:absolute,to:1564779809186)))', - urlKqlNetworkNetwork: `/app/security/network/?query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`, - urlKqlNetworkHosts: `/app/security/network/?query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`, + '/app/security/network/flows/?timerange=(global:(linkTo:!(),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(),timerange:(from:1564776209186,kind:absolute,to:1564779809186)))', + urlKqlNetworkNetwork: `/app/security/network/flows/?query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`, + urlKqlNetworkHosts: `/app/security/network/flows/?query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`, urlKqlHostsNetwork: `/app/security/hosts/allHosts?query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`, urlKqlHostsHosts: `/app/security/hosts/allHosts?query=(language:kuery,query:'source.ip:%20"10.142.0.9"')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))`, urlHost: diff --git a/x-pack/plugins/security_solution/public/network/pages/navigation/types.ts b/x-pack/plugins/security_solution/public/network/pages/navigation/types.ts index 5fd8cc172d83e..433ed7fffd741 100644 --- a/x-pack/plugins/security_solution/public/network/pages/navigation/types.ts +++ b/x-pack/plugins/security_solution/public/network/pages/navigation/types.ts @@ -67,7 +67,7 @@ export enum NetworkRouteType { anomalies = 'anomalies', tls = 'tls', http = 'http', - alerts = 'alerts', + alerts = 'external-alerts', } export type GetNetworkRoutePath = ( From 73f78ad9eaa655e560640695ae8645dcc0c92f16 Mon Sep 17 00:00:00 2001 From: Gloria Hornero <snootchie.boochies@gmail.com> Date: Wed, 17 Jun 2020 09:35:07 +0200 Subject: [PATCH 27/43] adds 'URL compatibility' test --- .../integration/url_compatibility.spec.ts | 17 +++++++++++++++++ .../cypress/urls/navigation.ts | 3 ++- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugins/security_solution/cypress/integration/url_compatibility.spec.ts diff --git a/x-pack/plugins/security_solution/cypress/integration/url_compatibility.spec.ts b/x-pack/plugins/security_solution/cypress/integration/url_compatibility.spec.ts new file mode 100644 index 0000000000000..911fd7e0f3483 --- /dev/null +++ b/x-pack/plugins/security_solution/cypress/integration/url_compatibility.spec.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { loginAndWaitForPage } from '../tasks/login'; + +import { DETECTIONS } from '../urls/navigation'; + +describe('URL compatibility', () => { + it('Redirects to Alerts from old Detections URL', () => { + loginAndWaitForPage(DETECTIONS); + + cy.url().should('include', '/security/alerts'); + }); +}); diff --git a/x-pack/plugins/security_solution/cypress/urls/navigation.ts b/x-pack/plugins/security_solution/cypress/urls/navigation.ts index e64a5d468e550..7978aebfb413b 100644 --- a/x-pack/plugins/security_solution/cypress/urls/navigation.ts +++ b/x-pack/plugins/security_solution/cypress/urls/navigation.ts @@ -4,8 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -export const CASES = '/app/security/cases'; export const ALERTS_URL = 'app/security/alerts'; +export const CASES = '/app/security/cases'; +export const DETECTIONS = '/app/siem#/detections'; export const HOSTS_PAGE = '/app/security/hosts/allHosts'; export const HOSTS_PAGE_TAB_URLS = { allHosts: '/app/security/hosts/allHosts', From 6dd88de98309097d6939e53eca7621031fa8f00e Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Wed, 17 Jun 2020 07:28:21 -0400 Subject: [PATCH 28/43] bring ml back to alerts --- .../public/common/components/header_global/index.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx b/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx index 19944aabf0dc2..fa58961587105 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx @@ -7,7 +7,6 @@ import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui'; import { pickBy } from 'lodash/fp'; import React, { useCallback } from 'react'; -import { useLocation } from 'react-router-dom'; import styled, { css } from 'styled-components'; import { gutterTimeline } from '../../lib/helpers'; @@ -42,7 +41,6 @@ interface HeaderGlobalProps { hideDetectionEngine?: boolean; } export const HeaderGlobal = React.memo<HeaderGlobalProps>(({ hideDetectionEngine = false }) => { - const currentLocation = useLocation(); const search = useGetUrlSearch(navTabs.overview); const { navigateToApp } = useKibana().services.application; const goToOverview = useCallback( @@ -90,7 +88,7 @@ export const HeaderGlobal = React.memo<HeaderGlobalProps>(({ hideDetectionEngine <FlexItem grow={false}> <EuiFlexGroup alignItems="center" gutterSize="s" responsive={false} wrap> {indicesExistOrDataTemporarilyUnavailable(indicesExist) && - currentLocation.pathname.includes(`/${SecurityPageName.alerts}/`) && ( + window.location.pathname.includes(`/${SecurityPageName.alerts}`) && ( <FlexItem grow={false}> <MlPopover /> </FlexItem> From dab391ce8ef76f92c21237b5190089421b4e105e Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Wed, 17 Jun 2020 10:56:51 -0400 Subject: [PATCH 29/43] review I --- .../security_solution/public/cases/routes.tsx | 2 ++ .../security_solution/public/hosts/routes.tsx | 2 ++ .../public/management/common/routing.ts | 4 ++-- .../components/management_page_view.tsx | 22 +++++-------------- .../pages/policy/view/policy_details.test.tsx | 3 +-- .../public/management/routes.tsx | 2 ++ .../public/network/routes.tsx | 2 ++ .../public/overview/routes.tsx | 2 ++ .../public/timelines/routes.tsx | 2 ++ .../page_objects/endpoint_page.ts | 5 ++--- .../page_objects/policy_page.ts | 14 +++++++----- 11 files changed, 31 insertions(+), 29 deletions(-) diff --git a/x-pack/plugins/security_solution/public/cases/routes.tsx b/x-pack/plugins/security_solution/public/cases/routes.tsx index 11b3fdd18f245..c321f83c693f3 100644 --- a/x-pack/plugins/security_solution/public/cases/routes.tsx +++ b/x-pack/plugins/security_solution/public/cases/routes.tsx @@ -8,11 +8,13 @@ import React from 'react'; import { Route, Switch } from 'react-router-dom'; import { Case } from './pages'; +import { NotFoundPage } from '../app/404'; export const CasesRoutes: React.FC = () => ( <Switch> <Route path="/"> <Case /> </Route> + <Route render={() => <NotFoundPage />} /> </Switch> ); diff --git a/x-pack/plugins/security_solution/public/hosts/routes.tsx b/x-pack/plugins/security_solution/public/hosts/routes.tsx index 323432e141061..e6b815cfb5036 100644 --- a/x-pack/plugins/security_solution/public/hosts/routes.tsx +++ b/x-pack/plugins/security_solution/public/hosts/routes.tsx @@ -8,9 +8,11 @@ import React from 'react'; import { Route, Switch } from 'react-router-dom'; import { HostsContainer } from './pages'; +import { NotFoundPage } from '../app/404'; export const HostsRoutes = () => ( <Switch> <Route path="/" render={({ match }) => <HostsContainer url={match.url} />} /> + <Route render={() => <NotFoundPage />} /> </Switch> ); diff --git a/x-pack/plugins/security_solution/public/management/common/routing.ts b/x-pack/plugins/security_solution/public/management/common/routing.ts index 0fc6b413ff85c..92eb7717318d3 100644 --- a/x-pack/plugins/security_solution/public/management/common/routing.ts +++ b/x-pack/plugins/security_solution/public/management/common/routing.ts @@ -74,10 +74,10 @@ export const getEndpointDetailsPath = ( export const getPoliciesPath = (search?: string) => `${generatePath(MANAGEMENT_ROUTING_POLICIES_PATH, { tabName: ManagementSubTab.policies, - })}${appendSearch(search ?? undefined)}`; + })}${appendSearch(search)}`; export const getPolicyDetailPath = (policyId: string, search?: string) => `${generatePath(MANAGEMENT_ROUTING_POLICY_DETAILS_PATH, { tabName: ManagementSubTab.policies, policyId, - })}${appendSearch(search ?? undefined)}`; + })}${appendSearch(search)}`; diff --git a/x-pack/plugins/security_solution/public/management/components/management_page_view.tsx b/x-pack/plugins/security_solution/public/management/components/management_page_view.tsx index add7f64d2a313..c3dbb93b369a9 100644 --- a/x-pack/plugins/security_solution/public/management/components/management_page_view.tsx +++ b/x-pack/plugins/security_solution/public/management/components/management_page_view.tsx @@ -4,35 +4,25 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { memo, useCallback, useMemo } from 'react'; +import React, { memo, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; -import { useParams, useHistory } from 'react-router-dom'; +import { useParams } from 'react-router-dom'; import { PageView, PageViewProps } from '../../common/components/endpoint/page_view'; import { ManagementSubTab } from '../types'; import { SecurityPageName } from '../../app/types'; import { useFormatUrl } from '../../common/components/link_to'; import { getEndpointListPath, getPoliciesPath } from '../common/routing'; +import { useNavigateByRouterEventHandler } from '../../common/hooks/endpoint/use_navigate_by_router_event_handler'; export const ManagementPageView = memo<Omit<PageViewProps, 'tabs'>>((options) => { - const history = useHistory(); const { formatUrl, search } = useFormatUrl(SecurityPageName.management); const { tabName } = useParams<{ tabName: ManagementSubTab }>(); - const goToEndpoint = useCallback( - (ev) => { - ev.preventDefault(); - history.push(getEndpointListPath({ name: 'endpointList' }, search)); - }, - [history, search] + const goToEndpoint = useNavigateByRouterEventHandler( + getEndpointListPath({ name: 'endpointList' }, search) ); - const goToPolicies = useCallback( - (ev) => { - ev.preventDefault(); - history.push(getPoliciesPath(search)); - }, - [history, search] - ); + const goToPolicies = useNavigateByRouterEventHandler(getPoliciesPath(search)); const tabs = useMemo((): PageViewProps['tabs'] | undefined => { if (options.viewType === 'details') { diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx index aa074d7076dff..315e3d29b6df2 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx @@ -17,7 +17,6 @@ describe('Policy Details', () => { const policyDetailsPathUrl = getPolicyDetailPath('1'); const policyListPathUrl = getPoliciesPath(); - const policyListPathUrlWithPrefix = getPoliciesPath(); const sleep = (ms = 100) => new Promise((wakeup) => setTimeout(wakeup, ms)); const generator = new EndpointDocGenerator(); const { history, AppWrapper, coreStart } = createAppRootMockRenderer(); @@ -93,7 +92,7 @@ describe('Policy Details', () => { const backToListButton = pageHeaderLeft.find('EuiButtonEmpty'); expect(backToListButton.prop('iconType')).toBe('arrowLeft'); - expect(backToListButton.prop('href')).toBe(policyListPathUrlWithPrefix); + expect(backToListButton.prop('href')).toBe(policyListPathUrl); expect(backToListButton.text()).toBe('Back to policy list'); const pageTitle = pageHeaderLeft.find('[data-test-subj="pageViewHeaderLeftTitle"]'); diff --git a/x-pack/plugins/security_solution/public/management/routes.tsx b/x-pack/plugins/security_solution/public/management/routes.tsx index 118d9b8c3e80b..209d7dd6dbcde 100644 --- a/x-pack/plugins/security_solution/public/management/routes.tsx +++ b/x-pack/plugins/security_solution/public/management/routes.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { Route, Switch } from 'react-router-dom'; import { ManagementContainer } from './pages'; +import { NotFoundPage } from '../app/404'; /** * Returns the React Router Routes for the management area @@ -14,5 +15,6 @@ import { ManagementContainer } from './pages'; export const ManagementRoutes = () => ( <Switch> <Route path="/" component={ManagementContainer} /> + <Route render={() => <NotFoundPage />} /> </Switch> ); diff --git a/x-pack/plugins/security_solution/public/network/routes.tsx b/x-pack/plugins/security_solution/public/network/routes.tsx index 4eca6710d04a4..d6d725512bdb7 100644 --- a/x-pack/plugins/security_solution/public/network/routes.tsx +++ b/x-pack/plugins/security_solution/public/network/routes.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { Route, Switch } from 'react-router-dom'; import { NetworkContainer } from './pages'; +import { NotFoundPage } from '../app/404'; export const NetworkRoutes = () => ( <Switch> @@ -15,5 +16,6 @@ export const NetworkRoutes = () => ( path="/" render={({ location, match }) => <NetworkContainer location={location} url={match.url} />} /> + <Route render={() => <NotFoundPage />} /> </Switch> ); diff --git a/x-pack/plugins/security_solution/public/overview/routes.tsx b/x-pack/plugins/security_solution/public/overview/routes.tsx index 31a0c482ab2c9..ff26475d7b259 100644 --- a/x-pack/plugins/security_solution/public/overview/routes.tsx +++ b/x-pack/plugins/security_solution/public/overview/routes.tsx @@ -8,9 +8,11 @@ import React from 'react'; import { Route, Switch } from 'react-router-dom'; import { Overview } from './pages'; +import { NotFoundPage } from '../app/404'; export const OverviewRoutes = () => ( <Switch> <Route path="/" render={() => <Overview />} /> + <Route render={() => <NotFoundPage />} /> </Switch> ); diff --git a/x-pack/plugins/security_solution/public/timelines/routes.tsx b/x-pack/plugins/security_solution/public/timelines/routes.tsx index 853b06c3ea995..4d1d5f603d217 100644 --- a/x-pack/plugins/security_solution/public/timelines/routes.tsx +++ b/x-pack/plugins/security_solution/public/timelines/routes.tsx @@ -8,9 +8,11 @@ import React from 'react'; import { Route, Switch } from 'react-router-dom'; import { Timelines } from './pages'; +import { NotFoundPage } from '../app/404'; export const TimelinesRoutes = () => ( <Switch> <Route path="/" render={() => <Timelines />} /> + <Route render={() => <NotFoundPage />} /> </Switch> ); diff --git a/x-pack/test/security_solution_endpoint/page_objects/endpoint_page.ts b/x-pack/test/security_solution_endpoint/page_objects/endpoint_page.ts index aae9bff96ba31..b676ca54677d6 100644 --- a/x-pack/test/security_solution_endpoint/page_objects/endpoint_page.ts +++ b/x-pack/test/security_solution_endpoint/page_objects/endpoint_page.ts @@ -17,10 +17,9 @@ export function EndpointPageProvider({ getService, getPageObjects }: FtrProvider * Navigate to the Endpoints list page */ async navigateToEndpointList(searchParams?: string) { - await pageObjects.common.navigateToUrl( + await pageObjects.common.navigateToUrlWithBrowserHistory( 'securitySolution:management', - `/endpoints${searchParams ? `?${searchParams}` : ''}`, - { shouldUseHashForSubUrl: false } + `/endpoints${searchParams ? `?${searchParams}` : ''}` ); await pageObjects.header.waitUntilLoadingHasFinished(); }, diff --git a/x-pack/test/security_solution_endpoint/page_objects/policy_page.ts b/x-pack/test/security_solution_endpoint/page_objects/policy_page.ts index 462d19c1b6d25..45664fe9f2e08 100644 --- a/x-pack/test/security_solution_endpoint/page_objects/policy_page.ts +++ b/x-pack/test/security_solution_endpoint/page_objects/policy_page.ts @@ -15,9 +15,10 @@ export function EndpointPolicyPageProvider({ getService, getPageObjects }: FtrPr * Navigates to the Endpoint Policy List */ async navigateToPolicyList() { - await pageObjects.common.navigateToUrl('securitySolution:management', '/policy', { - shouldUseHashForSubUrl: false, - }); + await pageObjects.common.navigateToUrlWithBrowserHistory( + 'securitySolution:management', + '/policy' + ); await pageObjects.header.waitUntilLoadingHasFinished(); }, @@ -53,9 +54,10 @@ export function EndpointPolicyPageProvider({ getService, getPageObjects }: FtrPr * @param policyId */ async navigateToPolicyDetails(policyId: string) { - await pageObjects.common.navigateToUrl('securitySolution:management', `/policy/${policyId}`, { - shouldUseHashForSubUrl: false, - }); + await pageObjects.common.navigateToUrlWithBrowserHistory( + 'securitySolution:management', + `/policy/${policyId}` + ); await pageObjects.header.waitUntilLoadingHasFinished(); }, From 726f92b72d3b0ebcbe22f6a8e38c68380f70f1cb Mon Sep 17 00:00:00 2001 From: Patryk Kopycinski <contact@patrykkopycinski.com> Date: Wed, 17 Jun 2020 19:10:10 +0200 Subject: [PATCH 30/43] Fix memory leak in timelines page --- .../cypress/integration/url_state.spec.ts | 10 +++--- .../components/recent_timelines/index.tsx | 32 +++++++++-------- .../components/open_timeline/index.tsx | 34 +++++++++++-------- .../timeline/selectable_timeline/index.tsx | 32 +++++++++-------- .../public/timelines/containers/all/index.tsx | 12 +++---- .../public/timelines/pages/index.tsx | 3 +- .../timelines/pages/timelines_page.test.tsx | 6 ++-- .../public/timelines/pages/timelines_page.tsx | 13 +++---- 8 files changed, 69 insertions(+), 73 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts b/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts index e75cb61eb5f79..dcf60bdcefcd8 100644 --- a/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts @@ -177,7 +177,7 @@ describe('url state', () => { cy.get(NETWORK).should( 'have.attr', 'href', - "#/link-to/network?query=(language:kuery,query:'source.ip:%20%2210.142.0.9%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))" + "/app/security/network?query=(language:kuery,query:'source.ip:%20%2210.142.0.9%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))" ); }); @@ -190,12 +190,12 @@ describe('url state', () => { cy.get(HOSTS).should( 'have.attr', 'href', - "#/link-to/hosts?query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))" + "/app/security/hosts?query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))" ); cy.get(NETWORK).should( 'have.attr', 'href', - "#/link-to/network?query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))" + "/app/security/network?query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))" ); cy.get(HOSTS_NAMES).first().invoke('text').should('eq', 'siem-kibana'); @@ -213,14 +213,14 @@ describe('url state', () => { .should( 'have.attr', 'href', - "#/link-to/hosts?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))" + "/app/security/hosts?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))" ); cy.get(BREADCRUMBS) .eq(2) .should( 'have.attr', 'href', - "#/link-to/hosts/siem-kibana?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))" + "/app/security/hosts/siem-kibana?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))" ); }); diff --git a/x-pack/plugins/security_solution/public/overview/components/recent_timelines/index.tsx b/x-pack/plugins/security_solution/public/overview/components/recent_timelines/index.tsx index 41dc544a57996..9c149a850bec9 100644 --- a/x-pack/plugins/security_solution/public/overview/components/recent_timelines/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/recent_timelines/index.tsx @@ -84,22 +84,24 @@ const StatefulRecentTimelinesComponent = React.memo<Props>( const { fetchAllTimeline, timelines, loading } = useGetAllTimeline(); - useEffect(() => { - fetchAllTimeline({ - pageInfo: { - pageIndex: 1, - pageSize: PAGE_SIZE, - }, - search: '', - sort: { - sortField: SortFieldTimeline.updated, - sortOrder: Direction.desc, - }, - onlyUserFavorite: filterBy === 'favorites', - timelineType: TimelineType.default, - }); + useEffect( + () => + fetchAllTimeline({ + pageInfo: { + pageIndex: 1, + pageSize: PAGE_SIZE, + }, + search: '', + sort: { + sortField: SortFieldTimeline.updated, + sortOrder: Direction.desc, + }, + onlyUserFavorite: filterBy === 'favorites', + timelineType: TimelineType.default, + }), // eslint-disable-next-line react-hooks/exhaustive-deps - }, [filterBy]); + [filterBy] + ); return ( <> diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.tsx index 344f10dfdb35e..24dee1460810f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.tsx @@ -109,19 +109,25 @@ export const StatefulOpenTimelineComponent = React.memo<OpenTimelineOwnProps>( const { timelineType, timelineTabs, timelineFilters } = useTimelineTypes(); const { fetchAllTimeline, timelines, loading, totalCount } = useGetAllTimeline(); - const refetch = useCallback(() => { - fetchAllTimeline({ - pageInfo: { - pageIndex: pageIndex + 1, - pageSize, - }, - search, - sort: { sortField: sortField as SortFieldTimeline, sortOrder: sortDirection as Direction }, - onlyUserFavorite: onlyFavorites, - timelineType, - }); + const refetch = useCallback( + () => + fetchAllTimeline({ + pageInfo: { + pageIndex: pageIndex + 1, + pageSize, + }, + search, + sort: { + sortField: sortField as SortFieldTimeline, + sortOrder: sortDirection as Direction, + }, + onlyUserFavorite: onlyFavorites, + timelineType, + }), + // eslint-disable-next-line react-hooks/exhaustive-deps - }, [pageIndex, pageSize, search, sortField, sortDirection, timelineType, onlyFavorites]); + [pageIndex, pageSize, search, sortField, sortDirection, timelineType, onlyFavorites] + ); /** Invoked when the user presses enters to submit the text in the search input */ const onQueryChange: OnQueryChange = useCallback((query: EuiSearchBarQuery) => { @@ -251,9 +257,7 @@ export const StatefulOpenTimelineComponent = React.memo<OpenTimelineOwnProps>( focusInput(); }, []); - useEffect(() => { - refetch(); - }, [refetch]); + useEffect(() => refetch(), [refetch]); return !isModal ? ( <OpenTimeline diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.tsx index 2e12ebad2f99d..76c3a647a9439 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/selectable_timeline/index.tsx @@ -244,22 +244,24 @@ const SelectableTimelineComponent: React.FC<SelectableTimelineProps> = ({ }, }; - useEffect(() => { - fetchAllTimeline({ - pageInfo: { - pageIndex: 1, - pageSize, - }, - search: searchTimelineValue, - sort: { - sortField: SortFieldTimeline.updated, - sortOrder: Direction.desc, - }, - onlyUserFavorite: onlyFavorites, - timelineType, - }); + useEffect( + () => + fetchAllTimeline({ + pageInfo: { + pageIndex: 1, + pageSize, + }, + search: searchTimelineValue, + sort: { + sortField: SortFieldTimeline.updated, + sortOrder: Direction.desc, + }, + onlyUserFavorite: onlyFavorites, + timelineType, + }), // eslint-disable-next-line react-hooks/exhaustive-deps - }, [onlyFavorites, pageSize, searchTimelineValue, timelineType]); + [onlyFavorites, pageSize, searchTimelineValue, timelineType] + ); return ( <EuiSelectableContainer isLoading={loading}> diff --git a/x-pack/plugins/security_solution/public/timelines/containers/all/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/all/index.tsx index 19112221cbfd0..f025cf15181c3 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/all/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/all/index.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { getOr, noop } from 'lodash/fp'; +import { getOr } from 'lodash/fp'; import memoizeOne from 'memoize-one'; import { useCallback, useState, useEffect } from 'react'; import { useDispatch } from 'react-redux'; @@ -86,15 +86,14 @@ export const useGetAllTimeline = (): AllTimelinesArgs => { const dispatch = useDispatch(); const apolloClient = useApolloClient(); const [, dispatchToaster] = useStateToaster(); - const [allTimelines, setAllTimelines] = useState<AllTimelinesArgs>({ - fetchAllTimeline: noop, + const [allTimelines, setAllTimelines] = useState<Omit<AllTimelinesArgs, 'fetchAllTimeline'>>({ loading: false, totalCount: 0, timelines: [], }); const fetchAllTimeline = useCallback( - async ({ onlyUserFavorite, pageInfo, search, sort, timelineType }: AllTimelinesVariables) => { + ({ onlyUserFavorite, pageInfo, search, sort, timelineType }: AllTimelinesVariables) => { let didCancel = false; const abortCtrl = new AbortController(); @@ -139,7 +138,6 @@ export const useGetAllTimeline = (): AllTimelinesArgs => { }) ); setAllTimelines({ - fetchAllTimeline, loading: false, totalCount, timelines: getAllTimeline(JSON.stringify(variables), timelines as TimelineResult[]), @@ -154,7 +152,6 @@ export const useGetAllTimeline = (): AllTimelinesArgs => { dispatchToaster, }); setAllTimelines({ - fetchAllTimeline, loading: false, totalCount: 0, timelines: [], @@ -168,8 +165,7 @@ export const useGetAllTimeline = (): AllTimelinesArgs => { abortCtrl.abort(); }; }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [apolloClient, allTimelines] + [apolloClient, allTimelines, dispatch, dispatchToaster] ); useEffect(() => { diff --git a/x-pack/plugins/security_solution/public/timelines/pages/index.tsx b/x-pack/plugins/security_solution/public/timelines/pages/index.tsx index 8aea1e45cce76..1f36f8f05f927 100644 --- a/x-pack/plugins/security_solution/public/timelines/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/pages/index.tsx @@ -5,7 +5,6 @@ */ import React from 'react'; -import { ApolloConsumer } from 'react-apollo'; import { Switch, Route, useHistory } from 'react-router-dom'; import { ChromeBreadcrumb } from '../../../../../../src/core/public'; @@ -55,7 +54,7 @@ export const Timelines = React.memo(() => { return ( <Switch> <Route exact path={timelinesPagePath}> - <ApolloConsumer>{(client) => <TimelinesPage apolloClient={client} />}</ApolloConsumer> + <TimelinesPage /> </Route> <Route path="/" diff --git a/x-pack/plugins/security_solution/public/timelines/pages/timelines_page.test.tsx b/x-pack/plugins/security_solution/public/timelines/pages/timelines_page.test.tsx index 3a5e3b56e5cc4..1bd5874394df3 100644 --- a/x-pack/plugins/security_solution/public/timelines/pages/timelines_page.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/pages/timelines_page.test.tsx @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import ApolloClient from 'apollo-client'; import { shallow, ShallowWrapper } from 'enzyme'; import React from 'react'; @@ -21,7 +20,6 @@ jest.mock('../../common/lib/kibana', () => { }); describe('TimelinesPageComponent', () => { - const mockAppollloClient = {} as ApolloClient<object>; let wrapper: ShallowWrapper; describe('If the user is authorized', () => { @@ -37,7 +35,7 @@ describe('TimelinesPageComponent', () => { }, }, }); - wrapper = shallow(<TimelinesPageComponent apolloClient={mockAppollloClient} />); + wrapper = shallow(<TimelinesPageComponent />); }); afterAll(() => { @@ -89,7 +87,7 @@ describe('TimelinesPageComponent', () => { }, }, }); - wrapper = shallow(<TimelinesPageComponent apolloClient={mockAppollloClient} />); + wrapper = shallow(<TimelinesPageComponent />); }); afterAll(() => { diff --git a/x-pack/plugins/security_solution/public/timelines/pages/timelines_page.tsx b/x-pack/plugins/security_solution/public/timelines/pages/timelines_page.tsx index 90eff4a561c35..089a928403b0b 100644 --- a/x-pack/plugins/security_solution/public/timelines/pages/timelines_page.tsx +++ b/x-pack/plugins/security_solution/public/timelines/pages/timelines_page.tsx @@ -5,7 +5,6 @@ */ import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import ApolloClient from 'apollo-client'; import React, { useCallback, useState } from 'react'; import styled from 'styled-components'; @@ -15,6 +14,7 @@ import { HeaderPage } from '../../common/components/header_page'; import { WrapperPage } from '../../common/components/wrapper_page'; import { useKibana } from '../../common/lib/kibana'; import { SpyRoute } from '../../common/utils/route/spy_routes'; +import { useApolloClient } from '../../common/utils/apollo_context'; import { StatefulOpenTimeline } from '../components/open_timeline'; import { NEW_TEMPLATE_TIMELINE } from '../components/timeline/properties/translations'; @@ -28,20 +28,15 @@ const TimelinesContainer = styled.div` width: 100%; `; -interface TimelinesProps<TCache = object> { - apolloClient: ApolloClient<TCache>; -} - -type OwnProps = TimelinesProps; - export const DEFAULT_SEARCH_RESULTS_PER_PAGE = 10; -export const TimelinesPageComponent: React.FC<OwnProps> = ({ apolloClient }) => { +export const TimelinesPageComponent: React.FC = () => { const [importDataModalToggle, setImportDataModalToggle] = useState<boolean>(false); const onImportTimelineBtnClick = useCallback(() => { setImportDataModalToggle(true); }, [setImportDataModalToggle]); + const apolloClient = useApolloClient(); const uiCapabilities = useKibana().services.application.capabilities; const capabilitiesCanUserCRUD: boolean = !!uiCapabilities.siem.crud; @@ -88,7 +83,7 @@ export const TimelinesPageComponent: React.FC<OwnProps> = ({ apolloClient }) => <TimelinesContainer> <StatefulOpenTimeline - apolloClient={apolloClient} + apolloClient={apolloClient!} defaultPageSize={DEFAULT_SEARCH_RESULTS_PER_PAGE} isModal={false} importDataModalToggle={importDataModalToggle && capabilitiesCanUserCRUD} From c7380d32c0d2f7fc1847fe08c841ac01425f45cb Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Wed, 17 Jun 2020 15:18:43 -0400 Subject: [PATCH 31/43] fix storage bug for timeline search bar --- x-pack/plugins/security_solution/public/plugin.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/security_solution/public/plugin.tsx b/x-pack/plugins/security_solution/public/plugin.tsx index d001b1e854400..58f0a0ddb749e 100644 --- a/x-pack/plugins/security_solution/public/plugin.tsx +++ b/x-pack/plugins/security_solution/public/plugin.tsx @@ -78,6 +78,7 @@ export class Plugin implements IPlugin<PluginSetup, PluginStart, SetupPlugins, S const services = { ...coreStart, ...startPlugins, + storage, security: plugins.security, } as StartServices; return { coreStart, startPlugins, services, store: this.store, storage }; From d2bf563e177d74dc267303c69bd581132bf442eb Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Wed, 17 Jun 2020 16:20:26 -0400 Subject: [PATCH 32/43] fix endpoint merge + functional test --- .../public/management/pages/policy/view/policy_list.tsx | 8 +++----- x-pack/test/security_solution_endpoint/config.ts | 4 ++-- .../page_objects/endpoint_page.ts | 2 +- .../page_objects/policy_page.ts | 4 ++-- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.tsx index 37ab22d97ac17..443d61c4afbbf 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_list.tsx @@ -151,11 +151,9 @@ export const PolicyList = React.memo(() => { // of the cancel and submit buttons and redirect the user back to endpoint policy path: `#/integrations${packageInfo ? `/endpoint-${packageInfo.version}/add-datasource` : ''}`, state: { - onCancelNavigateTo: ['securitySolution', { path: getPoliciesPath() }], - onCancelUrl: services.application?.getUrlForApp('securitySolution', { - path: getPoliciesPath(), - }), - onSaveNavigateTo: ['securitySolution', { path: getPoliciesPath() }], + onCancelNavigateTo: ['securitySolution:management', { path: getPoliciesPath() }], + onCancelUrl: formatUrl(getPoliciesPath()), + onSaveNavigateTo: ['securitySolution:management', { path: getPoliciesPath() }], }, } ); diff --git a/x-pack/test/security_solution_endpoint/config.ts b/x-pack/test/security_solution_endpoint/config.ts index b6c8e52386cb9..f02a6bdcd51ed 100644 --- a/x-pack/test/security_solution_endpoint/config.ts +++ b/x-pack/test/security_solution_endpoint/config.ts @@ -22,8 +22,8 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { services, apps: { ...xpackFunctionalConfig.get('apps'), - endpoint: { - pathname: '/app/endpoint', + ['securitySolutionManagement']: { + pathname: '/app/security/management', }, }, kbnTestServer: { diff --git a/x-pack/test/security_solution_endpoint/page_objects/endpoint_page.ts b/x-pack/test/security_solution_endpoint/page_objects/endpoint_page.ts index b676ca54677d6..7339903d74a0b 100644 --- a/x-pack/test/security_solution_endpoint/page_objects/endpoint_page.ts +++ b/x-pack/test/security_solution_endpoint/page_objects/endpoint_page.ts @@ -18,7 +18,7 @@ export function EndpointPageProvider({ getService, getPageObjects }: FtrProvider */ async navigateToEndpointList(searchParams?: string) { await pageObjects.common.navigateToUrlWithBrowserHistory( - 'securitySolution:management', + 'securitySolutionManagement', `/endpoints${searchParams ? `?${searchParams}` : ''}` ); await pageObjects.header.waitUntilLoadingHasFinished(); diff --git a/x-pack/test/security_solution_endpoint/page_objects/policy_page.ts b/x-pack/test/security_solution_endpoint/page_objects/policy_page.ts index e199785646487..f51ee3a225714 100644 --- a/x-pack/test/security_solution_endpoint/page_objects/policy_page.ts +++ b/x-pack/test/security_solution_endpoint/page_objects/policy_page.ts @@ -16,7 +16,7 @@ export function EndpointPolicyPageProvider({ getService, getPageObjects }: FtrPr */ async navigateToPolicyList() { await pageObjects.common.navigateToUrlWithBrowserHistory( - 'securitySolution:management', + 'securitySolutionManagement', '/policy' ); await pageObjects.header.waitUntilLoadingHasFinished(); @@ -55,7 +55,7 @@ export function EndpointPolicyPageProvider({ getService, getPageObjects }: FtrPr */ async navigateToPolicyDetails(policyId: string) { await pageObjects.common.navigateToUrlWithBrowserHistory( - 'securitySolution:management', + 'securitySolutionManagement', `/policy/${policyId}` ); await pageObjects.header.waitUntilLoadingHasFinished(); From 884ce77814319839ca117992405a5d274ddc72f8 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Wed, 17 Jun 2020 16:38:31 -0400 Subject: [PATCH 33/43] avoid timeline flyout toggle --- .../public/timelines/components/flyout/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/index.tsx index c556c2d53f7c2..38b3068f3d3aa 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/index.tsx @@ -44,7 +44,7 @@ interface OwnProps { type Props = OwnProps & ProsFromRedux; export const FlyoutComponent = React.memo<Props>( - ({ dataProviders, flyoutHeight, show, showTimeline, timelineId, usersViewing, width }) => { + ({ dataProviders, flyoutHeight, show = true, showTimeline, timelineId, usersViewing, width }) => { const handleClose = useCallback(() => showTimeline({ id: timelineId, show: false }), [ showTimeline, timelineId, From 93c8dbcfc73e73ac7be4ae6ac1883714f12a5542 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Thu, 18 Jun 2020 00:01:45 -0400 Subject: [PATCH 34/43] Fix link to ml score --- .../ml/links/create_explorer_link.test.ts | 2 +- ...lorer_link.ts => create_explorer_link.tsx} | 32 +++++++- .../__snapshots__/anomaly_score.test.tsx.snap | 77 +++++++++++++++++-- .../create_descriptions_list.test.tsx.snap | 77 +++++++++++++++++-- .../ml/score/create_description_list.tsx | 11 ++- .../get_anomalies_host_table_columns.tsx | 16 ++-- .../get_anomalies_network_table_columns.tsx | 16 ++-- 7 files changed, 197 insertions(+), 34 deletions(-) rename x-pack/plugins/security_solution/public/common/components/ml/links/{create_explorer_link.ts => create_explorer_link.tsx} (54%) diff --git a/x-pack/plugins/security_solution/public/common/components/ml/links/create_explorer_link.test.ts b/x-pack/plugins/security_solution/public/common/components/ml/links/create_explorer_link.test.ts index 387b0f1001881..4a25f82a94a61 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/links/create_explorer_link.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/ml/links/create_explorer_link.test.ts @@ -22,7 +22,7 @@ describe('create_explorer_link', () => { new Date('3000').valueOf() ); expect(entities).toEqual( - "ml#/explorer?_g=(ml:(jobIds:!(job-1)),refreshInterval:(display:Off,pause:!f,value:0),time:(from:'1970-01-01T00:00:00.000Z',mode:absolute,to:'3000-01-01T00:00:00.000Z'))&_a=(mlExplorerFilter:(),mlExplorerSwimlane:(),mlSelectLimit:(display:'10',val:10),mlShowCharts:!t)" + "#/explorer?_g=(ml:(jobIds:!(job-1)),refreshInterval:(display:Off,pause:!f,value:0),time:(from:'1970-01-01T00:00:00.000Z',mode:absolute,to:'3000-01-01T00:00:00.000Z'))&_a=(mlExplorerFilter:(),mlExplorerSwimlane:(),mlSelectLimit:(display:'10',val:10),mlShowCharts:!t)" ); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/ml/links/create_explorer_link.ts b/x-pack/plugins/security_solution/public/common/components/ml/links/create_explorer_link.tsx similarity index 54% rename from x-pack/plugins/security_solution/public/common/components/ml/links/create_explorer_link.ts rename to x-pack/plugins/security_solution/public/common/components/ml/links/create_explorer_link.tsx index 4e3efeb05e414..eb8198dac1b87 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/links/create_explorer_link.ts +++ b/x-pack/plugins/security_solution/public/common/components/ml/links/create_explorer_link.tsx @@ -4,13 +4,43 @@ * you may not use this file except in compliance with the Elastic License. */ +import { EuiLink } from '@elastic/eui'; +import React from 'react'; import { Anomaly } from '../types'; +import { useKibana } from '../../../lib/kibana'; + +interface ExplorerLinkProps { + score: Anomaly; + startDate: number; + endDate: number; + linkName: React.ReactNode; +} + +export const ExplorerLink: React.FC<ExplorerLinkProps> = ({ + score, + startDate, + endDate, + linkName, +}) => { + const { getUrlForApp } = useKibana().services.application; + return ( + <EuiLink + href={`${getUrlForApp('ml', { + path: createExplorerLink(score, startDate, endDate), + absolute: true, + })}`} + target="_blank" + > + {linkName} + </EuiLink> + ); +}; export const createExplorerLink = (score: Anomaly, startDate: number, endDate: number): string => { const startDateIso = new Date(startDate).toISOString(); const endDateIso = new Date(endDate).toISOString(); - const JOB_PREFIX = `ml#/explorer?_g=(ml:(jobIds:!(${score.jobId}))`; + const JOB_PREFIX = `#/explorer?_g=(ml:(jobIds:!(${score.jobId}))`; const REFRESH_INTERVAL = `,refreshInterval:(display:Off,pause:!f,value:0),time:(from:'${startDateIso}',mode:absolute,to:'${endDateIso}'))`; const INTERVAL_SELECTION = `&_a=(mlExplorerFilter:(),mlExplorerSwimlane:(),mlSelectLimit:(display:'10',val:10),mlShowCharts:!t)`; diff --git a/x-pack/plugins/security_solution/public/common/components/ml/score/__snapshots__/anomaly_score.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/ml/score/__snapshots__/anomaly_score.test.tsx.snap index 1cf35aac19043..249cc1fe5f896 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/score/__snapshots__/anomaly_score.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/ml/score/__snapshots__/anomaly_score.test.tsx.snap @@ -125,12 +125,77 @@ exports[`anomaly_scores renders correctly against snapshot 1`] = ` <EuiFlexItem grow={false} > - <ForwardRef - href="ml#/explorer?_g=(ml:(jobIds:!(job-1)),refreshInterval:(display:Off,pause:!f,value:0),time:(from:'1970-01-01T00:00:00.000Z',mode:absolute,to:'3000-01-01T00:00:00.000Z'))&_a=(mlExplorerFilter:(),mlExplorerSwimlane:(),mlSelectLimit:(display:'10',val:10),mlShowCharts:!t)" - target="_blank" - > - View in Machine Learning - </ForwardRef> + <ExplorerLink + endDate={32503680000000} + linkName="View in Machine Learning" + score={ + Object { + "detectorIndex": 0, + "entityName": "process.name", + "entityValue": "du", + "influencers": Array [ + Object { + "host.name": "zeek-iowa", + }, + Object { + "process.name": "du", + }, + Object { + "user.name": "root", + }, + ], + "jobId": "job-1", + "rowId": "1561157194802_0", + "severity": 16.193669439507826, + "source": Object { + "actual": Array [ + 1, + ], + "bucket_span": 900, + "by_field_name": "process.name", + "by_field_value": "du", + "detector_index": 0, + "function": "rare", + "function_description": "rare", + "influencers": Array [ + Object { + "influencer_field_name": "user.name", + "influencer_field_values": Array [ + "root", + ], + }, + Object { + "influencer_field_name": "process.name", + "influencer_field_values": Array [ + "du", + ], + }, + Object { + "influencer_field_name": "host.name", + "influencer_field_values": Array [ + "zeek-iowa", + ], + }, + ], + "initial_record_score": 16.193669439507826, + "is_interim": false, + "job_id": "job-1", + "multi_bucket_impact": 0, + "partition_field_name": "host.name", + "partition_field_value": "zeek-iowa", + "probability": 0.024041164411288146, + "record_score": 16.193669439507826, + "result_type": "record", + "timestamp": 1560664800000, + "typical": Array [ + 0.024041164411288146, + ], + }, + "time": 1560664800000, + } + } + startDate={0} + /> </EuiFlexItem> </ForwardRef>, "title": <React.Fragment> diff --git a/x-pack/plugins/security_solution/public/common/components/ml/score/__snapshots__/create_descriptions_list.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/ml/score/__snapshots__/create_descriptions_list.test.tsx.snap index 0a136eb31fd6d..2e771f9f045b8 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/score/__snapshots__/create_descriptions_list.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/ml/score/__snapshots__/create_descriptions_list.test.tsx.snap @@ -43,12 +43,77 @@ exports[`create_description_list renders correctly against snapshot 1`] = ` <EuiFlexItem grow={false} > - <EuiLink - href="ml#/explorer?_g=(ml:(jobIds:!(job-1)),refreshInterval:(display:Off,pause:!f,value:0),time:(from:'1970-01-01T00:00:00.000Z',mode:absolute,to:'3000-01-01T00:00:00.000Z'))&_a=(mlExplorerFilter:(),mlExplorerSwimlane:(),mlSelectLimit:(display:'10',val:10),mlShowCharts:!t)" - target="_blank" - > - View in Machine Learning - </EuiLink> + <ExplorerLink + endDate={32503680000000} + linkName="View in Machine Learning" + score={ + Object { + "detectorIndex": 0, + "entityName": "process.name", + "entityValue": "du", + "influencers": Array [ + Object { + "host.name": "zeek-iowa", + }, + Object { + "process.name": "du", + }, + Object { + "user.name": "root", + }, + ], + "jobId": "job-1", + "rowId": "1561157194802_0", + "severity": 16.193669439507826, + "source": Object { + "actual": Array [ + 1, + ], + "bucket_span": 900, + "by_field_name": "process.name", + "by_field_value": "du", + "detector_index": 0, + "function": "rare", + "function_description": "rare", + "influencers": Array [ + Object { + "influencer_field_name": "user.name", + "influencer_field_values": Array [ + "root", + ], + }, + Object { + "influencer_field_name": "process.name", + "influencer_field_values": Array [ + "du", + ], + }, + Object { + "influencer_field_name": "host.name", + "influencer_field_values": Array [ + "zeek-iowa", + ], + }, + ], + "initial_record_score": 16.193669439507826, + "is_interim": false, + "job_id": "job-1", + "multi_bucket_impact": 0, + "partition_field_name": "host.name", + "partition_field_value": "zeek-iowa", + "probability": 0.024041164411288146, + "record_score": 16.193669439507826, + "result_type": "record", + "timestamp": 1560664800000, + "typical": Array [ + 0.024041164411288146, + ], + }, + "time": 1560664800000, + } + } + startDate={0} + /> </EuiFlexItem> </EuiFlexGroup> </EuiDescriptionListDescription> diff --git a/x-pack/plugins/security_solution/public/common/components/ml/score/create_description_list.tsx b/x-pack/plugins/security_solution/public/common/components/ml/score/create_description_list.tsx index 0651bc5874860..133f2407c45ed 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/score/create_description_list.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/score/create_description_list.tsx @@ -14,7 +14,7 @@ import { getScoreString } from './score_health'; import { PreferenceFormattedDate } from '../../formatted_date'; import { createInfluencers } from './../influencers/create_influencers'; import * as i18n from './translations'; -import { createExplorerLink } from '../links/create_explorer_link'; +import { ExplorerLink } from '../links/create_explorer_link'; const LargeScore = styled(EuiText)` font-size: 45px; @@ -51,9 +51,12 @@ export const createDescriptionList = ( <EuiFlexGroup direction="column" gutterSize="none" responsive={false}> <EuiFlexItem grow={false}>{score.jobId}</EuiFlexItem> <EuiFlexItem grow={false}> - <EuiLink href={createExplorerLink(score, startDate, endDate)} target="_blank"> - {i18n.VIEW_IN_MACHINE_LEARNING} - </EuiLink> + <ExplorerLink + score={score} + startDate={startDate} + endDate={endDate} + linkName={i18n.VIEW_IN_MACHINE_LEARNING} + /> </EuiFlexItem> </EuiFlexGroup> ), diff --git a/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_host_table_columns.tsx b/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_host_table_columns.tsx index 53da31af4ad67..fc89189bf4f46 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_host_table_columns.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_host_table_columns.tsx @@ -7,7 +7,7 @@ /* eslint-disable react/display-name */ import React from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { Columns } from '../../paginated_table'; import { AnomaliesByHost, Anomaly, NarrowDateRange } from '../types'; import { getRowItemDraggable } from '../../tables/helpers'; @@ -18,7 +18,7 @@ import { HostDetailsLink } from '../../links'; import * as i18n from './translations'; import { getEntries } from '../get_entries'; import { DraggableScore } from '../score/draggable_score'; -import { createExplorerLink } from '../links/create_explorer_link'; +import { ExplorerLink } from '../links/create_explorer_link'; import { HostsType } from '../../../../hosts/store/model'; import { escapeDataProviderId } from '../../drag_and_drop/helpers'; import { FormattedRelativePreferenceDate } from '../../formatted_date'; @@ -55,12 +55,12 @@ export const getAnomaliesHostTableColumns = ( field: 'anomaly.jobId', sortable: true, render: (jobId, anomaliesByHost) => ( - <EuiLink - href={`${createExplorerLink(anomaliesByHost.anomaly, startDate, endDate)}`} - target="_blank" - > - {jobId} - </EuiLink> + <ExplorerLink + score={anomaliesByHost.anomaly} + startDate={startDate} + endDate={endDate} + linkName={jobId} + /> ), }, { diff --git a/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_network_table_columns.tsx b/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_network_table_columns.tsx index abd4eec8898e4..ce4269afbe5b2 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_network_table_columns.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/tables/get_anomalies_network_table_columns.tsx @@ -7,7 +7,7 @@ /* eslint-disable react/display-name */ import React from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { Columns } from '../../paginated_table'; import { Anomaly, AnomaliesByNetwork } from '../types'; @@ -19,7 +19,7 @@ import { IPDetailsLink } from '../../links'; import * as i18n from './translations'; import { getEntries } from '../get_entries'; import { DraggableScore } from '../score/draggable_score'; -import { createExplorerLink } from '../links/create_explorer_link'; +import { ExplorerLink } from '../links/create_explorer_link'; import { FormattedRelativePreferenceDate } from '../../formatted_date'; import { NetworkType } from '../../../../network/store/model'; import { escapeDataProviderId } from '../../drag_and_drop/helpers'; @@ -54,12 +54,12 @@ export const getAnomaliesNetworkTableColumns = ( field: 'anomaly.jobId', sortable: true, render: (jobId, anomaliesByHost) => ( - <EuiLink - href={`${createExplorerLink(anomaliesByHost.anomaly, startDate, endDate)}`} - target="_blank" - > - {jobId} - </EuiLink> + <ExplorerLink + score={anomaliesByHost.anomaly} + startDate={startDate} + endDate={endDate} + linkName={jobId} + /> ), }, { From fa3d27d64b7029b79a353d4aa916ffb3950bd4aa Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Thu, 18 Jun 2020 00:03:42 -0400 Subject: [PATCH 35/43] Fix breadcrumb --- .../pages/detection_engine/rules/utils.ts | 43 +++++++++++++++---- .../cases/components/all_cases/index.tsx | 4 +- .../public/cases/pages/utils.ts | 28 +++++++++--- .../common/components/header_global/index.tsx | 4 +- .../common/components/link_to/helpers.ts | 3 +- .../public/common/components/link_to/index.ts | 5 ++- .../link_to/redirect_to_detection_engine.tsx | 13 +++--- .../navigation/breadcrumbs/index.ts | 25 +++++++---- .../common/components/navigation/index.tsx | 8 +++- .../navigation/tab_navigation/index.tsx | 10 ++++- .../common/components/navigation/types.ts | 5 +++ .../public/hosts/pages/details/utils.ts | 24 ++++++++--- .../public/network/pages/ip_details/utils.ts | 27 ++++++++---- .../public/timelines/pages/index.tsx | 14 ++++-- 14 files changed, 155 insertions(+), 58 deletions(-) diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/utils.ts b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/utils.ts index 5a87f4e0b532e..cf7e198519fb5 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/utils.ts +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/utils.ts @@ -19,21 +19,30 @@ import { import * as i18nDetections from '../translations'; import * as i18nRules from './translations'; import { RouteSpyState } from '../../../../common/utils/route/types'; +import { GetUrlForApp } from '../../../../common/components/navigation/types'; +import { SecurityPageName } from '../../../../app/types'; +import { APP_ID } from '../../../../../common/constants'; -const getTabBreadcrumb = (pathname: string, search: string[]) => { +const getTabBreadcrumb = (pathname: string, search: string[], getUrlForApp: GetUrlForApp) => { const tabPath = pathname.split('/')[1]; if (tabPath === 'alerts') { return { text: i18nDetections.ALERT, - href: `${getDetectionEngineTabUrl(tabPath)}${!isEmpty(search[0]) ? search[0] : ''}`, + href: getUrlForApp(`${APP_ID}:${SecurityPageName.alerts}`, { + path: getDetectionEngineTabUrl(tabPath, !isEmpty(search[0]) ? search[0] : ''), + absolute: true, + }), }; } if (tabPath === 'rules') { return { text: i18nRules.PAGE_TITLE, - href: `${getRulesUrl()}${!isEmpty(search[0]) ? search[0] : ''}`, + href: getUrlForApp(`${APP_ID}:${SecurityPageName.alerts}`, { + path: getRulesUrl(!isEmpty(search[0]) ? search[0] : ''), + absolute: true, + }), }; } }; @@ -44,15 +53,22 @@ const isRuleCreatePage = (pathname: string) => const isRuleEditPage = (pathname: string) => pathname.includes('/rules') && pathname.includes('/edit'); -export const getBreadcrumbs = (params: RouteSpyState, search: string[]): ChromeBreadcrumb[] => { +export const getBreadcrumbs = ( + params: RouteSpyState, + search: string[], + getUrlForApp: GetUrlForApp +): ChromeBreadcrumb[] => { let breadcrumb = [ { text: i18nDetections.PAGE_TITLE, - href: `${getDetectionEngineUrl()}${!isEmpty(search[0]) ? search[0] : ''}`, + href: getUrlForApp(`${APP_ID}:${SecurityPageName.alerts}`, { + path: !isEmpty(search[0]) ? search[0] : '', + absolute: true, + }), }, ]; - const tabBreadcrumb = getTabBreadcrumb(params.pathName, search); + const tabBreadcrumb = getTabBreadcrumb(params.pathName, search, getUrlForApp); if (tabBreadcrumb) { breadcrumb = [...breadcrumb, tabBreadcrumb]; @@ -63,7 +79,10 @@ export const getBreadcrumbs = (params: RouteSpyState, search: string[]): ChromeB ...breadcrumb, { text: params.state.ruleName, - href: `${getRuleDetailsUrl(params.detailName)}${!isEmpty(search[1]) ? search[1] : ''}`, + href: getUrlForApp(`${APP_ID}:${SecurityPageName.alerts}`, { + path: getRuleDetailsUrl(params.detailName, !isEmpty(search[0]) ? search[0] : ''), + absolute: true, + }), }, ]; } @@ -73,7 +92,10 @@ export const getBreadcrumbs = (params: RouteSpyState, search: string[]): ChromeB ...breadcrumb, { text: i18nRules.ADD_PAGE_TITLE, - href: `${getCreateRuleUrl()}${!isEmpty(search[1]) ? search[1] : ''}`, + href: getUrlForApp(`${APP_ID}:${SecurityPageName.alerts}`, { + path: getCreateRuleUrl(!isEmpty(search[0]) ? search[0] : ''), + absolute: true, + }), }, ]; } @@ -83,7 +105,10 @@ export const getBreadcrumbs = (params: RouteSpyState, search: string[]): ChromeB ...breadcrumb, { text: i18nRules.EDIT_PAGE_TITLE, - href: `${getEditRuleUrl(params.detailName)}${!isEmpty(search[1]) ? search[1] : ''}`, + href: getUrlForApp(`${APP_ID}:${SecurityPageName.alerts}`, { + path: getEditRuleUrl(params.detailName, !isEmpty(search[0]) ? search[0] : ''), + absolute: true, + }), }, ]; } diff --git a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx index 4a62a14968a41..2de957039efe6 100644 --- a/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/all_cases/index.tsx @@ -354,7 +354,7 @@ export const AllCases = React.memo<AllCasesProps>( isDisabled={!userCanCrud} fill onClick={goToCreateCase} - href={formatUrl(getCreateCaseUrl(urlSearch))} + href={formatUrl(getCreateCaseUrl())} iconType="plusInCircle" data-test-subj="createNewCaseBtn" > @@ -432,7 +432,7 @@ export const AllCases = React.memo<AllCasesProps>( fill size="s" onClick={goToCreateCase} - href={formatUrl(getCreateCaseUrl(urlSearch))} + href={formatUrl(getCreateCaseUrl())} iconType="plusInCircle" > {i18n.ADD_NEW_CASE} diff --git a/x-pack/plugins/security_solution/public/cases/pages/utils.ts b/x-pack/plugins/security_solution/public/cases/pages/utils.ts index 0b60d66756d0c..312c7b7a846ce 100644 --- a/x-pack/plugins/security_solution/public/cases/pages/utils.ts +++ b/x-pack/plugins/security_solution/public/cases/pages/utils.ts @@ -8,17 +8,27 @@ import { isEmpty } from 'lodash/fp'; import { ChromeBreadcrumb } from 'src/core/public'; -import { getCaseDetailsUrl, getCaseUrl, getCreateCaseUrl } from '../../common/components/link_to'; +import { getCaseDetailsUrl, getCreateCaseUrl } from '../../common/components/link_to'; import { RouteSpyState } from '../../common/utils/route/types'; import * as i18n from './translations'; +import { GetUrlForApp } from '../../common/components/navigation/types'; +import { APP_ID } from '../../../common/constants'; +import { SecurityPageName } from '../../app/types'; -export const getBreadcrumbs = (params: RouteSpyState, search: string[]): ChromeBreadcrumb[] => { - const queryParameters = !isEmpty(search[0]) ? search[0] : null; +export const getBreadcrumbs = ( + params: RouteSpyState, + search: string[], + getUrlForApp: GetUrlForApp +): ChromeBreadcrumb[] => { + const queryParameters = !isEmpty(search[0]) ? search[0] : ''; let breadcrumb = [ { text: i18n.PAGE_TITLE, - href: getCaseUrl(queryParameters), + href: getUrlForApp(`${APP_ID}:${SecurityPageName.case}`, { + path: queryParameters, + absolute: true, + }), }, ]; if (params.detailName === 'create') { @@ -26,7 +36,10 @@ export const getBreadcrumbs = (params: RouteSpyState, search: string[]): ChromeB ...breadcrumb, { text: i18n.CREATE_BC_TITLE, - href: getCreateCaseUrl(queryParameters), + href: getUrlForApp(`${APP_ID}:${SecurityPageName.case}`, { + path: getCreateCaseUrl(queryParameters), + absolute: true, + }), }, ]; } else if (params.detailName != null) { @@ -34,7 +47,10 @@ export const getBreadcrumbs = (params: RouteSpyState, search: string[]): ChromeB ...breadcrumb, { text: params.state?.caseTitle ?? '', - href: getCaseDetailsUrl({ id: params.detailName, search: queryParameters }), + href: getUrlForApp(`${APP_ID}:${SecurityPageName.case}`, { + path: getCaseDetailsUrl({ id: params.detailName, search: queryParameters }), + absolute: true, + }), }, ]; } diff --git a/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx b/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx index fa58961587105..de19c1903586a 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/header_global/index.tsx @@ -19,7 +19,7 @@ import * as i18n from './translations'; import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../containers/source'; import { useGetUrlSearch } from '../navigation/use_get_url_search'; import { useKibana } from '../../lib/kibana'; -import { APP_ID, ADD_DATA_PATH } from '../../../../common/constants'; +import { APP_ID, ADD_DATA_PATH, APP_ALERTS_PATH } from '../../../../common/constants'; import { LinkAnchor } from '../links'; const Wrapper = styled.header` @@ -88,7 +88,7 @@ export const HeaderGlobal = React.memo<HeaderGlobalProps>(({ hideDetectionEngine <FlexItem grow={false}> <EuiFlexGroup alignItems="center" gutterSize="s" responsive={false} wrap> {indicesExistOrDataTemporarilyUnavailable(indicesExist) && - window.location.pathname.includes(`/${SecurityPageName.alerts}`) && ( + window.location.pathname.includes(APP_ALERTS_PATH) && ( <FlexItem grow={false}> <MlPopover /> </FlexItem> diff --git a/x-pack/plugins/security_solution/public/common/components/link_to/helpers.ts b/x-pack/plugins/security_solution/public/common/components/link_to/helpers.ts index b086745bf8f76..cf62547a0c720 100644 --- a/x-pack/plugins/security_solution/public/common/components/link_to/helpers.ts +++ b/x-pack/plugins/security_solution/public/common/components/link_to/helpers.ts @@ -6,4 +6,5 @@ import { isEmpty } from 'lodash/fp'; -export const appendSearch = (search?: string) => (isEmpty(search) ? '' : `?${search}`); +export const appendSearch = (search?: string) => + isEmpty(search) ? '' : `${search?.startsWith('?') ? search : `?${search}`}`; diff --git a/x-pack/plugins/security_solution/public/common/components/link_to/index.ts b/x-pack/plugins/security_solution/public/common/components/link_to/index.ts index 91399ebdfa285..140fa0e460172 100644 --- a/x-pack/plugins/security_solution/public/common/components/link_to/index.ts +++ b/x-pack/plugins/security_solution/public/common/components/link_to/index.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { isEmpty } from 'lodash/fp'; import { useCallback } from 'react'; import { useHistory } from 'react-router-dom'; import { SecurityPageName } from '../../../app/types'; @@ -30,7 +31,9 @@ export const useFormatUrl = (page: SecurityPageName) => { const pathArr = path.split('?'); return history.createHref({ pathname: pathArr[0], - search, + search: isEmpty(pathArr[1]) + ? search + : `${pathArr[1]}${isEmpty(search) ? '' : `&${search}`}`, }); }, [history, search] diff --git a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_detection_engine.tsx b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_detection_engine.tsx index 530f689c55bbd..20e5367bfdde6 100644 --- a/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_detection_engine.tsx +++ b/x-pack/plugins/security_solution/public/common/components/link_to/redirect_to_detection_engine.tsx @@ -8,12 +8,15 @@ import { appendSearch } from './helpers'; export const getDetectionEngineUrl = (search?: string) => `${appendSearch(search)}`; -export const getDetectionEngineTabUrl = (tabPath: string) => `/${tabPath}`; +export const getDetectionEngineTabUrl = (tabPath: string, search?: string) => + `/${tabPath}${appendSearch(search)}`; -export const getRulesUrl = () => '/rules'; +export const getRulesUrl = (search?: string) => `/rules${appendSearch(search)}`; -export const getCreateRuleUrl = () => `/rules/create`; +export const getCreateRuleUrl = (search?: string) => `/rules/create${appendSearch(search)}`; -export const getRuleDetailsUrl = (detailName: string) => `/rules/id/${detailName}`; +export const getRuleDetailsUrl = (detailName: string, search?: string) => + `/rules/id/${detailName}${appendSearch(search)}`; -export const getEditRuleUrl = (detailName: string) => `/rules/id/${detailName}/edit`; +export const getEditRuleUrl = (detailName: string, search?: string) => + `/rules/id/${detailName}/edit${appendSearch(search)}`; diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.ts b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.ts index a18d04fa65d7d..5c1c68b802726 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.ts +++ b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.ts @@ -26,13 +26,14 @@ import { getAppOverviewUrl } from '../../link_to'; import { TabNavigationProps } from '../tab_navigation/types'; import { getSearch } from '../helpers'; -import { SearchNavTab } from '../types'; +import { GetUrlForApp, SearchNavTab } from '../types'; export const setBreadcrumbs = ( spyState: RouteSpyState & TabNavigationProps, - chrome: StartServices['chrome'] + chrome: StartServices['chrome'], + getUrlForApp: GetUrlForApp ) => { - const breadcrumbs = getBreadcrumbsForRoute(spyState); + const breadcrumbs = getBreadcrumbsForRoute(spyState, getUrlForApp); if (breadcrumbs) { chrome.setBreadcrumbs(breadcrumbs); } @@ -61,7 +62,8 @@ const isAlertsRoutes = (spyState: RouteSpyState) => spyState != null && spyState.pageName === SecurityPageName.alerts; export const getBreadcrumbsForRoute = ( - object: RouteSpyState & TabNavigationProps + object: RouteSpyState & TabNavigationProps, + getUrlForApp: GetUrlForApp ): ChromeBreadcrumb[] | null => { const spyState: RouteSpyState = omit('navTabs', object); if (isHostsRoutes(spyState) && object.navTabs) { @@ -77,7 +79,8 @@ export const getBreadcrumbsForRoute = ( urlStateKeys.reduce( (acc: string[], item: SearchNavTab) => [...acc, getSearch(item, object)], [] - ) + ), + getUrlForApp ), ]; } @@ -94,7 +97,8 @@ export const getBreadcrumbsForRoute = ( urlStateKeys.reduce( (acc: string[], item: SearchNavTab) => [...acc, getSearch(item, object)], [] - ) + ), + getUrlForApp ), ]; } @@ -112,7 +116,8 @@ export const getBreadcrumbsForRoute = ( urlStateKeys.reduce( (acc: string[], item: SearchNavTab) => [...acc, getSearch(item, object)], [] - ) + ), + getUrlForApp ), ]; } @@ -130,7 +135,8 @@ export const getBreadcrumbsForRoute = ( urlStateKeys.reduce( (acc: string[], item: SearchNavTab) => [...acc, getSearch(item, object)], [] - ) + ), + getUrlForApp ), ]; } @@ -148,7 +154,8 @@ export const getBreadcrumbsForRoute = ( urlStateKeys.reduce( (acc: string[], item: SearchNavTab) => [...acc, getSearch(item, object)], [] - ) + ), + getUrlForApp ), ]; } 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 49a94a6dc21f7..5ee35e7da0f3e 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 @@ -31,7 +31,10 @@ export const SiemNavigationComponent: React.FC< flowTarget, state, }) => { - const { chrome } = useKibana().services; + const { + chrome, + application: { getUrlForApp }, + } = useKibana().services; useEffect(() => { if (pathName || pageName) { @@ -51,7 +54,8 @@ export const SiemNavigationComponent: React.FC< timeline: urlState.timeline, state, }, - chrome + chrome, + getUrlForApp ); } // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.tsx index ef2ac9524baa9..2bfeb9483b07b 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.tsx @@ -27,7 +27,7 @@ const TabNavigationItemComponent = ({ urlSearch, }: TabNavigationItemProps) => { const history = useHistory(); - const { navigateToApp } = useKibana().services.application; + const { navigateToApp, getUrlForApp } = useKibana().services.application; const { formatUrl } = useFormatUrl(((pageId ?? id) as unknown) as SecurityPageName); const handleClick = useCallback( (ev) => { @@ -41,7 +41,13 @@ const TabNavigationItemComponent = ({ }, [history, hrefWithSearch, id, navigateToApp, pageId, urlSearch] ); - const appHref = formatUrl(pageId != null ? href : ''); + const appHref = + pageId != null + ? formatUrl(href) + : getUrlForApp(`${APP_ID}:${id}`, { + path: urlSearch, + absolute: true, + }); return ( <EuiTab data-href={appHref} 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 7be1af54d0a53..80302be18355c 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 @@ -51,3 +51,8 @@ export type SiemNavTabKey = | SecurityPageName.management; export type SiemNavTab = Record<SiemNavTabKey, NavTab>; + +export type GetUrlForApp = ( + appId: string, + options?: { path?: string; absolute?: boolean } +) => string; diff --git a/x-pack/plugins/security_solution/public/hosts/pages/details/utils.ts b/x-pack/plugins/security_solution/public/hosts/pages/details/utils.ts index d45cb3368b4e1..d36ba5bc387e6 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/details/utils.ts +++ b/x-pack/plugins/security_solution/public/hosts/pages/details/utils.ts @@ -10,13 +10,13 @@ import { get, isEmpty } from 'lodash/fp'; import { ChromeBreadcrumb } from '../../../../../../../src/core/public'; import { hostsModel } from '../../store'; import { HostsTableType } from '../../store/model'; -import { - getHostsUrl, - getHostDetailsUrl, -} from '../../../common/components/link_to/redirect_to_hosts'; +import { getHostDetailsUrl } from '../../../common/components/link_to/redirect_to_hosts'; import * as i18n from '../translations'; import { HostRouteSpyState } from '../../../common/utils/route/types'; +import { GetUrlForApp } from '../../../common/components/navigation/types'; +import { APP_ID } from '../../../../common/constants'; +import { SecurityPageName } from '../../../app/types'; export const type = hostsModel.HostsType.details; @@ -29,11 +29,18 @@ const TabNameMappedToI18nKey: Record<HostsTableType, string> = { [HostsTableType.alerts]: i18n.NAVIGATION_ALERTS_TITLE, }; -export const getBreadcrumbs = (params: HostRouteSpyState, search: string[]): ChromeBreadcrumb[] => { +export const getBreadcrumbs = ( + params: HostRouteSpyState, + search: string[], + getUrlForApp: GetUrlForApp +): ChromeBreadcrumb[] => { let breadcrumb = [ { text: i18n.PAGE_TITLE, - href: `${getHostsUrl()}${!isEmpty(search[0]) ? search[0] : ''}`, + href: getUrlForApp(`${APP_ID}:${SecurityPageName.hosts}`, { + path: !isEmpty(search[0]) ? search[0] : '', + absolute: true, + }), }, ]; @@ -42,7 +49,10 @@ export const getBreadcrumbs = (params: HostRouteSpyState, search: string[]): Chr ...breadcrumb, { text: params.detailName, - href: `${getHostDetailsUrl(params.detailName)}${!isEmpty(search[1]) ? search[1] : ''}`, + href: getUrlForApp(`${APP_ID}:${SecurityPageName.hosts}`, { + path: getHostDetailsUrl(params.detailName, !isEmpty(search[0]) ? search[0] : ''), + absolute: true, + }), }, ]; } diff --git a/x-pack/plugins/security_solution/public/network/pages/ip_details/utils.ts b/x-pack/plugins/security_solution/public/network/pages/ip_details/utils.ts index b1f986f20778f..3fc9b5fef259a 100644 --- a/x-pack/plugins/security_solution/public/network/pages/ip_details/utils.ts +++ b/x-pack/plugins/security_solution/public/network/pages/ip_details/utils.ts @@ -9,14 +9,14 @@ import { get, isEmpty } from 'lodash/fp'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { ChromeBreadcrumb } from '../../../../../../../src/core/public'; import { decodeIpv6 } from '../../../common/lib/helpers'; -import { - getNetworkUrl, - getIPDetailsUrl, -} from '../../../common/components/link_to/redirect_to_network'; +import { getIPDetailsUrl } from '../../../common/components/link_to/redirect_to_network'; import { networkModel } from '../../store'; import * as i18n from '../translations'; import { NetworkRouteType } from '../navigation/types'; import { NetworkRouteSpyState } from '../../../common/utils/route/types'; +import { GetUrlForApp } from '../../../common/components/navigation/types'; +import { APP_ID } from '../../../../common/constants'; +import { SecurityPageName } from '../../../app/types'; export const type = networkModel.NetworkType.details; const TabNameMappedToI18nKey: Record<NetworkRouteType, string> = { @@ -30,12 +30,16 @@ const TabNameMappedToI18nKey: Record<NetworkRouteType, string> = { export const getBreadcrumbs = ( params: NetworkRouteSpyState, - search: string[] + search: string[], + getUrlForApp: GetUrlForApp ): ChromeBreadcrumb[] => { let breadcrumb = [ { text: i18n.PAGE_TITLE, - href: `${getNetworkUrl()}${!isEmpty(search[0]) ? search[0] : ''}`, + href: getUrlForApp(`${APP_ID}:${SecurityPageName.network}`, { + path: !isEmpty(search[0]) ? search[0] : '', + absolute: true, + }), }, ]; if (params.detailName != null) { @@ -43,9 +47,14 @@ export const getBreadcrumbs = ( ...breadcrumb, { text: decodeIpv6(params.detailName), - href: `${getIPDetailsUrl(params.detailName, params.flowTarget)}${ - !isEmpty(search[1]) ? search[1] : '' - }`, + href: getUrlForApp(`${APP_ID}:${SecurityPageName.network}`, { + path: getIPDetailsUrl( + params.detailName, + params.flowTarget, + !isEmpty(search[0]) ? search[0] : '' + ), + absolute: true, + }), }, ]; } diff --git a/x-pack/plugins/security_solution/public/timelines/pages/index.tsx b/x-pack/plugins/security_solution/public/timelines/pages/index.tsx index 1f36f8f05f927..b4cbec9bebc0d 100644 --- a/x-pack/plugins/security_solution/public/timelines/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/pages/index.tsx @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { isEmpty } from 'lodash/fp'; import React from 'react'; import { Switch, Route, useHistory } from 'react-router-dom'; @@ -11,12 +12,15 @@ import { ChromeBreadcrumb } from '../../../../../../src/core/public'; import { TimelineType } from '../../../common/types/timeline'; import { TAB_TIMELINES, TAB_TEMPLATES } from '../components/open_timeline/translations'; -import { getTimelinesUrl } from '../../common/components/link_to'; import { TimelineRouteSpyState } from '../../common/utils/route/types'; import { TimelinesPage } from './timelines_page'; import { PAGE_TITLE } from './translations'; import { appendSearch } from '../../common/components/link_to/helpers'; +import { GetUrlForApp } from '../../common/components/navigation/types'; +import { APP_ID } from '../../../common/constants'; +import { SecurityPageName } from '../../app/types'; + const timelinesPagePath = `/:tabName(${TimelineType.default}|${TimelineType.template})`; const timelinesDefaultPath = `/${TimelineType.default}`; @@ -27,12 +31,16 @@ const TabNameMappedToI18nKey: Record<string, string> = { export const getBreadcrumbs = ( params: TimelineRouteSpyState, - search: string[] + search: string[], + getUrlForApp: GetUrlForApp ): ChromeBreadcrumb[] => { let breadcrumb = [ { text: PAGE_TITLE, - href: `${getTimelinesUrl(appendSearch(search[1]))}`, + href: getUrlForApp(`${APP_ID}:${SecurityPageName.timelines}`, { + path: !isEmpty(search[0]) ? search[0] : '', + absolute: true, + }), }, ]; From 10f40b058a1ec27298e358b396a1a16a3e378a56 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Thu, 18 Jun 2020 00:03:59 -0400 Subject: [PATCH 36/43] Fix management url --- .../view/details/host_details.tsx | 22 ++++++++++++------- .../endpoint_hosts/view/details/index.tsx | 17 +++++++++----- .../pages/endpoint_hosts/view/index.test.tsx | 4 ++-- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/host_details.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/host_details.tsx index 8de16df955097..507fa18906f01 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/host_details.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/host_details.tsx @@ -26,6 +26,8 @@ import { FormattedDateAndTime } from '../../../../../common/components/endpoint/ import { useNavigateByRouterEventHandler } from '../../../../../common/hooks/endpoint/use_navigate_by_router_event_handler'; import { LinkToApp } from '../../../../../common/components/endpoint/link_to_app'; import { getEndpointDetailsPath, getPolicyDetailPath } from '../../../../common/routing'; +import { SecurityPageName } from '../../../../../app/types'; +import { useFormatUrl } from '../../../../../common/components/link_to'; const HostIds = styled(EuiListGroupItem)` margin-top: 0; @@ -51,6 +53,8 @@ export const HostDetails = memo(({ details }: { details: HostMetadata }) => { const policyStatus = useHostSelector( policyResponseStatus ) as keyof typeof POLICY_STATUS_TO_HEALTH_COLOR; + const { formatUrl } = useFormatUrl(SecurityPageName.management); + const detailsResultsUpper = useMemo(() => { return [ { @@ -77,27 +81,29 @@ export const HostDetails = memo(({ details }: { details: HostMetadata }) => { const [policyResponseUri, policyResponseRoutePath] = useMemo(() => { const { selected_host, show, ...currentUrlParams } = queryParams; return [ - getEndpointDetailsPath({ - name: 'endpointPolicyResponse', - ...currentUrlParams, - selected_host: details.host.id, - }), + formatUrl( + getEndpointDetailsPath({ + name: 'endpointPolicyResponse', + ...currentUrlParams, + selected_host: details.host.id, + }) + ), getEndpointDetailsPath({ name: 'endpointPolicyResponse', ...currentUrlParams, selected_host: details.host.id, }), ]; - }, [details.host.id, queryParams]); + }, [details.host.id, formatUrl, queryParams]); const policyStatusClickHandler = useNavigateByRouterEventHandler(policyResponseRoutePath); const [policyDetailsRoutePath, policyDetailsRouteUrl] = useMemo(() => { return [ getPolicyDetailPath(details.endpoint.policy.applied.id), - getPolicyDetailPath(details.endpoint.policy.applied.id), + formatUrl(getPolicyDetailPath(details.endpoint.policy.applied.id)), ]; - }, [details.endpoint.policy.applied.id]); + }, [details.endpoint.policy.applied.id, formatUrl]); const policyDetailsClickHandler = useNavigateByRouterEventHandler(policyDetailsRoutePath); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/index.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/index.tsx index ec38e9c46cc45..3d44b73858e90 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/index.tsx @@ -39,6 +39,8 @@ import { HostMetadata } from '../../../../../../common/endpoint/types'; import { FlyoutSubHeader, FlyoutSubHeaderProps } from './components/flyout_sub_header'; import { useNavigateByRouterEventHandler } from '../../../../../common/hooks/endpoint/use_navigate_by_router_event_handler'; import { getEndpointListPath } from '../../../../common/routing'; +import { SecurityPageName } from '../../../../../app/types'; +import { useFormatUrl } from '../../../../../common/components/link_to'; export const HostDetailsFlyout = memo(() => { const history = useHistory(); @@ -116,20 +118,23 @@ const PolicyResponseFlyoutPanel = memo<{ const responseAttentionCount = useHostSelector(policyResponseFailedOrWarningActionCount); const loading = useHostSelector(policyResponseLoading); const error = useHostSelector(policyResponseError); + const { formatUrl } = useFormatUrl(SecurityPageName.management); const [detailsUri, detailsRoutePath] = useMemo( () => [ - getEndpointListPath({ - name: 'endpointList', - ...queryParams, - selected_host: hostMeta.host.id, - }), + formatUrl( + getEndpointListPath({ + name: 'endpointList', + ...queryParams, + selected_host: hostMeta.host.id, + }) + ), getEndpointListPath({ name: 'endpointList', ...queryParams, selected_host: hostMeta.host.id, }), ], - [hostMeta.host.id, queryParams] + [hostMeta.host.id, formatUrl, queryParams] ); const backToDetailsClickHandler = useNavigateByRouterEventHandler(detailsRoutePath); const backButtonProp = useMemo((): FlyoutSubHeaderProps['backButton'] => { diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx index faa2b9d2bf006..2101f8e631a2b 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx @@ -236,7 +236,7 @@ describe('when on the hosts page', () => { const policyDetailsLink = await renderResult.findByTestId('policyDetailsValue'); expect(policyDetailsLink).not.toBeNull(); expect(policyDetailsLink.getAttribute('href')).toEqual( - `#/management/policy/${hostDetails.metadata.endpoint.policy.applied.id}` + `/policy/${hostDetails.metadata.endpoint.policy.applied.id}` ); }); @@ -252,7 +252,7 @@ describe('when on the hosts page', () => { }); const changedUrlAction = await userChangedUrlChecker; expect(changedUrlAction.payload.pathname).toEqual( - `/management/policy/${hostDetails.metadata.endpoint.policy.applied.id}` + `/policy/${hostDetails.metadata.endpoint.policy.applied.id}` ); }); From 38c57cebc3e36b9b8df241bd2ec58581c297a86e Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Thu, 18 Jun 2020 00:38:09 -0400 Subject: [PATCH 37/43] fix unit test --- .../detection_engine/rules/utils.test.ts | 8 ++- .../navigation/breadcrumbs/index.test.ts | 61 +++++++++++++------ .../components/navigation/index.test.tsx | 8 ++- 3 files changed, 55 insertions(+), 22 deletions(-) diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/utils.test.ts b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/utils.test.ts index 3b703b5db4c8a..91de1467a8310 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/utils.test.ts +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/utils.test.ts @@ -6,6 +6,9 @@ import { getBreadcrumbs } from './utils'; +const getUrlForAppMock = (appId: string, options?: { path?: string; absolute?: boolean }) => + `${appId}${options?.path ?? ''}`; + describe('getBreadcrumbs', () => { it('returns default value for incorrect params', () => { expect( @@ -17,8 +20,9 @@ describe('getBreadcrumbs', () => { search: '', pathName: 'pathName', }, - [] + [], + getUrlForAppMock ) - ).toEqual([{ href: '', text: 'Alerts' }]); + ).toEqual([{ href: 'securitySolution:alerts', text: 'Alerts' }]); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts index 75ac189ff784e..2c30f9a2e4ac3 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/index.test.ts @@ -99,6 +99,9 @@ const getMockObject = ( }, }); +const getUrlForAppMock = (appId: string, options?: { path?: string; absolute?: boolean }) => + `${appId}${options?.path ?? ''}`; + describe('Navigation Breadcrumbs', () => { const hostName = 'siem-kibana'; @@ -108,7 +111,10 @@ describe('Navigation Breadcrumbs', () => { describe('getBreadcrumbsForRoute', () => { test('should return Host breadcrumbs when supplied host pathname', () => { - const breadcrumbs = getBreadcrumbsForRoute(getMockObject('hosts', '/', undefined)); + const breadcrumbs = getBreadcrumbsForRoute( + getMockObject('hosts', '/', undefined), + getUrlForAppMock + ); expect(breadcrumbs).toEqual([ { href: '/app/security/overview', @@ -116,7 +122,7 @@ describe('Navigation Breadcrumbs', () => { }, { href: - '?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', + 'securitySolution:hosts?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', text: 'Hosts', }, { @@ -127,13 +133,16 @@ describe('Navigation Breadcrumbs', () => { }); test('should return Network breadcrumbs when supplied network pathname', () => { - const breadcrumbs = getBreadcrumbsForRoute(getMockObject('network', '/', undefined)); + const breadcrumbs = getBreadcrumbsForRoute( + getMockObject('network', '/', undefined), + getUrlForAppMock + ); expect(breadcrumbs).toEqual([ { text: 'Security', href: '/app/security/overview' }, { text: 'Network', href: - '?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', + 'securitySolution:network?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', }, { text: 'Flows', @@ -143,60 +152,76 @@ describe('Navigation Breadcrumbs', () => { }); test('should return Timelines breadcrumbs when supplied timelines pathname', () => { - const breadcrumbs = getBreadcrumbsForRoute(getMockObject('timelines', '/', undefined)); + const breadcrumbs = getBreadcrumbsForRoute( + getMockObject('timelines', '/', undefined), + getUrlForAppMock + ); expect(breadcrumbs).toEqual([ { text: 'Security', href: '/app/security/overview' }, - { text: 'Timelines', href: '' }, + { + text: 'Timelines', + href: + 'securitySolution:timelines?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', + }, ]); }); test('should return Host Details breadcrumbs when supplied a pathname with hostName', () => { - const breadcrumbs = getBreadcrumbsForRoute(getMockObject('hosts', '/', hostName)); + const breadcrumbs = getBreadcrumbsForRoute( + getMockObject('hosts', '/', hostName), + getUrlForAppMock + ); expect(breadcrumbs).toEqual([ { text: 'Security', href: '/app/security/overview' }, { text: 'Hosts', href: - '?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', + 'securitySolution:hosts?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', }, { text: 'siem-kibana', href: - '/siem-kibana?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', + 'securitySolution:hosts/siem-kibana?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', }, { text: 'Authentications', href: '' }, ]); }); test('should return IP Details breadcrumbs when supplied pathname with ipv4', () => { - const breadcrumbs = getBreadcrumbsForRoute(getMockObject('network', '/', ipv4)); + const breadcrumbs = getBreadcrumbsForRoute( + getMockObject('network', '/', ipv4), + getUrlForAppMock + ); expect(breadcrumbs).toEqual([ { text: 'Security', href: '/app/security/overview' }, { text: 'Network', href: - '?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', + 'securitySolution:network?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', }, { text: ipv4, - href: `/ip/${ipv4}/source?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))`, + href: `securitySolution:network/ip/${ipv4}/source?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))`, }, { text: 'Flows', href: '' }, ]); }); test('should return IP Details breadcrumbs when supplied pathname with ipv6', () => { - const breadcrumbs = getBreadcrumbsForRoute(getMockObject('network', '/', ipv6Encoded)); + const breadcrumbs = getBreadcrumbsForRoute( + getMockObject('network', '/', ipv6Encoded), + getUrlForAppMock + ); expect(breadcrumbs).toEqual([ { text: 'Security', href: '/app/security/overview' }, { text: 'Network', href: - '?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', + 'securitySolution:network?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', }, { text: ipv6, - href: `/ip/${ipv6Encoded}/source?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))`, + href: `securitySolution:network/ip/${ipv6Encoded}/source?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))`, }, { text: 'Flows', href: '' }, ]); @@ -205,18 +230,18 @@ describe('Navigation Breadcrumbs', () => { describe('setBreadcrumbs()', () => { test('should call chrome breadcrumb service with correct breadcrumbs', () => { - setBreadcrumbs(getMockObject('hosts', '/', hostName), chromeMock); + setBreadcrumbs(getMockObject('hosts', '/', hostName), chromeMock, getUrlForAppMock); expect(setBreadcrumbsMock).toBeCalledWith([ { text: 'Security', href: '/app/security/overview' }, { text: 'Hosts', href: - '?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', + 'securitySolution:hosts?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', }, { text: 'siem-kibana', href: - '/siem-kibana?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', + 'securitySolution:hosts/siem-kibana?timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))', }, { text: 'Authentications', href: '' }, ]); diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/index.test.tsx index b4b37e22057de..69e6ef8688c4d 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/index.test.tsx @@ -25,6 +25,7 @@ jest.mock('react-router-dom', () => ({ jest.mock('./breadcrumbs', () => ({ setBreadcrumbs: jest.fn(), })); +const mockGetUrlForApp = jest.fn(); jest.mock('../../lib/kibana', () => { return { useKibana: () => ({ @@ -32,6 +33,7 @@ jest.mock('../../lib/kibana', () => { chrome: undefined, application: { navigateToApp: jest.fn(), + getUrlForApp: mockGetUrlForApp, }, }, }), @@ -171,7 +173,8 @@ describe('SIEM Navigation', () => { }, }, }, - undefined + undefined, + mockGetUrlForApp ); }); test('it calls setBreadcrumbs with correct path on update', () => { @@ -270,7 +273,8 @@ describe('SIEM Navigation', () => { }, }, }, - undefined + undefined, + mockGetUrlForApp ); }); }); From 803a8e199eaa815eb52cea269beb0a2b50b1c2cd Mon Sep 17 00:00:00 2001 From: Gloria Hornero <snootchie.boochies@gmail.com> Date: Thu, 18 Jun 2020 11:10:52 +0200 Subject: [PATCH 38/43] fixes typecheck issue --- .../public/alerts/pages/detection_engine/rules/utils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/utils.ts b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/utils.ts index cf7e198519fb5..c244928d9a68e 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/utils.ts +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/utils.ts @@ -9,7 +9,6 @@ import { isEmpty } from 'lodash/fp'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { ChromeBreadcrumb } from '../../../../../../../../src/core/public'; import { - getDetectionEngineUrl, getDetectionEngineTabUrl, getRulesUrl, getRuleDetailsUrl, From ff936e9c5fd09e388d41930fa26626da017fcc67 Mon Sep 17 00:00:00 2001 From: Gloria Hornero <snootchie.boochies@gmail.com> Date: Thu, 18 Jun 2020 11:12:32 +0200 Subject: [PATCH 39/43] fixes remaining url cypress tests --- .../cypress/integration/url_state.spec.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts b/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts index dcf60bdcefcd8..1ae34a7768d78 100644 --- a/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts @@ -166,7 +166,10 @@ describe('url state', () => { loginAndWaitForPageWithoutDateRange(ABSOLUTE_DATE_RANGE.url); kqlSearch('source.ip: "10.142.0.9" {enter}'); - cy.url().should('include', `query=(language:kuery,query:'source.ip:%20%2210.142.0.9%22%20')`); + cy.url().should( + 'include', + `query=(language:kuery,query:%27source.ip:%20%2210.142.0.9%22%20%27)` + ); }); it('sets the url state when kql is set and check if href reflect this change', () => { @@ -177,7 +180,7 @@ describe('url state', () => { cy.get(NETWORK).should( 'have.attr', 'href', - "/app/security/network?query=(language:kuery,query:'source.ip:%20%2210.142.0.9%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))" + `${location.origin}/app/security/network?query=(language:kuery,query:%27source.ip:%20%2210.142.0.9%22%20%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))` ); }); @@ -190,12 +193,12 @@ describe('url state', () => { cy.get(HOSTS).should( 'have.attr', 'href', - "/app/security/hosts?query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))" + `${location.origin}/app/security/hosts?query=(language:kuery,query:%27host.name:%20%22siem-kibana%22%20%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))` ); cy.get(NETWORK).should( 'have.attr', 'href', - "/app/security/network?query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))" + `${location.origin}/app/security/network?query=(language:kuery,query:%27host.name:%20%22siem-kibana%22%20%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))` ); cy.get(HOSTS_NAMES).first().invoke('text').should('eq', 'siem-kibana'); @@ -206,21 +209,21 @@ describe('url state', () => { cy.get(ANOMALIES_TAB).should( 'have.attr', 'href', - "#/hosts/siem-kibana/anomalies?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))" + "/app/security/hosts/siem-kibana/anomalies?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))" ); cy.get(BREADCRUMBS) .eq(1) .should( 'have.attr', 'href', - "/app/security/hosts?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))" + `${location.origin}/app/security/hosts?query=(language:kuery,query:%27agent.type:%20%22auditbeat%22%20%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))` ); cy.get(BREADCRUMBS) .eq(2) .should( 'have.attr', 'href', - "/app/security/hosts/siem-kibana?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))" + `${location.origin}/app/security/hosts/siem-kibana?query=(language:kuery,query:%27agent.type:%20%22auditbeat%22%20%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))` ); }); @@ -255,7 +258,7 @@ describe('url state', () => { expect(matched).to.have.lengthOf(1); closeTimeline(); cy.visit('/app/kibana'); - cy.visit(`/app/security/overview?timeline\=(id:'${newTimelineId}',isOpen:!t)`); + cy.visit(`/app/security/timelines?timeline\=(id:'${newTimelineId}',isOpen:!t)`); cy.contains('a', 'Security'); cy.get(DATE_PICKER_APPLY_BUTTON_TIMELINE).invoke('text').should('not.equal', 'Updating'); cy.get(TIMELINE_TITLE).should('have.attr', 'value', timelineName); From 4a6bfdfa6bb4709d293bccdd9cae304b77cb9b47 Mon Sep 17 00:00:00 2001 From: Gloria Hornero <snootchie.boochies@gmail.com> Date: Thu, 18 Jun 2020 13:23:37 +0200 Subject: [PATCH 40/43] fixes timeline scenario --- .../security_solution/cypress/integration/url_state.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts b/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts index 1ae34a7768d78..c588a7323d69c 100644 --- a/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts @@ -258,9 +258,10 @@ describe('url state', () => { expect(matched).to.have.lengthOf(1); closeTimeline(); cy.visit('/app/kibana'); - cy.visit(`/app/security/timelines?timeline\=(id:'${newTimelineId}',isOpen:!t)`); + cy.visit(`/app/security/timelines?timeline=(id:'${newTimelineId}',isOpen:!t)`); cy.contains('a', 'Security'); cy.get(DATE_PICKER_APPLY_BUTTON_TIMELINE).invoke('text').should('not.equal', 'Updating'); + cy.get(TIMELINE_TITLE).should('be.visible'); cy.get(TIMELINE_TITLE).should('have.attr', 'value', timelineName); }); }); From 992943cbaf0735166ac442d9874ab443bd70c4a9 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Thu, 18 Jun 2020 11:41:37 -0400 Subject: [PATCH 41/43] fix link to details rule from timeline --- .../common/components/events_viewer/index.tsx | 1 + .../renderers/formatted_field_helpers.tsx | 27 +++++++++++++------ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx index 9f1d71108e2f1..1645db371802c 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx @@ -190,6 +190,7 @@ export const StatefulEventsViewer = connector( (prevProps, nextProps) => prevProps.id === nextProps.id && deepEqual(prevProps.columns, nextProps.columns) && + deepEqual(prevProps.defaultIndices, nextProps.defaultIndices) && deepEqual(prevProps.dataProviders, nextProps.dataProviders) && prevProps.deletedEventIds === nextProps.deletedEventIds && prevProps.end === nextProps.end && diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field_helpers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field_helpers.tsx index f950b847d7adc..5acf26f3f4a4b 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field_helpers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field_helpers.tsx @@ -36,6 +36,7 @@ interface RenderRuleNameProps { truncate?: boolean; value: string | number | null | undefined; } + export const RenderRuleName: React.FC<RenderRuleNameProps> = ({ contextId, eventId, @@ -46,15 +47,19 @@ export const RenderRuleName: React.FC<RenderRuleNameProps> = ({ }) => { const ruleName = `${value}`; const ruleId = linkValue; - const { formatUrl } = useFormatUrl(SecurityPageName.alerts); - const { navigateToApp } = useKibana().services.application; + const { search } = useFormatUrl(SecurityPageName.alerts); + const { navigateToApp, getUrlForApp } = useKibana().services.application; const content = truncate ? <TruncatableText>{value}</TruncatableText> : value; - const goToRuleDetails = useCallback(() => { - navigateToApp(`${APP_ID}:${SecurityPageName.alerts}`, { - path: getRuleDetailsUrl(ruleId ?? ''), - }); - }, [navigateToApp, ruleId]); + const goToRuleDetails = useCallback( + (ev) => { + ev.preventDefault(); + navigateToApp(`${APP_ID}:${SecurityPageName.alerts}`, { + path: getRuleDetailsUrl(ruleId ?? '', search), + }); + }, + [navigateToApp, ruleId, search] + ); return isString(value) && ruleName.length > 0 && ruleId != null ? ( <DefaultDraggable @@ -63,7 +68,13 @@ export const RenderRuleName: React.FC<RenderRuleNameProps> = ({ tooltipContent={value} value={value} > - <LinkAnchor onClick={goToRuleDetails} href={formatUrl(getRuleDetailsUrl(ruleId))}> + <LinkAnchor + onClick={goToRuleDetails} + href={getUrlForApp(`${APP_ID}:${SecurityPageName.alerts}`, { + path: getRuleDetailsUrl(ruleId, search), + absolute: true, + })} + > {content} </LinkAnchor> </DefaultDraggable> From c4d0f9e66991c91efe86c2dee33c51b2177f9242 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Fri, 19 Jun 2020 09:33:11 -0400 Subject: [PATCH 42/43] review remove absolute path for consistency --- .../cypress/integration/url_state.spec.ts | 10 +++++----- .../components/rules/step_rule_actions/index.tsx | 15 ++++++++++++--- .../alerts/pages/detection_engine/rules/utils.ts | 6 ------ .../security_solution/public/cases/pages/utils.ts | 3 --- .../components/ml/links/create_explorer_link.tsx | 1 - .../navigation/tab_navigation/index.tsx | 1 - .../public/hosts/pages/details/utils.ts | 2 -- .../endpoint_hosts/view/details/host_details.tsx | 6 +++--- .../pages/endpoint_hosts/view/index.test.tsx | 4 ++-- .../pages/endpoint_hosts/view/index.tsx | 4 ++-- .../public/network/pages/ip_details/utils.ts | 2 -- .../body/renderers/formatted_field_helpers.tsx | 1 - .../public/timelines/pages/index.tsx | 1 - 13 files changed, 24 insertions(+), 32 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts b/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts index c588a7323d69c..c05ae164a904d 100644 --- a/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts @@ -180,7 +180,7 @@ describe('url state', () => { cy.get(NETWORK).should( 'have.attr', 'href', - `${location.origin}/app/security/network?query=(language:kuery,query:%27source.ip:%20%2210.142.0.9%22%20%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))` + `/app/security/network?query=(language:kuery,query:%27source.ip:%20%2210.142.0.9%22%20%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))` ); }); @@ -193,12 +193,12 @@ describe('url state', () => { cy.get(HOSTS).should( 'have.attr', 'href', - `${location.origin}/app/security/hosts?query=(language:kuery,query:%27host.name:%20%22siem-kibana%22%20%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))` + `/app/security/hosts?query=(language:kuery,query:%27host.name:%20%22siem-kibana%22%20%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))` ); cy.get(NETWORK).should( 'have.attr', 'href', - `${location.origin}/app/security/network?query=(language:kuery,query:%27host.name:%20%22siem-kibana%22%20%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))` + `/app/security/network?query=(language:kuery,query:%27host.name:%20%22siem-kibana%22%20%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))` ); cy.get(HOSTS_NAMES).first().invoke('text').should('eq', 'siem-kibana'); @@ -216,14 +216,14 @@ describe('url state', () => { .should( 'have.attr', 'href', - `${location.origin}/app/security/hosts?query=(language:kuery,query:%27agent.type:%20%22auditbeat%22%20%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))` + `/app/security/hosts?query=(language:kuery,query:%27agent.type:%20%22auditbeat%22%20%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))` ); cy.get(BREADCRUMBS) .eq(2) .should( 'have.attr', 'href', - `${location.origin}/app/security/hosts/siem-kibana?query=(language:kuery,query:%27agent.type:%20%22auditbeat%22%20%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))` + `/app/security/hosts/siem-kibana?query=(language:kuery,query:%27agent.type:%20%22auditbeat%22%20%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))` ); }); diff --git a/x-pack/plugins/security_solution/public/alerts/components/rules/step_rule_actions/index.tsx b/x-pack/plugins/security_solution/public/alerts/components/rules/step_rule_actions/index.tsx index 9334bd59bebf5..061b8b0f8c36e 100644 --- a/x-pack/plugins/security_solution/public/alerts/components/rules/step_rule_actions/index.tsx +++ b/x-pack/plugins/security_solution/public/alerts/components/rules/step_rule_actions/index.tsx @@ -34,6 +34,8 @@ import { RuleActionsField } from '../rule_actions_field'; import { useKibana } from '../../../../common/lib/kibana'; import { getSchema } from './schema'; import * as I18n from './translations'; +import { APP_ID } from '../../../../../common/constants'; +import { SecurityPageName } from '../../../../app/types'; interface StepRuleActionsProps extends RuleStepProps { defaultValues?: ActionsStepRule | null; @@ -84,9 +86,16 @@ const StepRuleActionsComponent: FC<StepRuleActionsProps> = ({ schema, }); - const kibanaAbsoluteUrl = useMemo(() => application.getUrlForApp('siem', { absolute: true }), [ - application, - ]); + // TO DO need to make sure that logic is still valid + const kibanaAbsoluteUrl = useMemo(() => { + const url = application.getUrlForApp(`${APP_ID}:${SecurityPageName.alerts}`, { + absolute: true, + }); + if (url != null && url.includes('app/security/alerts')) { + return url.replace('app/security/alerts', 'app/security'); + } + return url; + }, [application]); const onSubmit = useCallback( async (enabled: boolean) => { diff --git a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/utils.ts b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/utils.ts index c244928d9a68e..203a93acd849c 100644 --- a/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/utils.ts +++ b/x-pack/plugins/security_solution/public/alerts/pages/detection_engine/rules/utils.ts @@ -30,7 +30,6 @@ const getTabBreadcrumb = (pathname: string, search: string[], getUrlForApp: GetU text: i18nDetections.ALERT, href: getUrlForApp(`${APP_ID}:${SecurityPageName.alerts}`, { path: getDetectionEngineTabUrl(tabPath, !isEmpty(search[0]) ? search[0] : ''), - absolute: true, }), }; } @@ -40,7 +39,6 @@ const getTabBreadcrumb = (pathname: string, search: string[], getUrlForApp: GetU text: i18nRules.PAGE_TITLE, href: getUrlForApp(`${APP_ID}:${SecurityPageName.alerts}`, { path: getRulesUrl(!isEmpty(search[0]) ? search[0] : ''), - absolute: true, }), }; } @@ -62,7 +60,6 @@ export const getBreadcrumbs = ( text: i18nDetections.PAGE_TITLE, href: getUrlForApp(`${APP_ID}:${SecurityPageName.alerts}`, { path: !isEmpty(search[0]) ? search[0] : '', - absolute: true, }), }, ]; @@ -80,7 +77,6 @@ export const getBreadcrumbs = ( text: params.state.ruleName, href: getUrlForApp(`${APP_ID}:${SecurityPageName.alerts}`, { path: getRuleDetailsUrl(params.detailName, !isEmpty(search[0]) ? search[0] : ''), - absolute: true, }), }, ]; @@ -93,7 +89,6 @@ export const getBreadcrumbs = ( text: i18nRules.ADD_PAGE_TITLE, href: getUrlForApp(`${APP_ID}:${SecurityPageName.alerts}`, { path: getCreateRuleUrl(!isEmpty(search[0]) ? search[0] : ''), - absolute: true, }), }, ]; @@ -106,7 +101,6 @@ export const getBreadcrumbs = ( text: i18nRules.EDIT_PAGE_TITLE, href: getUrlForApp(`${APP_ID}:${SecurityPageName.alerts}`, { path: getEditRuleUrl(params.detailName, !isEmpty(search[0]) ? search[0] : ''), - absolute: true, }), }, ]; diff --git a/x-pack/plugins/security_solution/public/cases/pages/utils.ts b/x-pack/plugins/security_solution/public/cases/pages/utils.ts index 312c7b7a846ce..76308e6a1dd9b 100644 --- a/x-pack/plugins/security_solution/public/cases/pages/utils.ts +++ b/x-pack/plugins/security_solution/public/cases/pages/utils.ts @@ -27,7 +27,6 @@ export const getBreadcrumbs = ( text: i18n.PAGE_TITLE, href: getUrlForApp(`${APP_ID}:${SecurityPageName.case}`, { path: queryParameters, - absolute: true, }), }, ]; @@ -38,7 +37,6 @@ export const getBreadcrumbs = ( text: i18n.CREATE_BC_TITLE, href: getUrlForApp(`${APP_ID}:${SecurityPageName.case}`, { path: getCreateCaseUrl(queryParameters), - absolute: true, }), }, ]; @@ -49,7 +47,6 @@ export const getBreadcrumbs = ( text: params.state?.caseTitle ?? '', href: getUrlForApp(`${APP_ID}:${SecurityPageName.case}`, { path: getCaseDetailsUrl({ id: params.detailName, search: queryParameters }), - absolute: true, }), }, ]; diff --git a/x-pack/plugins/security_solution/public/common/components/ml/links/create_explorer_link.tsx b/x-pack/plugins/security_solution/public/common/components/ml/links/create_explorer_link.tsx index eb8198dac1b87..e00f53a08a918 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/links/create_explorer_link.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/links/create_explorer_link.tsx @@ -27,7 +27,6 @@ export const ExplorerLink: React.FC<ExplorerLinkProps> = ({ <EuiLink href={`${getUrlForApp('ml', { path: createExplorerLink(score, startDate, endDate), - absolute: true, })}`} target="_blank" > diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.tsx b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.tsx index 2bfeb9483b07b..217ad0e58570f 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/navigation/tab_navigation/index.tsx @@ -46,7 +46,6 @@ const TabNavigationItemComponent = ({ ? formatUrl(href) : getUrlForApp(`${APP_ID}:${id}`, { path: urlSearch, - absolute: true, }); return ( <EuiTab diff --git a/x-pack/plugins/security_solution/public/hosts/pages/details/utils.ts b/x-pack/plugins/security_solution/public/hosts/pages/details/utils.ts index d36ba5bc387e6..5c5c7283eee47 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/details/utils.ts +++ b/x-pack/plugins/security_solution/public/hosts/pages/details/utils.ts @@ -39,7 +39,6 @@ export const getBreadcrumbs = ( text: i18n.PAGE_TITLE, href: getUrlForApp(`${APP_ID}:${SecurityPageName.hosts}`, { path: !isEmpty(search[0]) ? search[0] : '', - absolute: true, }), }, ]; @@ -51,7 +50,6 @@ export const getBreadcrumbs = ( text: params.detailName, href: getUrlForApp(`${APP_ID}:${SecurityPageName.hosts}`, { path: getHostDetailsUrl(params.detailName, !isEmpty(search[0]) ? search[0] : ''), - absolute: true, }), }, ]; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/host_details.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/host_details.tsx index ee605d07e115a..b7e90c19799c7 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/host_details.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/details/host_details.tsx @@ -100,10 +100,10 @@ export const HostDetails = memo(({ details }: { details: HostMetadata }) => { const [policyDetailsRoutePath, policyDetailsRouteUrl] = useMemo(() => { return [ - getPolicyDetailPath(details.endpoint.policy.applied.id), - formatUrl(getPolicyDetailPath(details.endpoint.policy.applied.id)), + getPolicyDetailPath(details.Endpoint.policy.applied.id), + formatUrl(getPolicyDetailPath(details.Endpoint.policy.applied.id)), ]; - }, [details.endpoint.policy.applied.id, formatUrl]); + }, [details.Endpoint.policy.applied.id, formatUrl]); const policyDetailsClickHandler = useNavigateByRouterEventHandler(policyDetailsRoutePath); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx index 7d8b4a6e22259..5fd22faa8b7e6 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx @@ -249,7 +249,7 @@ describe('when on the hosts page', () => { const policyDetailsLink = await renderResult.findByTestId('policyDetailsValue'); expect(policyDetailsLink).not.toBeNull(); expect(policyDetailsLink.getAttribute('href')).toEqual( - `/policy/${hostDetails.metadata.endpoint.policy.applied.id}` + `/policy/${hostDetails.metadata.Endpoint.policy.applied.id}` ); }); @@ -265,7 +265,7 @@ describe('when on the hosts page', () => { }); const changedUrlAction = await userChangedUrlChecker; expect(changedUrlAction.payload.pathname).toEqual( - `/policy/${hostDetails.metadata.endpoint.policy.applied.id}` + `/policy/${hostDetails.metadata.Endpoint.policy.applied.id}` ); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx index 7d2507db52d50..45a33f76ee0c5 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx @@ -164,7 +164,7 @@ export const HostList = () => { }), truncateText: true, // eslint-disable-next-line react/display-name - render: (policy: HostInfo['metadata']['endpoint']['policy']['applied']) => { + render: (policy: HostInfo['metadata']['Endpoint']['policy']['applied']) => { const toRoutePath = getPolicyDetailPath(policy.id); const toRouteUrl = formatUrl(toRoutePath); return ( @@ -183,7 +183,7 @@ export const HostList = () => { defaultMessage: 'Policy Status', }), // eslint-disable-next-line react/display-name - render: (policy: HostInfo['metadata']['endpoint']['policy']['applied'], item: HostInfo) => { + render: (policy: HostInfo['metadata']['Endpoint']['policy']['applied'], item: HostInfo) => { const toRoutePath = getEndpointDetailsPath({ name: 'endpointPolicyResponse', selected_host: item.metadata.host.id, diff --git a/x-pack/plugins/security_solution/public/network/pages/ip_details/utils.ts b/x-pack/plugins/security_solution/public/network/pages/ip_details/utils.ts index 3fc9b5fef259a..640b9d9818cdd 100644 --- a/x-pack/plugins/security_solution/public/network/pages/ip_details/utils.ts +++ b/x-pack/plugins/security_solution/public/network/pages/ip_details/utils.ts @@ -38,7 +38,6 @@ export const getBreadcrumbs = ( text: i18n.PAGE_TITLE, href: getUrlForApp(`${APP_ID}:${SecurityPageName.network}`, { path: !isEmpty(search[0]) ? search[0] : '', - absolute: true, }), }, ]; @@ -53,7 +52,6 @@ export const getBreadcrumbs = ( params.flowTarget, !isEmpty(search[0]) ? search[0] : '' ), - absolute: true, }), }, ]; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field_helpers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field_helpers.tsx index 5acf26f3f4a4b..81820e2253fc9 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field_helpers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/formatted_field_helpers.tsx @@ -72,7 +72,6 @@ export const RenderRuleName: React.FC<RenderRuleNameProps> = ({ onClick={goToRuleDetails} href={getUrlForApp(`${APP_ID}:${SecurityPageName.alerts}`, { path: getRuleDetailsUrl(ruleId, search), - absolute: true, })} > {content} diff --git a/x-pack/plugins/security_solution/public/timelines/pages/index.tsx b/x-pack/plugins/security_solution/public/timelines/pages/index.tsx index b4cbec9bebc0d..91f1980309cd0 100644 --- a/x-pack/plugins/security_solution/public/timelines/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/pages/index.tsx @@ -39,7 +39,6 @@ export const getBreadcrumbs = ( text: PAGE_TITLE, href: getUrlForApp(`${APP_ID}:${SecurityPageName.timelines}`, { path: !isEmpty(search[0]) ? search[0] : '', - absolute: true, }), }, ]; From ff22d66c374bce395c1a199ff8bcba951de1e022 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Fri, 19 Jun 2020 16:14:08 -0400 Subject: [PATCH 43/43] fix cypress test --- .../cypress/integration/url_state.spec.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts b/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts index c05ae164a904d..9059a0983093a 100644 --- a/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/url_state.spec.ts @@ -180,7 +180,7 @@ describe('url state', () => { cy.get(NETWORK).should( 'have.attr', 'href', - `/app/security/network?query=(language:kuery,query:%27source.ip:%20%2210.142.0.9%22%20%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))` + `/app/security/network?query=(language:kuery,query:'source.ip:%20%2210.142.0.9%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1564691609186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1564691609186)))` ); }); @@ -193,12 +193,12 @@ describe('url state', () => { cy.get(HOSTS).should( 'have.attr', 'href', - `/app/security/hosts?query=(language:kuery,query:%27host.name:%20%22siem-kibana%22%20%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))` + `/app/security/hosts?query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))` ); cy.get(NETWORK).should( 'have.attr', 'href', - `/app/security/network?query=(language:kuery,query:%27host.name:%20%22siem-kibana%22%20%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))` + `/app/security/network?query=(language:kuery,query:'host.name:%20%22siem-kibana%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))` ); cy.get(HOSTS_NAMES).first().invoke('text').should('eq', 'siem-kibana'); @@ -216,14 +216,14 @@ describe('url state', () => { .should( 'have.attr', 'href', - `/app/security/hosts?query=(language:kuery,query:%27agent.type:%20%22auditbeat%22%20%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))` + `/app/security/hosts?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))` ); cy.get(BREADCRUMBS) .eq(2) .should( 'have.attr', 'href', - `/app/security/hosts/siem-kibana?query=(language:kuery,query:%27agent.type:%20%22auditbeat%22%20%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))` + `/app/security/hosts/siem-kibana?query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:1564689809186,kind:absolute,to:1577914409186)),timeline:(linkTo:!(global),timerange:(from:1564689809186,kind:absolute,to:1577914409186)))` ); });