diff --git a/src/optimize/base_optimizer.js b/src/optimize/base_optimizer.js index efff7f0aa2b46b..d9df2a1955df39 100644 --- a/src/optimize/base_optimizer.js +++ b/src/optimize/base_optimizer.js @@ -256,6 +256,7 @@ export default class BaseOptimizer { profile: this.profile || false, output: { + futureEmitAssets: true, // TODO: remove on webpack 5 path: this.uiBundles.getWorkingDir(), filename: '[name].bundle.js', sourceMapFilename: '[file].map', diff --git a/src/optimize/dynamic_dll_plugin/dll_config_model.js b/src/optimize/dynamic_dll_plugin/dll_config_model.js index c7ab2fe30dd141..3e31b0a7a2eade 100644 --- a/src/optimize/dynamic_dll_plugin/dll_config_model.js +++ b/src/optimize/dynamic_dll_plugin/dll_config_model.js @@ -48,6 +48,7 @@ function generateDLL(config) { entry: dllEntry, context: dllContext, output: { + futureEmitAssets: true, // TODO: remove on webpack 5 filename: dllBundleFilename, path: dllOutputPath, publicPath: dllPublicPath, diff --git a/x-pack/legacy/plugins/siem/public/app/app.tsx b/x-pack/legacy/plugins/siem/public/app/app.tsx index 5f9199735d8c04..030cb727506498 100644 --- a/x-pack/legacy/plugins/siem/public/app/app.tsx +++ b/x-pack/legacy/plugins/siem/public/app/app.tsx @@ -16,9 +16,8 @@ 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 { I18nContext } from 'ui/i18n'; -import { KibanaContextProvider, useUiSetting$ } from '../lib/kibana'; +import { KibanaContextProvider, useKibana, useUiSetting$ } from '../lib/kibana'; import { Storage } from '../../../../../../src/plugins/kibana_utils/public'; import { DEFAULT_DARK_MODE } from '../../common/constants'; @@ -27,7 +26,7 @@ import { compose } from '../lib/compose/kibana_compose'; import { AppFrontendLibs, AppApolloClient } from '../lib/lib'; import { CoreStart, StartPlugins } from '../plugin'; import { PageRouter } from '../routes'; -import { createStore } from '../store'; +import { createStore, createInitialState } from '../store'; import { GlobalToaster, ManageGlobalToaster } from '../components/toasters'; import { MlCapabilitiesProvider } from '../components/ml/permissions/ml_capabilities_provider'; @@ -46,33 +45,30 @@ const AppPluginRootComponent: React.FC = ({ apolloClient, history, }) => ( - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + ); const AppPluginRoot = memo(AppPluginRootComponent); const StartAppComponent: FC = libs => { + const { i18n } = useKibana().services; const history = createHashHistory(); const libs$ = new BehaviorSubject(libs); - const store = createStore(undefined, libs$.pipe(pluck('apolloClient'))); + const store = createStore(createInitialState(), libs$.pipe(pluck('apolloClient'))); const [darkMode] = useUiSetting$(DEFAULT_DARK_MODE); const theme = useMemo( () => ({ @@ -83,7 +79,16 @@ const StartAppComponent: FC = libs => { ); return ( - + + + + + ); }; @@ -103,7 +108,7 @@ const SiemAppComponent: React.FC = ({ core, plugins }) => ...plugins, }} > - + ); diff --git a/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx b/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx index 5e0502840796a7..f0061e2f68f5be 100644 --- a/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx +++ b/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx @@ -19,7 +19,7 @@ import { getOr, get, isNull, isNumber } from 'lodash/fp'; import { AutoSizer } from '../auto_sizer'; import { ChartPlaceHolder } from './chart_place_holder'; -import { useTimeZone } from '../../hooks'; +import { useTimeZone } from '../../lib/kibana'; import { chartDefaultSettings, ChartSeriesConfigs, diff --git a/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx b/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx index 797344064a515d..8678666e87eb46 100644 --- a/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx +++ b/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx @@ -9,7 +9,7 @@ import { Chart, BarSeries, Axis, Position, ScaleType, Settings } from '@elastic/ import { getOr, get, isNumber } from 'lodash/fp'; import deepmerge from 'deepmerge'; -import { useTimeZone } from '../../hooks'; +import { useTimeZone } from '../../lib/kibana'; import { AutoSizer } from '../auto_sizer'; import { ChartPlaceHolder } from './chart_place_holder'; import { diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.test.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.test.tsx index c752273777d2f0..a807b4d6a838bd 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.test.tsx @@ -11,12 +11,6 @@ import { useIndexPatterns } from '../../hooks/use_index_patterns'; import { EmbeddedMapComponent } from './embedded_map'; import { SetQuery } from './types'; -jest.mock('../search_bar', () => ({ - siemFilterManager: { - addFilters: jest.fn(), - }, -})); - const mockUseIndexPatterns = useIndexPatterns as jest.Mock; jest.mock('../../hooks/use_index_patterns'); mockUseIndexPatterns.mockImplementation(() => [true, []]); diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/index_patterns_missing_prompt.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/index_patterns_missing_prompt.tsx index cba7e5ba1fd932..abd33505b67b99 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/index_patterns_missing_prompt.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/index_patterns_missing_prompt.tsx @@ -7,13 +7,13 @@ import { EuiButton, EuiCode, EuiEmptyPrompt } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; -import chrome from 'ui/chrome'; -import { useKibana } from '../../lib/kibana'; +import { useKibana, useBasePath } from '../../lib/kibana'; import * as i18n from './translations'; export const IndexPatternsMissingPromptComponent = () => { - const docLinks = useKibana().services.docLinks; + const { docLinks } = useKibana().services; + const kibanaBasePath = `${useBasePath()}/app/kibana`; return ( { values={{ defaultIndex: ( @@ -61,7 +61,7 @@ export const IndexPatternsMissingPromptComponent = () => { } actions={ ({ - siemFilterManager: { - addFilters: jest.fn(), - }, -})); - describe('MapToolTip', () => { test('placeholder component renders correctly against snapshot', () => { const wrapper = shallow(); diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/map_tool_tip/point_tool_tip_content.test.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/map_tool_tip/point_tool_tip_content.test.tsx index a9d70e4f35d355..c90af16b0d99a6 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/map_tool_tip/point_tool_tip_content.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/map_tool_tip/point_tool_tip_content.test.tsx @@ -14,12 +14,6 @@ import { HostDetailsLink, IPDetailsLink } from '../../links'; import { useMountAppended } from '../../../utils/use_mount_appended'; import { FlowTarget } from '../../../graphql/types'; -jest.mock('../../search_bar', () => ({ - siemFilterManager: { - addFilters: jest.fn(), - }, -})); - describe('PointToolTipContent', () => { const mount = useMountAppended(); diff --git a/x-pack/legacy/plugins/siem/public/components/formatted_date/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/formatted_date/index.test.tsx index 0d8222ce85e1be..2ca67057039dfd 100644 --- a/x-pack/legacy/plugins/siem/public/components/formatted_date/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/formatted_date/index.test.tsx @@ -7,13 +7,13 @@ import { mount, shallow } from 'enzyme'; import React from 'react'; -import { useDateFormat, useTimeZone } from '../../hooks'; +import { useDateFormat, useTimeZone } from '../../lib/kibana'; import { TestProviders } from '../../mock'; import { getEmptyString, getEmptyValue } from '../empty_value'; import { PreferenceFormattedDate, FormattedDate, FormattedRelativePreferenceDate } from '.'; -jest.mock('../../hooks'); +jest.mock('../../lib/kibana'); const mockUseDateFormat = useDateFormat as jest.Mock; const mockUseTimeZone = useTimeZone as jest.Mock; diff --git a/x-pack/legacy/plugins/siem/public/components/formatted_date/index.tsx b/x-pack/legacy/plugins/siem/public/components/formatted_date/index.tsx index fb579a14b04575..f74ee995c965b2 100644 --- a/x-pack/legacy/plugins/siem/public/components/formatted_date/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/formatted_date/index.tsx @@ -8,9 +8,8 @@ import moment from 'moment-timezone'; import React from 'react'; import { FormattedRelative } from '@kbn/i18n/react'; -import { useDateFormat, useTimeZone } from '../../hooks'; +import { useDateFormat, useTimeZone, useUiSetting$ } from '../../lib/kibana'; import { getOrEmptyTagFromValue } from '../empty_value'; -import { useUiSetting$ } from '../../lib/kibana'; import { LocalizedDateTooltip } from '../localized_date_tooltip'; import { getMaybeDate } from './maybe_date'; diff --git a/x-pack/legacy/plugins/siem/public/components/help_menu/index.tsx b/x-pack/legacy/plugins/siem/public/components/help_menu/index.tsx index 732d83ac6e7361..e74299f57c934a 100644 --- a/x-pack/legacy/plugins/siem/public/components/help_menu/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/help_menu/index.tsx @@ -5,13 +5,14 @@ */ import React, { useEffect } from 'react'; -import chrome from 'ui/chrome'; import { i18n } from '@kbn/i18n'; -import { documentationLinks } from 'ui/documentation_links'; +import { useKibana } from '../../lib/kibana'; export const HelpMenu = React.memo(() => { + const { chrome, docLinks } = useKibana().services; + useEffect(() => { - chrome.helpExtension.set({ + chrome.setHelpExtension({ appName: i18n.translate('xpack.siem.chrome.help.appName', { defaultMessage: 'SIEM', }), @@ -20,7 +21,7 @@ export const HelpMenu = React.memo(() => { content: i18n.translate('xpack.siem.chrome.helpMenu.documentation', { defaultMessage: 'SIEM documentation', }), - href: documentationLinks.siem.guide, + href: docLinks.links.siem.guide, iconType: 'documents', linkType: 'custom', }, diff --git a/x-pack/legacy/plugins/siem/public/components/ml/anomaly/use_anomalies_table_data.ts b/x-pack/legacy/plugins/siem/public/components/ml/anomaly/use_anomalies_table_data.ts index 48277b0b6fa52c..0baa1ef7cdd050 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/anomaly/use_anomalies_table_data.ts +++ b/x-pack/legacy/plugins/siem/public/components/ml/anomaly/use_anomalies_table_data.ts @@ -14,9 +14,8 @@ import { useStateToaster } from '../../toasters'; import { errorToToaster } from '../api/error_to_toaster'; import * as i18n from './translations'; -import { useUiSetting$ } from '../../../lib/kibana'; +import { useTimeZone, useUiSetting$ } from '../../../lib/kibana'; import { DEFAULT_ANOMALY_SCORE } from '../../../../common/constants'; -import { useTimeZone } from '../../../hooks'; interface Args { influencers?: InfluencerInput[]; diff --git a/x-pack/legacy/plugins/siem/public/components/ml/api/anomalies_table_data.ts b/x-pack/legacy/plugins/siem/public/components/ml/api/anomalies_table_data.ts index cb84d9182d2e01..35dbbf012272ee 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/api/anomalies_table_data.ts +++ b/x-pack/legacy/plugins/siem/public/components/ml/api/anomalies_table_data.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { npStart } from 'ui/new_platform'; import { Anomalies, InfluencerInput, CriteriaFields } from '../types'; import { throwIfNotOk } from '../../../hooks/api/api'; +import { KibanaServices } from '../../../lib/kibana'; export interface Body { jobIds: string[]; @@ -22,7 +22,7 @@ export interface Body { } export const anomaliesTableData = async (body: Body, signal: AbortSignal): Promise => { - const response = await npStart.core.http.fetch( + const response = await KibanaServices.get().http.fetch( '/api/ml/results/anomalies_table_data', { method: 'POST', diff --git a/x-pack/legacy/plugins/siem/public/components/ml/api/get_ml_capabilities.ts b/x-pack/legacy/plugins/siem/public/components/ml/api/get_ml_capabilities.ts index dcfd7365f8e0d5..feafbba2024dcd 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml/api/get_ml_capabilities.ts +++ b/x-pack/legacy/plugins/siem/public/components/ml/api/get_ml_capabilities.ts @@ -4,10 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { npStart } from 'ui/new_platform'; - import { InfluencerInput, MlCapabilities } from '../types'; import { throwIfNotOk } from '../../../hooks/api/api'; +import { KibanaServices } from '../../../lib/kibana'; export interface Body { jobIds: string[]; @@ -23,12 +22,15 @@ export interface Body { } export const getMlCapabilities = async (signal: AbortSignal): Promise => { - const response = await npStart.core.http.fetch('/api/ml/ml_capabilities', { - method: 'GET', - asResponse: true, - asSystemRequest: true, - signal, - }); + const response = await KibanaServices.get().http.fetch( + '/api/ml/ml_capabilities', + { + method: 'GET', + asResponse: true, + asSystemRequest: true, + signal, + } + ); await throwIfNotOk(response.response); return response.body!; diff --git a/x-pack/legacy/plugins/siem/public/components/ml_popover/api.tsx b/x-pack/legacy/plugins/siem/public/components/ml_popover/api.tsx index cf939d8e09b7ec..120fd8c404ffde 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml_popover/api.tsx +++ b/x-pack/legacy/plugins/siem/public/components/ml_popover/api.tsx @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { npStart } from 'ui/new_platform'; - import { CheckRecognizerProps, CloseJobsResponse, @@ -21,6 +19,7 @@ import { } from './types'; import { throwIfErrorAttached, throwIfErrorAttachedToSetup } from '../ml/api/throw_if_not_ok'; import { throwIfNotOk } from '../../hooks/api/api'; +import { KibanaServices } from '../../lib/kibana'; /** * Checks the ML Recognizer API to see if a given indexPattern has any compatible modules @@ -32,7 +31,7 @@ export const checkRecognizer = async ({ indexPatternName, signal, }: CheckRecognizerProps): Promise => { - const response = await npStart.core.http.fetch( + const response = await KibanaServices.get().http.fetch( `/api/ml/modules/recognize/${indexPatternName}`, { method: 'GET', @@ -53,7 +52,7 @@ export const checkRecognizer = async ({ * @param signal to cancel request */ export const getModules = async ({ moduleId = '', signal }: GetModulesProps): Promise => { - const response = await npStart.core.http.fetch( + const response = await KibanaServices.get().http.fetch( `/api/ml/modules/get_module/${moduleId}`, { method: 'GET', @@ -83,7 +82,7 @@ export const setupMlJob = async ({ groups = ['siem'], prefix = '', }: MlSetupArgs): Promise => { - const response = await npStart.core.http.fetch( + const response = await KibanaServices.get().http.fetch( `/api/ml/modules/setup/${configTemplate}`, { method: 'POST', @@ -119,7 +118,7 @@ export const startDatafeeds = async ({ datafeedIds: string[]; start: number; }): Promise => { - const response = await npStart.core.http.fetch( + const response = await KibanaServices.get().http.fetch( '/api/ml/jobs/force_start_datafeeds', { method: 'POST', @@ -149,7 +148,7 @@ export const stopDatafeeds = async ({ }: { datafeedIds: string[]; }): Promise<[StopDatafeedResponse | ErrorResponse, CloseJobsResponse]> => { - const stopDatafeedsResponse = await npStart.core.http.fetch( + const stopDatafeedsResponse = await KibanaServices.get().http.fetch( '/api/ml/jobs/stop_datafeeds', { method: 'POST', @@ -165,7 +164,7 @@ export const stopDatafeeds = async ({ const stopDatafeedsResponseJson = stopDatafeedsResponse.body!; const datafeedPrefix = 'datafeed-'; - const closeJobsResponse = await npStart.core.http.fetch( + const closeJobsResponse = await KibanaServices.get().http.fetch( '/api/ml/jobs/close_jobs', { method: 'POST', @@ -194,13 +193,16 @@ export const stopDatafeeds = async ({ * @param signal to cancel request */ export const getJobsSummary = async (signal: AbortSignal): Promise => { - const response = await npStart.core.http.fetch('/api/ml/jobs/jobs_summary', { - method: 'POST', - body: JSON.stringify({}), - asResponse: true, - asSystemRequest: true, - signal, - }); + const response = await KibanaServices.get().http.fetch( + '/api/ml/jobs/jobs_summary', + { + method: 'POST', + body: JSON.stringify({}), + asResponse: true, + asSystemRequest: true, + signal, + } + ); await throwIfNotOk(response.response); return response.body!; diff --git a/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/jobs_table.test.tsx b/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/jobs_table.test.tsx index fa524d8ff3dbc6..c9307afbd3dbff 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/jobs_table.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/jobs_table.test.tsx @@ -11,6 +11,8 @@ import { mockSiemJobs } from '../__mocks__/api'; import { cloneDeep } from 'lodash/fp'; import { SiemJob } from '../types'; +jest.mock('../../../lib/kibana'); + describe('JobsTableComponent', () => { let siemJobs: SiemJob[]; let onJobStateChangeMock = jest.fn(); diff --git a/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/jobs_table.tsx b/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/jobs_table.tsx index 34597f1761629d..5bca802a21747d 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/jobs_table.tsx +++ b/x-pack/legacy/plugins/siem/public/components/ml_popover/jobs_table/jobs_table.tsx @@ -6,7 +6,6 @@ /* eslint-disable react/display-name */ -import chrome from 'ui/chrome'; import React, { useEffect, useState } from 'react'; import { @@ -23,6 +22,7 @@ import { } from '@elastic/eui'; import styled from 'styled-components'; +import { useBasePath } from '../../../lib/kibana'; import * as i18n from './translations'; import { JobSwitch } from './job_switch'; import { SiemJob } from '../types'; @@ -38,7 +38,8 @@ const truncateThreshold = 200; const getJobsTableColumns = ( isLoading: boolean, - onJobStateChange: (job: SiemJob, latestTimestampMs: number, enable: boolean) => Promise + onJobStateChange: (job: SiemJob, latestTimestampMs: number, enable: boolean) => Promise, + basePath: string ) => [ { name: i18n.COLUMN_JOB_NAME, @@ -46,7 +47,7 @@ const getJobsTableColumns = ( {id} @@ -97,6 +98,7 @@ export interface JobTableProps { export const JobsTableComponent = ({ isLoading, jobs, onJobStateChange }: JobTableProps) => { const [pageIndex, setPageIndex] = useState(0); + const basePath = useBasePath(); const pageSize = 5; const pagination = { @@ -114,7 +116,7 @@ export const JobsTableComponent = ({ isLoading, jobs, onJobStateChange }: JobTab } diff --git a/x-pack/legacy/plugins/siem/public/components/ml_popover/popover_description.test.tsx b/x-pack/legacy/plugins/siem/public/components/ml_popover/popover_description.test.tsx index e611a5234da5ed..a179c0e266940d 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml_popover/popover_description.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/ml_popover/popover_description.test.tsx @@ -8,6 +8,8 @@ import { shallow } from 'enzyme'; import React from 'react'; import { PopoverDescriptionComponent } from './popover_description'; +jest.mock('../../lib/kibana'); + describe('JobsTableFilters', () => { test('renders correctly against snapshot', () => { const wrapper = shallow(); diff --git a/x-pack/legacy/plugins/siem/public/components/ml_popover/popover_description.tsx b/x-pack/legacy/plugins/siem/public/components/ml_popover/popover_description.tsx index c9cc1c5d4e5396..20e8dd2492fef3 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml_popover/popover_description.tsx +++ b/x-pack/legacy/plugins/siem/public/components/ml_popover/popover_description.tsx @@ -7,7 +7,8 @@ import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; import { EuiLink, EuiText } from '@elastic/eui'; -import chrome from 'ui/chrome'; + +import { useBasePath } from '../../lib/kibana'; export const PopoverDescriptionComponent = () => ( @@ -16,7 +17,7 @@ export const PopoverDescriptionComponent = () => ( defaultMessage="Run any of the Machine Learning jobs below to view anomalous events throughout the SIEM application. We’ve provided a few common detection jobs to get you started. If you wish to add your own custom jobs, simply create and tag them with “SIEM” from the {machineLearning} application for inclusion here." values={{ machineLearning: ( - + ( /> ); - PopoverDescriptionComponent.displayName = 'PopoverDescriptionComponent'; export const PopoverDescription = React.memo(PopoverDescriptionComponent); diff --git a/x-pack/legacy/plugins/siem/public/components/ml_popover/upgrade_contents.test.tsx b/x-pack/legacy/plugins/siem/public/components/ml_popover/upgrade_contents.test.tsx index 2ba08073b25b94..1d2d344c3b7af1 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml_popover/upgrade_contents.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/ml_popover/upgrade_contents.test.tsx @@ -8,6 +8,8 @@ import { shallow } from 'enzyme'; import React from 'react'; import { UpgradeContentsComponent } from './upgrade_contents'; +jest.mock('../../lib/kibana'); + describe('JobsTableFilters', () => { test('renders correctly against snapshot', () => { const wrapper = shallow(); diff --git a/x-pack/legacy/plugins/siem/public/components/ml_popover/upgrade_contents.tsx b/x-pack/legacy/plugins/siem/public/components/ml_popover/upgrade_contents.tsx index a337e234f11d3b..5a483e0d5b8760 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml_popover/upgrade_contents.tsx +++ b/x-pack/legacy/plugins/siem/public/components/ml_popover/upgrade_contents.tsx @@ -5,7 +5,6 @@ */ import React from 'react'; -import chrome from 'ui/chrome'; import styled from 'styled-components'; import { @@ -18,6 +17,7 @@ import { EuiFlexItem, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; +import { useBasePath } from '../../lib/kibana'; import * as i18n from './translations'; const PopoverContentsDiv = styled.div` @@ -59,7 +59,7 @@ export const UpgradeContentsComponent = () => ( diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/breadcrumbs/index.test.ts b/x-pack/legacy/plugins/siem/public/components/navigation/breadcrumbs/index.test.ts index fc0a37bc751c76..7770780fb9613e 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/breadcrumbs/index.test.ts +++ b/x-pack/legacy/plugins/siem/public/components/navigation/breadcrumbs/index.test.ts @@ -3,7 +3,6 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import chrome from 'ui/chrome'; import '../../../mock/match_media'; import { encodeIpv6 } from '../../../lib/helpers'; @@ -13,23 +12,11 @@ import { RouteSpyState, SiemRouteType } from '../../../utils/route/types'; import { TabNavigationProps } from '../tab_navigation/types'; import { NetworkRouteType } from '../../../pages/network/navigation/types'; -jest.mock('ui/chrome', () => ({ - getBasePath: () => { - return ''; - }, - breadcrumbs: { - set: jest.fn(), - }, - getUiSettingsClient: () => ({ - get: jest.fn(), - }), -})); - -jest.mock('../../search_bar', () => ({ - siemFilterManager: { - addFilters: jest.fn(), - }, -})); +const setBreadcrumbsMock = jest.fn(); +const chromeMock = { + setBreadcrumbs: setBreadcrumbsMock, + // eslint-disable-next-line @typescript-eslint/no-explicit-any +} as any; const mockDefaultTab = (pageName: string): SiemRouteType | undefined => { switch (pageName) { @@ -217,10 +204,11 @@ describe('Navigation Breadcrumbs', () => { ]); }); }); + describe('setBreadcrumbs()', () => { test('should call chrome breadcrumb service with correct breadcrumbs', () => { - setBreadcrumbs(getMockObject('hosts', '/hosts', hostName)); - expect(chrome.breadcrumbs.set).toBeCalledWith([ + setBreadcrumbs(getMockObject('hosts', '/hosts', hostName), chromeMock); + expect(setBreadcrumbsMock).toBeCalledWith([ { text: 'SIEM', href: '#/link-to/overview' }, { text: 'Hosts', diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/breadcrumbs/index.ts b/x-pack/legacy/plugins/siem/public/components/navigation/breadcrumbs/index.ts index 91055bca066c46..e8d5032fd75480 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/breadcrumbs/index.ts +++ b/x-pack/legacy/plugins/siem/public/components/navigation/breadcrumbs/index.ts @@ -4,10 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import chrome, { Breadcrumb } from 'ui/chrome'; - import { getOr, omit } from 'lodash/fp'; + +import { ChromeBreadcrumb } from '../../../../../../../../src/core/public'; import { APP_NAME } from '../../../../common/constants'; +import { StartServices } from '../../../plugin'; import { getBreadcrumbs as getHostDetailsBreadcrumbs } from '../../../pages/hosts/details/utils'; import { getBreadcrumbs as getIPDetailsBreadcrumbs } from '../../../pages/network/ip_details'; import { getBreadcrumbs as getDetectionRulesBreadcrumbs } from '../../../pages/detection_engine/rules/utils'; @@ -19,14 +20,17 @@ import { TabNavigationProps } from '../tab_navigation/types'; import { getSearch } from '../helpers'; import { SearchNavTab } from '../types'; -export const setBreadcrumbs = (spyState: RouteSpyState & TabNavigationProps) => { +export const setBreadcrumbs = ( + spyState: RouteSpyState & TabNavigationProps, + chrome: StartServices['chrome'] +) => { const breadcrumbs = getBreadcrumbsForRoute(spyState); if (breadcrumbs) { - chrome.breadcrumbs.set(breadcrumbs); + chrome.setBreadcrumbs(breadcrumbs); } }; -export const siemRootBreadcrumb: Breadcrumb[] = [ +export const siemRootBreadcrumb: ChromeBreadcrumb[] = [ { text: APP_NAME, href: getOverviewUrl(), @@ -44,7 +48,7 @@ const isDetectionsRoutes = (spyState: RouteSpyState) => export const getBreadcrumbsForRoute = ( object: RouteSpyState & TabNavigationProps -): Breadcrumb[] | null => { +): ChromeBreadcrumb[] | null => { const spyState: RouteSpyState = omit('navTabs', object); if (isHostsRoutes(spyState) && object.navTabs) { const tempNav: SearchNavTab = { urlKey: 'host', isDetailPage: false }; diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx index 56be39f67b1bdd..ac7a4a0ee52b75 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx @@ -61,79 +61,83 @@ describe('SIEM Navigation', () => { }; const wrapper = mount(); test('it calls setBreadcrumbs with correct path on mount', () => { - expect(setBreadcrumbs).toHaveBeenNthCalledWith(1, { - detailName: undefined, - navTabs: { - detections: { - disabled: false, - href: '#/link-to/detections', - id: 'detections', - name: 'Detections', - urlKey: 'detections', - }, - hosts: { - disabled: false, - href: '#/link-to/hosts', - id: 'hosts', - name: 'Hosts', - urlKey: 'host', - }, - network: { - disabled: false, - href: '#/link-to/network', - id: 'network', - name: 'Network', - urlKey: 'network', - }, - overview: { - disabled: false, - href: '#/link-to/overview', - id: 'overview', - name: 'Overview', - urlKey: 'overview', - }, - timelines: { - disabled: false, - href: '#/link-to/timelines', - id: 'timelines', - name: 'Timelines', - urlKey: 'timeline', - }, - }, - pageName: 'hosts', - pathName: '/hosts', - search: '', - tabName: 'authentications', - query: { query: '', language: 'kuery' }, - filters: [], - savedQuery: undefined, - timeline: { - id: '', - isOpen: false, - }, - timerange: { - global: { - linkTo: ['timeline'], - timerange: { - from: 1558048243696, - fromStr: 'now-24h', - kind: 'relative', - to: 1558134643697, - toStr: 'now', + expect(setBreadcrumbs).toHaveBeenNthCalledWith( + 1, + { + detailName: undefined, + navTabs: { + detections: { + disabled: false, + href: '#/link-to/detections', + id: 'detections', + name: 'Detections', + urlKey: 'detections', + }, + hosts: { + disabled: false, + href: '#/link-to/hosts', + id: 'hosts', + name: 'Hosts', + urlKey: 'host', + }, + network: { + disabled: false, + href: '#/link-to/network', + id: 'network', + name: 'Network', + urlKey: 'network', + }, + overview: { + disabled: false, + href: '#/link-to/overview', + id: 'overview', + name: 'Overview', + urlKey: 'overview', + }, + timelines: { + disabled: false, + href: '#/link-to/timelines', + id: 'timelines', + name: 'Timelines', + urlKey: 'timeline', }, }, + pageName: 'hosts', + pathName: '/hosts', + search: '', + tabName: 'authentications', + query: { query: '', language: 'kuery' }, + filters: [], + savedQuery: undefined, timeline: { - linkTo: ['global'], - timerange: { - from: 1558048243696, - fromStr: 'now-24h', - kind: 'relative', - to: 1558134643697, - toStr: 'now', + id: '', + isOpen: false, + }, + 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', + }, }, }, }, - }); + undefined + ); }); test('it calls setBreadcrumbs with correct path on update', () => { wrapper.setProps({ @@ -142,76 +146,80 @@ describe('SIEM Navigation', () => { tabName: undefined, }); wrapper.update(); - expect(setBreadcrumbs).toHaveBeenNthCalledWith(1, { - detailName: undefined, - filters: [], - navTabs: { - detections: { - disabled: false, - href: '#/link-to/detections', - id: 'detections', - name: 'Detections', - urlKey: 'detections', - }, - hosts: { - disabled: false, - href: '#/link-to/hosts', - id: 'hosts', - name: 'Hosts', - urlKey: 'host', - }, - network: { - disabled: false, - href: '#/link-to/network', - id: 'network', - name: 'Network', - urlKey: 'network', - }, - overview: { - disabled: false, - href: '#/link-to/overview', - id: 'overview', - name: 'Overview', - urlKey: 'overview', - }, - timelines: { - disabled: false, - href: '#/link-to/timelines', - id: 'timelines', - name: 'Timelines', - urlKey: 'timeline', - }, - }, - pageName: 'hosts', - pathName: '/hosts', - query: { language: 'kuery', query: '' }, - savedQuery: undefined, - search: '', - state: undefined, - tabName: 'authentications', - timeline: { id: '', isOpen: false }, - timerange: { - global: { - linkTo: ['timeline'], - timerange: { - from: 1558048243696, - fromStr: 'now-24h', - kind: 'relative', - to: 1558134643697, - toStr: 'now', + expect(setBreadcrumbs).toHaveBeenNthCalledWith( + 1, + { + detailName: undefined, + filters: [], + navTabs: { + detections: { + disabled: false, + href: '#/link-to/detections', + id: 'detections', + name: 'Detections', + urlKey: 'detections', + }, + hosts: { + disabled: false, + href: '#/link-to/hosts', + id: 'hosts', + name: 'Hosts', + urlKey: 'host', + }, + network: { + disabled: false, + href: '#/link-to/network', + id: 'network', + name: 'Network', + urlKey: 'network', + }, + overview: { + disabled: false, + href: '#/link-to/overview', + id: 'overview', + name: 'Overview', + urlKey: 'overview', + }, + timelines: { + disabled: false, + href: '#/link-to/timelines', + id: 'timelines', + name: 'Timelines', + urlKey: 'timeline', }, }, - timeline: { - linkTo: ['global'], - timerange: { - from: 1558048243696, - fromStr: 'now-24h', - kind: 'relative', - to: 1558134643697, - toStr: 'now', + pageName: 'hosts', + pathName: '/hosts', + query: { language: 'kuery', query: '' }, + savedQuery: undefined, + search: '', + state: undefined, + tabName: 'authentications', + timeline: { id: '', isOpen: false }, + 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', + }, }, }, }, - }); + undefined + ); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/index.tsx b/x-pack/legacy/plugins/siem/public/components/navigation/index.tsx index aac5ed37b64406..ad684303890298 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/navigation/index.tsx @@ -9,6 +9,7 @@ import { connect } from 'react-redux'; import { compose } from 'redux'; import deepEqual from 'fast-deep-equal/es6/react'; +import { useKibana } from '../../lib/kibana'; import { RouteSpyState } from '../../utils/route/types'; import { useRouteSpy } from '../../utils/route/use_route_spy'; import { makeMapStateToProps } from '../url_state/helpers'; @@ -30,25 +31,30 @@ export const SiemNavigationComponent: React.FC { + const { chrome } = useKibana().services; + useEffect(() => { if (pathName) { - setBreadcrumbs({ - query: urlState.query, - detailName, - filters: urlState.filters, - navTabs, - pageName, - pathName, - savedQuery: urlState.savedQuery, - search, - tabName, - flowTarget, - timerange: urlState.timerange, - timeline: urlState.timeline, - state, - }); + setBreadcrumbs( + { + query: urlState.query, + detailName, + filters: urlState.filters, + navTabs, + pageName, + pathName, + savedQuery: urlState.savedQuery, + search, + tabName, + flowTarget, + timerange: urlState.timerange, + timeline: urlState.timeline, + state, + }, + chrome + ); } - }, [pathName, search, navTabs, urlState, state]); + }, [chrome, pathName, search, navTabs, urlState, state]); return ( ({ if (onOpenTimeline != null) { onOpenTimeline(timeline); } else if (updateTimeline) { + const { from, to } = getTimeRangeSettings(); updateTimeline({ duplicate, - from: getOr(getDefaultFromValue(), 'dateRange.start', timeline), + from: getOr(from, 'dateRange.start', timeline), id: 'timeline-1', notes, timeline: { ...timeline, show: openTimeline, }, - to: getOr(getDefaultToValue(), 'dateRange.end', timeline), + to: getOr(to, 'dateRange.end', timeline), })(); } }) diff --git a/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/index.test.tsx index 345701c97901f6..54a516fef69ed4 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/index.test.tsx @@ -9,21 +9,22 @@ import React from 'react'; import { apolloClientObservable, mockGlobalState, TestProviders } from '../../../mock'; import { createStore, State } from '../../../store'; -import { siemFilterManager } from '../../search_bar'; import { AddFilterToGlobalSearchBar } from '.'; -import { esFilters } from '../../../../../../../../src/plugins/data/public'; -interface MockSiemFilterManager { - addFilters: (filters: esFilters.Filter[]) => void; -} -const mockSiemFilterManager: MockSiemFilterManager = siemFilterManager as MockSiemFilterManager; -jest.mock('../../search_bar', () => ({ - siemFilterManager: { - addFilters: jest.fn(), - }, -})); const mockAddFilters = jest.fn(); -mockSiemFilterManager.addFilters = mockAddFilters; +jest.mock('../../../lib/kibana', () => ({ + useKibana: () => ({ + services: { + data: { + query: { + filterManager: { + addFilters: mockAddFilters, + }, + }, + }, + }, + }), +})); describe('AddFilterToGlobalSearchBar Component', () => { const state: State = mockGlobalState; @@ -31,6 +32,7 @@ describe('AddFilterToGlobalSearchBar Component', () => { beforeEach(() => { store = createStore(state, apolloClientObservable); + mockAddFilters.mockClear(); }); test('Rendering', async () => { diff --git a/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/index.tsx index 012d8322fa3293..d6f1e2dd509bef 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/add_filter_to_global_search_bar/index.tsx @@ -5,12 +5,12 @@ */ import { EuiIcon, EuiPanel, EuiToolTip } from '@elastic/eui'; -import React from 'react'; +import React, { useCallback } from 'react'; import styled from 'styled-components'; -import { WithHoverActions } from '../../with_hover_actions'; -import { siemFilterManager } from '../../search_bar'; import { esFilters } from '../../../../../../../../src/plugins/data/public'; +import { WithHoverActions } from '../../with_hover_actions'; +import { useKibana } from '../../../lib/kibana'; import * as i18n from './translations'; @@ -24,12 +24,15 @@ interface OwnProps { export const AddFilterToGlobalSearchBar = React.memo( ({ children, filter, onFilterAdded }) => { - const addToKql = () => { - siemFilterManager.addFilters(filter); + const { filterManager } = useKibana().services.data.query; + + const addToKql = useCallback(() => { + filterManager.addFilters(filter); if (onFilterAdded != null) { onFilterAdded(); } - }; + }, [filter, filterManager, onFilterAdded]); + return ( ({ - siemFilterManager: { - addFilters: jest.fn(), - }, -})); - describe('Users Table Component', () => { const loadPage = jest.fn(); const state: State = mockGlobalState; diff --git a/x-pack/legacy/plugins/siem/public/components/recent_timelines/helpers.ts b/x-pack/legacy/plugins/siem/public/components/recent_timelines/helpers.ts index 61b49da01dc3aa..41fa90f1776e65 100644 --- a/x-pack/legacy/plugins/siem/public/components/recent_timelines/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/components/recent_timelines/helpers.ts @@ -5,22 +5,16 @@ */ import { throwIfNotOk } from '../../hooks/api/api'; +import { KibanaServices } from '../../lib/kibana'; import { MeApiResponse } from './recent_timelines'; -export const getMeApiUrl = (getBasePath: () => string): string => - `${getBasePath()}/internal/security/me`; - -export const fetchUsername = async (meApiUrl: string) => { - const response = await fetch(meApiUrl, { +export const fetchUsername = async () => { + const response = await KibanaServices.get().http.fetch('/internal/security/me', { method: 'GET', credentials: 'same-origin', - headers: { - 'content-type': 'application/json', - }, + asResponse: true, }); - await throwIfNotOk(response); - const apiResponse: MeApiResponse = await response.json(); - - return apiResponse.username; + await throwIfNotOk(response.response); + return response.body!.username; }; diff --git a/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx b/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx index f7acd2bef5e064..235f028c760500 100644 --- a/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx @@ -11,11 +11,10 @@ import { Dispatch } from 'redux'; import { Subscription } from 'rxjs'; import styled from 'styled-components'; import deepEqual from 'fast-deep-equal/es6/react'; -import { IIndexPattern } from 'src/plugins/data/public'; +import { FilterManager, IIndexPattern, TimeRange, Query, esFilters } from 'src/plugins/data/public'; import { SavedQuery } from 'src/legacy/core_plugins/data/public'; import { OnTimeChangeProps } from '@elastic/eui'; -import { npStart } from 'ui/new_platform'; import { inputsActions } from '../../store/inputs'; import { InputsRange } from '../../store/inputs/model'; @@ -34,11 +33,7 @@ import { toStrSelector, } from './selectors'; import { timelineActions, hostsActions, networkActions } from '../../store/actions'; -import { TimeRange, Query, esFilters } from '../../../../../../../src/plugins/data/public'; - -export const siemFilterManager = npStart.plugins.data.query.filterManager; -export const savedQueryService = npStart.plugins.data.query.savedQueries; -const { SearchBar } = npStart.plugins.data.ui; +import { useKibana } from '../../lib/kibana'; interface SiemSearchBarRedux { end: number; @@ -93,7 +88,12 @@ const SearchBarComponent = memo { - const { timefilter } = npStart.plugins.data.query.timefilter; + const { data } = useKibana().services; + const { + timefilter: { timefilter }, + filterManager, + } = data.query; + if (fromStr != null && toStr != null) { timefilter.setTime({ from: fromStr, to: toStr }); } else if (start != null && end != null) { @@ -114,6 +114,7 @@ const SearchBarComponent = memo q.refetch && (q.refetch as inputsModel.Refetch)()); } }, - [id, queries] + [id, queries, filterManager] ); const onSaved = useCallback( @@ -191,6 +193,7 @@ const SearchBarComponent = memo { let isSubscribed = true; const subscriptions = new Subscription(); subscriptions.add( - siemFilterManager.getUpdates$().subscribe({ + filterManager.getUpdates$().subscribe({ next: () => { if (isSubscribed) { setSearchBarFilter({ id, - filters: siemFilterManager.getFilters(), + filters: filterManager.getFilters(), }); } }, @@ -256,7 +260,7 @@ const SearchBarComponent = memo [indexPattern], [indexPattern]); return ( - void; -export const dispatchUpdateSearch = (dispatch: Dispatch) => ({ +const dispatchUpdateSearch = (dispatch: Dispatch) => ({ end, filters, id, @@ -335,6 +340,7 @@ export const dispatchUpdateSearch = (dispatch: Dispatch) => ({ savedQuery, start, timelineId, + filterManager, updateTime = false, }: UpdateReduxSearchBar): void => { if (updateTime) { @@ -379,7 +385,7 @@ export const dispatchUpdateSearch = (dispatch: Dispatch) => ({ ); } if (filters != null) { - siemFilterManager.setFilters(filters); + filterManager.setFilters(filters); } if (savedQuery != null || resetSavedQuery) { dispatch(inputsActions.setSavedQuery({ id, savedQuery })); diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/url_state/index.test.tsx index ab290c2f2fd67b..9a9bb409936fc8 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/url_state/index.test.tsx @@ -25,7 +25,6 @@ import { useUrlStateHooks } from './use_url_state'; let mockProps: UrlStateContainerPropTypes; -// const mockUseRouteSpy: jest.Mock = useRouteSpy as jest.Mock; const mockRouteSpy: RouteSpyState = { pageName: SiemPageName.network, detailName: undefined, @@ -37,10 +36,17 @@ jest.mock('../../utils/route/use_route_spy', () => ({ useRouteSpy: () => [mockRouteSpy], })); -jest.mock('../search_bar', () => ({ - siemFilterManager: { - setFilters: jest.fn(), - }, +jest.mock('../../lib/kibana', () => ({ + useKibana: () => ({ + services: { + data: { + query: { + filterManager: {}, + savedQueries: {}, + }, + }, + }, + }), })); describe('UrlStateContainer', () => { diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/index_mocked.test.tsx b/x-pack/legacy/plugins/siem/public/components/url_state/index_mocked.test.tsx index f673b77ea13c5c..6995bc8bf1d401 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/index_mocked.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/url_state/index_mocked.test.tsx @@ -15,14 +15,21 @@ import { getFilterQuery, getMockPropsObj, mockHistory, testCases } from './test_ import { UrlStateContainerPropTypes } from './types'; import { useUrlStateHooks } from './use_url_state'; -jest.mock('../search_bar', () => ({ - siemFilterManager: { - addFilters: jest.fn(), - }, -})); - let mockProps: UrlStateContainerPropTypes; +jest.mock('../../lib/kibana', () => ({ + useKibana: () => ({ + services: { + data: { + query: { + filterManager: {}, + savedQueries: {}, + }, + }, + }, + }), +})); + describe('UrlStateContainer - lodash.throttle mocked to test update url', () => { afterEach(() => { jest.clearAllMocks(); diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/initialize_redux_by_url.tsx b/x-pack/legacy/plugins/siem/public/components/url_state/initialize_redux_by_url.tsx index 013983c78a3a51..e182b879651f13 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/initialize_redux_by_url.tsx +++ b/x-pack/legacy/plugins/siem/public/components/url_state/initialize_redux_by_url.tsx @@ -6,7 +6,6 @@ import { get, isEmpty } from 'lodash/fp'; import { Dispatch } from 'redux'; -import { SavedQuery } from 'src/legacy/core_plugins/data/public'; import { Query, esFilters } from 'src/plugins/data/public'; import { inputsActions } from '../../store/actions'; @@ -17,7 +16,6 @@ import { AbsoluteTimeRange, RelativeTimeRange, } from '../../store/inputs/model'; -import { savedQueryService, siemFilterManager } from '../search_bar'; import { CONSTANTS } from './constants'; import { decodeRisonUrlState } from './helpers'; @@ -30,8 +28,10 @@ export const dispatchSetInitialStateFromUrl = ( ): DispatchSetInitialStateFromUrl => ({ apolloClient, detailName, + filterManager, indexPattern, pageName, + savedQueries, updateTimeline, updateTimelineIsLoading, urlStateToUpdate, @@ -125,14 +125,14 @@ export const dispatchSetInitialStateFromUrl = ( if (urlKey === CONSTANTS.filters) { const filters: esFilters.Filter[] = decodeRisonUrlState(newUrlStateString); - siemFilterManager.setFilters(filters || []); + filterManager.setFilters(filters || []); } if (urlKey === CONSTANTS.savedQuery) { const savedQueryId: string = decodeRisonUrlState(newUrlStateString); if (savedQueryId !== '') { - savedQueryService.getSavedQuery(savedQueryId).then((savedQueryData: SavedQuery) => { - siemFilterManager.setFilters(savedQueryData.attributes.filters || []); + savedQueries.getSavedQuery(savedQueryId).then(savedQueryData => { + filterManager.setFilters(savedQueryData.attributes.filters || []); dispatch( inputsActions.setFilterQuery({ id: 'global', diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/types.ts b/x-pack/legacy/plugins/siem/public/components/url_state/types.ts index be1ae1ad63bd4b..b5ad26330e6718 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/types.ts +++ b/x-pack/legacy/plugins/siem/public/components/url_state/types.ts @@ -6,7 +6,13 @@ import ApolloClient from 'apollo-client'; import { ActionCreator } from 'typescript-fsa'; -import { IIndexPattern, Query, esFilters } from 'src/plugins/data/public'; +import { + IIndexPattern, + Query, + esFilters, + FilterManager, + SavedQueryService, +} from 'src/plugins/data/public'; import { UrlInputsModel } from '../../store/inputs/model'; import { RouteSpyState } from '../../utils/route/types'; @@ -120,8 +126,10 @@ export interface UrlStateToRedux { export interface SetInitialStateFromUrl { apolloClient: ApolloClient | ApolloClient<{}> | undefined; detailName: string | undefined; + filterManager: FilterManager; indexPattern: IIndexPattern | undefined; pageName: string; + savedQueries: SavedQueryService; updateTimeline: DispatchUpdateTimeline; updateTimelineIsLoading: ActionCreator; urlStateToUpdate: UrlStateToRedux[]; diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/use_url_state.tsx b/x-pack/legacy/plugins/siem/public/components/url_state/use_url_state.tsx index a21880782f4d92..4b253dcfd7f896 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/use_url_state.tsx +++ b/x-pack/legacy/plugins/siem/public/components/url_state/use_url_state.tsx @@ -10,6 +10,7 @@ import { useEffect, useRef, useState } from 'react'; import { Query, esFilters } from 'src/plugins/data/public'; import deepEqual from 'fast-deep-equal/es6/react'; +import { useKibana } from '../../lib/kibana'; import { UrlInputsModel } from '../../store/inputs/model'; import { useApolloClient } from '../../utils/apollo_context'; @@ -57,6 +58,7 @@ export const useUrlStateHooks = ({ }: UrlStateContainerPropTypes) => { const [isInitializing, setIsInitializing] = useState(true); const apolloClient = useApolloClient(); + const { filterManager, savedQueries } = useKibana().services.data.query; const prevProps = usePrevious({ pathName, urlState }); const replaceStateInLocation = ( @@ -143,8 +145,10 @@ export const useUrlStateHooks = ({ setInitialStateFromUrl({ apolloClient, detailName, + filterManager, indexPattern, pageName, + savedQueries, updateTimeline, updateTimelineIsLoading, urlStateToUpdate, diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.ts b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.ts index 06fb0c6dc34800..da98944d5f0c97 100644 --- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.ts +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/api.ts @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { npStart } from 'ui/new_platform'; - import { AddRulesProps, DeleteRulesProps, @@ -24,6 +22,7 @@ import { ImportRulesResponse, PrePackagedRulesStatusResponse, } from './types'; +import { KibanaServices } from '../../../lib/kibana'; import { throwIfNotOk } from '../../../hooks/api/api'; import { DETECTION_ENGINE_RULES_URL, @@ -41,7 +40,7 @@ import * as i18n from '../../../pages/detection_engine/rules/translations'; * @param signal to cancel request */ export const addRule = async ({ rule, signal }: AddRulesProps): Promise => { - const response = await npStart.core.http.fetch(DETECTION_ENGINE_RULES_URL, { + const response = await KibanaServices.get().http.fetch(DETECTION_ENGINE_RULES_URL, { method: rule.id != null ? 'PUT' : 'POST', body: JSON.stringify(rule), asResponse: true, @@ -95,7 +94,7 @@ export const fetchRules = async ({ ...(filters.length ? { filter: filters.join(' AND ') } : {}), }; - const response = await npStart.core.http.fetch( + const response = await KibanaServices.get().http.fetch( `${DETECTION_ENGINE_RULES_URL}/_find`, { method: 'GET', @@ -117,7 +116,7 @@ export const fetchRules = async ({ * */ export const fetchRuleById = async ({ id, signal }: FetchRuleProps): Promise => { - const response = await npStart.core.http.fetch(DETECTION_ENGINE_RULES_URL, { + const response = await KibanaServices.get().http.fetch(DETECTION_ENGINE_RULES_URL, { method: 'GET', query: { id }, asResponse: true, @@ -137,7 +136,7 @@ export const fetchRuleById = async ({ id, signal }: FetchRuleProps): Promise => { - const response = await npStart.core.http.fetch( + const response = await KibanaServices.get().http.fetch( `${DETECTION_ENGINE_RULES_URL}/_bulk_update`, { method: 'PUT', @@ -158,7 +157,7 @@ export const enableRules = async ({ ids, enabled }: EnableRulesProps): Promise> => { - const response = await npStart.core.http.fetch( + const response = await KibanaServices.get().http.fetch( `${DETECTION_ENGINE_RULES_URL}/_bulk_delete`, { method: 'PUT', @@ -177,7 +176,7 @@ export const deleteRules = async ({ ids }: DeleteRulesProps): Promise => { - const response = await npStart.core.http.fetch( + const response = await KibanaServices.get().http.fetch( `${DETECTION_ENGINE_RULES_URL}/_bulk_create`, { method: 'POST', @@ -215,11 +214,14 @@ export const duplicateRules = async ({ rules }: DuplicateRulesProps): Promise => { - const response = await npStart.core.http.fetch(DETECTION_ENGINE_PREPACKAGED_URL, { - method: 'PUT', - signal, - asResponse: true, - }); + const response = await KibanaServices.get().http.fetch( + DETECTION_ENGINE_PREPACKAGED_URL, + { + method: 'PUT', + signal, + asResponse: true, + } + ); await throwIfNotOk(response.response); return true; @@ -242,7 +244,7 @@ export const importRules = async ({ const formData = new FormData(); formData.append('file', fileToImport); - const response = await npStart.core.http.fetch( + const response = await KibanaServices.get().http.fetch( `${DETECTION_ENGINE_RULES_URL}/_import`, { method: 'POST', @@ -279,16 +281,19 @@ export const exportRules = async ({ ? JSON.stringify({ objects: ruleIds.map(rule => ({ rule_id: rule })) }) : undefined; - const response = await npStart.core.http.fetch(`${DETECTION_ENGINE_RULES_URL}/_export`, { - method: 'POST', - body, - query: { - exclude_export_details: excludeExportDetails, - file_name: filename, - }, - signal, - asResponse: true, - }); + const response = await KibanaServices.get().http.fetch( + `${DETECTION_ENGINE_RULES_URL}/_export`, + { + method: 'POST', + body, + query: { + exclude_export_details: excludeExportDetails, + file_name: filename, + }, + signal, + asResponse: true, + } + ); await throwIfNotOk(response.response); return response.body!; @@ -309,7 +314,7 @@ export const getRuleStatusById = async ({ id: string; signal: AbortSignal; }): Promise => { - const response = await npStart.core.http.fetch( + const response = await KibanaServices.get().http.fetch( DETECTION_ENGINE_RULES_STATUS_URL, { method: 'GET', @@ -330,7 +335,7 @@ export const getRuleStatusById = async ({ * */ export const fetchTags = async ({ signal }: { signal: AbortSignal }): Promise => { - const response = await npStart.core.http.fetch(DETECTION_ENGINE_TAGS_URL, { + const response = await KibanaServices.get().http.fetch(DETECTION_ENGINE_TAGS_URL, { method: 'GET', signal, asResponse: true, @@ -352,7 +357,7 @@ export const getPrePackagedRulesStatus = async ({ }: { signal: AbortSignal; }): Promise => { - const response = await npStart.core.http.fetch( + const response = await KibanaServices.get().http.fetch( DETECTION_ENGINE_PREPACKAGED_RULES_STATUS_URL, { method: 'GET', diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/api.ts b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/api.ts index d0da70e6461248..085bde3e54ef14 100644 --- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/api.ts +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/api.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { npStart } from 'ui/new_platform'; - +import { KibanaServices } from '../../../lib/kibana'; import { throwIfNotOk } from '../../../hooks/api/api'; import { DETECTION_ENGINE_QUERY_SIGNALS_URL, @@ -33,7 +32,7 @@ export const fetchQuerySignals = async ({ query, signal, }: QuerySignals): Promise> => { - const response = await npStart.core.http.fetch>( + const response = await KibanaServices.get().http.fetch>( DETECTION_ENGINE_QUERY_SIGNALS_URL, { method: 'POST', @@ -59,7 +58,7 @@ export const updateSignalStatus = async ({ status, signal, }: UpdateSignalStatusProps): Promise => { - const response = await npStart.core.http.fetch(DETECTION_ENGINE_SIGNALS_STATUS_URL, { + const response = await KibanaServices.get().http.fetch(DETECTION_ENGINE_SIGNALS_STATUS_URL, { method: 'POST', body: JSON.stringify({ status, ...query }), asResponse: true, @@ -77,7 +76,7 @@ export const updateSignalStatus = async ({ */ export const getSignalIndex = async ({ signal }: BasicSignals): Promise => { try { - return await npStart.core.http.fetch(DETECTION_ENGINE_INDEX_URL, { + return await KibanaServices.get().http.fetch(DETECTION_ENGINE_INDEX_URL, { method: 'GET', signal, }); @@ -95,11 +94,14 @@ export const getSignalIndex = async ({ signal }: BasicSignals): Promise => { - const response = await npStart.core.http.fetch(DETECTION_ENGINE_PRIVILEGES_URL, { - method: 'GET', - signal, - asResponse: true, - }); + const response = await KibanaServices.get().http.fetch( + DETECTION_ENGINE_PRIVILEGES_URL, + { + method: 'GET', + signal, + asResponse: true, + } + ); await throwIfNotOk(response.response); return response.body!; @@ -112,7 +114,7 @@ export const getUserPrivilege = async ({ signal }: BasicSignals): Promise => { try { - return await npStart.core.http.fetch(DETECTION_ENGINE_INDEX_URL, { + return await KibanaServices.get().http.fetch(DETECTION_ENGINE_INDEX_URL, { method: 'POST', signal, }); diff --git a/x-pack/legacy/plugins/siem/public/hooks/api/api.tsx b/x-pack/legacy/plugins/siem/public/hooks/api/api.tsx index f5f32da7d8c0b2..e29e2ed193f946 100644 --- a/x-pack/legacy/plugins/siem/public/hooks/api/api.tsx +++ b/x-pack/legacy/plugins/siem/public/hooks/api/api.tsx @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { npStart } from 'ui/new_platform'; import * as i18n from '../translations'; +import { StartServices } from '../../plugin'; import { parseJsonFromBody, ToasterErrors } from '../../components/ml/api/throw_if_not_ok'; import { IndexPatternSavedObject, IndexPatternSavedObjectAttributes } from '../types'; @@ -14,8 +14,10 @@ import { IndexPatternSavedObject, IndexPatternSavedObjectAttributes } from '../t * * TODO: Refactor to context provider: https://github.com/elastic/siem-team/issues/448 */ -export const getIndexPatterns = async (): Promise => { - const response = await npStart.core.savedObjects.client.find({ +export const getIndexPatterns = async ( + savedObjects: StartServices['savedObjects'] +): Promise => { + const response = await savedObjects.client.find({ type: 'index-pattern', fields: ['title'], perPage: 10000, diff --git a/x-pack/legacy/plugins/siem/public/hooks/index.ts b/x-pack/legacy/plugins/siem/public/hooks/index.ts deleted file mode 100644 index 5049e4587d3837..00000000000000 --- a/x-pack/legacy/plugins/siem/public/hooks/index.ts +++ /dev/null @@ -1,7 +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 { useDateFormat, useTimeZone } from './use_ui_settings'; diff --git a/x-pack/legacy/plugins/siem/public/hooks/use_index_patterns.tsx b/x-pack/legacy/plugins/siem/public/hooks/use_index_patterns.tsx index 35bed69e8617eb..e10d4873f1b6e9 100644 --- a/x-pack/legacy/plugins/siem/public/hooks/use_index_patterns.tsx +++ b/x-pack/legacy/plugins/siem/public/hooks/use_index_patterns.tsx @@ -6,6 +6,7 @@ import { useEffect, useState } from 'react'; +import { useKibana } from '../lib/kibana'; import { useStateToaster } from '../components/toasters'; import { errorToToaster } from '../components/ml/api/error_to_toaster'; @@ -19,6 +20,7 @@ export const useIndexPatterns = (refreshToggle = false): Return => { const [indexPatterns, setIndexPatterns] = useState([]); const [isLoading, setIsLoading] = useState(true); const [, dispatchToaster] = useStateToaster(); + const { savedObjects } = useKibana().services; useEffect(() => { let isSubscribed = true; @@ -26,7 +28,7 @@ export const useIndexPatterns = (refreshToggle = false): Return => { async function fetchIndexPatterns() { try { - const data = await getIndexPatterns(); + const data = await getIndexPatterns(savedObjects); if (isSubscribed) { setIndexPatterns(data); diff --git a/x-pack/legacy/plugins/siem/public/lib/compose/helpers.test.ts b/x-pack/legacy/plugins/siem/public/lib/compose/helpers.test.ts index b135e299e2e432..af4521b4f6e2c6 100644 --- a/x-pack/legacy/plugins/siem/public/lib/compose/helpers.test.ts +++ b/x-pack/legacy/plugins/siem/public/lib/compose/helpers.test.ts @@ -21,7 +21,7 @@ const mockHttpLink = { mockHttpLink: 'mockHttpLink' }; // @ts-ignore withClientState.mockReturnValue(mockWithClientState); // @ts-ignore -apolloLinkHttp.HttpLink.mockImplementation(() => mockHttpLink); +apolloLinkHttp.createHttpLink.mockImplementation(() => mockHttpLink); describe('getLinks helper', () => { test('It should return links in correct order', () => { @@ -31,7 +31,7 @@ describe('getLinks helper', () => { introspectionQueryResultData, }), }); - const links = getLinks(mockCache); + const links = getLinks(mockCache, 'basePath'); expect(links[0]).toEqual(errorLink); expect(links[1]).toEqual(reTryOneTimeOnErrorLink); expect(links[2]).toEqual(mockWithClientState); diff --git a/x-pack/legacy/plugins/siem/public/lib/compose/helpers.ts b/x-pack/legacy/plugins/siem/public/lib/compose/helpers.ts index 9cdd4148134ad2..b698fc55cc5e5e 100644 --- a/x-pack/legacy/plugins/siem/public/lib/compose/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/lib/compose/helpers.ts @@ -4,23 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ -import { HttpLink } from 'apollo-link-http'; +import { createHttpLink } from 'apollo-link-http'; import { withClientState } from 'apollo-link-state'; import { InMemoryCache } from 'apollo-cache-inmemory'; -import chrome from 'ui/chrome'; import { errorLink, reTryOneTimeOnErrorLink } from '../../containers/errors'; -export const getLinks = (cache: InMemoryCache) => [ +export const getLinks = (cache: InMemoryCache, basePath: string) => [ errorLink, reTryOneTimeOnErrorLink, withClientState({ cache, resolvers: {}, }), - new HttpLink({ + createHttpLink({ credentials: 'same-origin', headers: { 'kbn-xsrf': 'true' }, - uri: `${chrome.getBasePath()}/api/siem/graphql`, + uri: `${basePath}/api/siem/graphql`, }), ]; diff --git a/x-pack/legacy/plugins/siem/public/lib/compose/kibana_compose.tsx b/x-pack/legacy/plugins/siem/public/lib/compose/kibana_compose.tsx index 00dfbcd8a4d8f0..c742ced4c504c7 100644 --- a/x-pack/legacy/plugins/siem/public/lib/compose/kibana_compose.tsx +++ b/x-pack/legacy/plugins/siem/public/lib/compose/kibana_compose.tsx @@ -7,29 +7,26 @@ import { InMemoryCache, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory'; import ApolloClient from 'apollo-client'; import { ApolloLink } from 'apollo-link'; -import 'ui/autoload/all'; -// @ts-ignore: path dynamic for kibana -import { uiModules } from 'ui/modules'; import introspectionQueryResultData from '../../graphql/introspection.json'; +import { CoreStart } from '../../plugin'; import { AppFrontendLibs } from '../lib'; import { getLinks } from './helpers'; -export function compose(): AppFrontendLibs { +export function compose(core: CoreStart): AppFrontendLibs { const cache = new InMemoryCache({ dataIdFromObject: () => null, fragmentMatcher: new IntrospectionFragmentMatcher({ introspectionQueryResultData, }), }); + const basePath = core.http.basePath.get(); - const graphQLOptions = { + const apolloClient = new ApolloClient({ connectToDevTools: process.env.NODE_ENV !== 'production', cache, - link: ApolloLink.from(getLinks(cache)), - }; - - const apolloClient = new ApolloClient(graphQLOptions); + link: ApolloLink.from(getLinks(cache, basePath)), + }); const libs: AppFrontendLibs = { apolloClient, diff --git a/x-pack/legacy/plugins/siem/public/lib/kibana/__mocks__/index.ts b/x-pack/legacy/plugins/siem/public/lib/kibana/__mocks__/index.ts index 93fd37c4d14cbd..227680d79912f4 100644 --- a/x-pack/legacy/plugins/siem/public/lib/kibana/__mocks__/index.ts +++ b/x-pack/legacy/plugins/siem/public/lib/kibana/__mocks__/index.ts @@ -12,8 +12,12 @@ import { createWithKibanaMock, } from '../../../mock/kibana_react'; +export const KibanaServices = { get: jest.fn() }; export const useKibana = jest.fn(createUseKibanaMock()); export const useUiSetting = jest.fn(createUseUiSettingMock()); export const useUiSetting$ = jest.fn(createUseUiSetting$Mock()); +export const useTimeZone = jest.fn(); +export const useDateFormat = jest.fn(); +export const useBasePath = jest.fn(() => '/test/base/path'); export const withKibana = jest.fn(createWithKibanaMock()); export const KibanaContextProvider = jest.fn(createKibanaContextProviderMock()); diff --git a/x-pack/legacy/plugins/siem/public/hooks/use_ui_settings.ts b/x-pack/legacy/plugins/siem/public/lib/kibana/hooks.ts similarity index 78% rename from x-pack/legacy/plugins/siem/public/hooks/use_ui_settings.ts rename to x-pack/legacy/plugins/siem/public/lib/kibana/hooks.ts index 7eb0242e8e1160..a4a70c77833c05 100644 --- a/x-pack/legacy/plugins/siem/public/hooks/use_ui_settings.ts +++ b/x-pack/legacy/plugins/siem/public/lib/kibana/hooks.ts @@ -6,8 +6,8 @@ import moment from 'moment-timezone'; -import { DEFAULT_DATE_FORMAT, DEFAULT_DATE_FORMAT_TZ } from '../../common/constants'; -import { useUiSetting } from '../lib/kibana'; +import { DEFAULT_DATE_FORMAT, DEFAULT_DATE_FORMAT_TZ } from '../../../common/constants'; +import { useUiSetting, useKibana } from './kibana_react'; export const useDateFormat = (): string => useUiSetting(DEFAULT_DATE_FORMAT); @@ -15,3 +15,5 @@ export const useTimeZone = (): string => { const timeZone = useUiSetting(DEFAULT_DATE_FORMAT_TZ); return timeZone === 'Browser' ? moment.tz.guess() : timeZone; }; + +export const useBasePath = (): string => useKibana().services.http.basePath.get(); diff --git a/x-pack/legacy/plugins/siem/public/lib/kibana/index.ts b/x-pack/legacy/plugins/siem/public/lib/kibana/index.ts index 012a1cfef5da2d..fd201d51e73a61 100644 --- a/x-pack/legacy/plugins/siem/public/lib/kibana/index.ts +++ b/x-pack/legacy/plugins/siem/public/lib/kibana/index.ts @@ -4,28 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - KibanaContextProvider, - KibanaReactContextValue, - useKibana, - useUiSetting, - useUiSetting$, - withKibana, -} from '../../../../../../../src/plugins/kibana_react/public'; -import { StartServices } from '../../plugin'; - -export type KibanaContext = KibanaReactContextValue; -export interface WithKibanaProps { - kibana: KibanaContext; -} - -// eslint-disable-next-line react-hooks/rules-of-hooks -const typedUseKibana = () => useKibana(); - -export { - KibanaContextProvider, - typedUseKibana as useKibana, - useUiSetting, - useUiSetting$, - withKibana, -}; +export * from './hooks'; +export * from './kibana_react'; +export * from './services'; diff --git a/x-pack/legacy/plugins/siem/public/lib/kibana/kibana_react.ts b/x-pack/legacy/plugins/siem/public/lib/kibana/kibana_react.ts new file mode 100644 index 00000000000000..012a1cfef5da2d --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/lib/kibana/kibana_react.ts @@ -0,0 +1,31 @@ +/* + * 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 { + KibanaContextProvider, + KibanaReactContextValue, + useKibana, + useUiSetting, + useUiSetting$, + withKibana, +} from '../../../../../../../src/plugins/kibana_react/public'; +import { StartServices } from '../../plugin'; + +export type KibanaContext = KibanaReactContextValue; +export interface WithKibanaProps { + kibana: KibanaContext; +} + +// eslint-disable-next-line react-hooks/rules-of-hooks +const typedUseKibana = () => useKibana(); + +export { + KibanaContextProvider, + typedUseKibana as useKibana, + useUiSetting, + useUiSetting$, + withKibana, +}; diff --git a/x-pack/legacy/plugins/siem/public/lib/kibana/services.ts b/x-pack/legacy/plugins/siem/public/lib/kibana/services.ts new file mode 100644 index 00000000000000..3a6a3f13dc5cec --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/lib/kibana/services.ts @@ -0,0 +1,27 @@ +/* + * 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 { StartServices } from '../../plugin'; + +type GlobalServices = Pick; + +export class KibanaServices { + private static services?: GlobalServices; + + public static init({ http, uiSettings }: StartServices) { + this.services = { http, uiSettings }; + } + + public static get(): GlobalServices { + if (!this.services) { + throw new Error( + 'Kibana services not set - are you trying to import this module from outside of the SIEM app?' + ); + } + + return this.services; + } +} diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine_empty_page.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine_empty_page.tsx index bf7a2109fd3b5d..7516bb13a9e750 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine_empty_page.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine_empty_page.tsx @@ -5,19 +5,16 @@ */ import React from 'react'; -import chrome from 'ui/chrome'; import { useKibana } from '../../lib/kibana'; import { EmptyPage } from '../../components/empty_page'; import * as i18n from '../common/translations'; -const basePath = chrome.getBasePath(); - export const DetectionEngineEmptyPage = React.memo(() => ( const isRuleEditPage = (pathname: string) => pathname.includes('/rules') && pathname.includes('/edit'); -export const getBreadcrumbs = (params: RouteSpyState, search: string[]): Breadcrumb[] => { +export const getBreadcrumbs = (params: RouteSpyState, search: string[]): ChromeBreadcrumb[] => { let breadcrumb = [ { text: i18nDetections.PAGE_TITLE, diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/details/utils.ts b/x-pack/legacy/plugins/siem/public/pages/hosts/details/utils.ts index c321478f101741..c4680dc3d795ef 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/details/utils.ts +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/details/utils.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Breadcrumb } from 'ui/chrome'; import { get, isEmpty } from 'lodash/fp'; +import { ChromeBreadcrumb } from '../../../../../../../../src/core/public'; import { hostsModel } from '../../../store'; import { HostsTableType } from '../../../store/hosts/model'; import { getHostsUrl, getHostDetailsUrl } from '../../../components/link_to/redirect_to_hosts'; @@ -25,7 +25,7 @@ const TabNameMappedToI18nKey: Record = { [HostsTableType.alerts]: i18n.NAVIGATION_ALERTS_TITLE, }; -export const getBreadcrumbs = (params: HostRouteSpyState, search: string[]): Breadcrumb[] => { +export const getBreadcrumbs = (params: HostRouteSpyState, search: string[]): ChromeBreadcrumb[] => { let breadcrumb = [ { text: i18n.PAGE_TITLE, diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts_empty_page.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts_empty_page.tsx index c29e532b3d5132..bded0b90e187be 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts_empty_page.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts_empty_page.tsx @@ -5,17 +5,15 @@ */ import React from 'react'; -import chrome from 'ui/chrome'; import { EmptyPage } from '../../components/empty_page'; import { useKibana } from '../../lib/kibana'; import * as i18n from '../common/translations'; -const basePath = chrome.getBasePath(); - export const HostsEmptyPage = React.memo(() => { - const docLinks = useKibana().services.docLinks; + const { http, docLinks } = useKibana().services; + const basePath = http.basePath.get(); return ( = { [NetworkRouteType.tls]: i18n.NAVIGATION_TLS_TITLE, }; -export const getBreadcrumbs = (params: NetworkRouteSpyState, search: string[]): Breadcrumb[] => { +export const getBreadcrumbs = ( + params: NetworkRouteSpyState, + search: string[] +): ChromeBreadcrumb[] => { let breadcrumb = [ { text: i18n.PAGE_TITLE, diff --git a/x-pack/legacy/plugins/siem/public/pages/network/network_empty_page.tsx b/x-pack/legacy/plugins/siem/public/pages/network/network_empty_page.tsx index 78a3ae147fd0fc..22db00400bf8a2 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/network_empty_page.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/network/network_empty_page.tsx @@ -5,16 +5,14 @@ */ import React from 'react'; -import chrome from 'ui/chrome'; import { useKibana } from '../../lib/kibana'; import { EmptyPage } from '../../components/empty_page'; import * as i18n from '../common/translations'; -const basePath = chrome.getBasePath(); - export const NetworkEmptyPage = React.memo(() => { - const docLinks = useKibana().services.docLinks; + const { http, docLinks } = useKibana().services; + const basePath = http.basePath.get(); return ( ({ - getBasePath: () => { - return ''; - }, getKibanaVersion: () => { return 'v8.0.0'; }, breadcrumbs: { set: jest.fn(), }, - getUiSettingsClient: () => ({ - get: jest.fn(), - }), })); // Test will fail because we will to need to mock some core services to make the test work diff --git a/x-pack/legacy/plugins/siem/public/pages/overview/overview_empty/index.tsx b/x-pack/legacy/plugins/siem/public/pages/overview/overview_empty/index.tsx index 9565b764b09e77..1325826f172c71 100644 --- a/x-pack/legacy/plugins/siem/public/pages/overview/overview_empty/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/overview/overview_empty/index.tsx @@ -5,16 +5,14 @@ */ import React from 'react'; -import chrome from 'ui/chrome'; import * as i18nCommon from '../../common/translations'; import { EmptyPage } from '../../../components/empty_page'; import { useKibana } from '../../../lib/kibana'; -const basePath = chrome.getBasePath(); - const OverviewEmptyComponent: React.FC = () => { - const docLinks = useKibana().services.docLinks; + const { http, docLinks } = useKibana().services; + const basePath = http.basePath.get(); return ( { id: this.id, title: this.name, async mount(context, params) { - const [coreStart, pluginsStart] = await core.getStartServices(); + const [coreStart, startPlugins] = await core.getStartServices(); const { renderApp } = await import('./app'); - return renderApp(coreStart, pluginsStart as StartPlugins, params); + return renderApp(coreStart, startPlugins as StartPlugins, params); }, }); @@ -66,6 +67,8 @@ export class Plugin implements IPlugin { } public start(core: CoreStart, plugins: StartPlugins) { + KibanaServices.init({ ...core, ...plugins }); + return {}; } diff --git a/x-pack/legacy/plugins/siem/public/store/inputs/reducer.ts b/x-pack/legacy/plugins/siem/public/store/inputs/reducer.ts index 8f9f8809a1e864..b64d7cea47e0ff 100644 --- a/x-pack/legacy/plugins/siem/public/store/inputs/reducer.ts +++ b/x-pack/legacy/plugins/siem/public/store/inputs/reducer.ts @@ -7,6 +7,7 @@ import { get } from 'lodash/fp'; import { reducerWithInitialState } from 'typescript-fsa-reducers'; +import { getIntervalSettings, getTimeRangeSettings } from '../../utils/default_date_settings'; import { deleteAllQuery, setAbsoluteRangeDatePicker, @@ -39,14 +40,6 @@ import { deleteOneQuery as helperDeleteOneQuery, } from './helpers'; import { InputsModel, TimeRange } from './model'; -import { - getDefaultFromValue, - getDefaultToValue, - getDefaultFromString, - getDefaultToString, - getDefaultIntervalKind, - getDefaultIntervalDuration, -} from '../../utils/default_date_settings'; export type InputsState = InputsModel; @@ -54,16 +47,10 @@ export const initialInputsState: InputsState = { global: { timerange: { kind: 'relative', - fromStr: getDefaultFromString(), - toStr: getDefaultToString(), - from: getDefaultFromValue(), - to: getDefaultToValue(), + ...getTimeRangeSettings(false), }, queries: [], - policy: { - kind: getDefaultIntervalKind(), - duration: getDefaultIntervalDuration(), - }, + policy: getIntervalSettings(false), linkTo: ['timeline'], query: { query: '', @@ -74,16 +61,10 @@ export const initialInputsState: InputsState = { timeline: { timerange: { kind: 'relative', - fromStr: getDefaultFromString(), - toStr: getDefaultToString(), - from: getDefaultFromValue(), - to: getDefaultToValue(), + ...getTimeRangeSettings(false), }, queries: [], - policy: { - kind: getDefaultIntervalKind(), - duration: getDefaultIntervalDuration(), - }, + policy: getIntervalSettings(false), linkTo: ['global'], query: { query: '', @@ -93,6 +74,54 @@ export const initialInputsState: InputsState = { }, }; +export const createInitialInputsState = (): InputsState => { + const { from, fromStr, to, toStr } = getTimeRangeSettings(); + const { kind, duration } = getIntervalSettings(); + + return { + global: { + timerange: { + kind: 'relative', + fromStr, + toStr, + from, + to, + }, + queries: [], + policy: { + kind, + duration, + }, + linkTo: ['timeline'], + query: { + query: '', + language: 'kuery', + }, + filters: [], + }, + timeline: { + timerange: { + kind: 'relative', + fromStr, + toStr, + from, + to, + }, + queries: [], + policy: { + kind, + duration, + }, + linkTo: ['global'], + query: { + query: '', + language: 'kuery', + }, + filters: [], + }, + }; +}; + export const inputsReducer = reducerWithInitialState(initialInputsState) .case(setTimelineRangeDatePicker, (state, { from, to }) => { return { diff --git a/x-pack/legacy/plugins/siem/public/store/reducer.ts b/x-pack/legacy/plugins/siem/public/store/reducer.ts index 3c93dfa61b7683..32554653febd55 100644 --- a/x-pack/legacy/plugins/siem/public/store/reducer.ts +++ b/x-pack/legacy/plugins/siem/public/store/reducer.ts @@ -9,7 +9,7 @@ import { combineReducers } from 'redux'; import { appReducer, AppState, initialAppState } from './app'; import { dragAndDropReducer, DragAndDropState, initialDragAndDropState } from './drag_and_drop'; import { hostsReducer, HostsState, initialHostsState } from './hosts'; -import { initialInputsState, inputsReducer, InputsState } from './inputs'; +import { createInitialInputsState, initialInputsState, inputsReducer, InputsState } from './inputs'; import { initialNetworkState, networkReducer, NetworkState } from './network'; import { initialTimelineState, timelineReducer } from './timeline/reducer'; import { TimelineState } from './timeline/types'; @@ -32,6 +32,11 @@ export const initialState: State = { timeline: initialTimelineState, }; +export const createInitialState = (): State => ({ + ...initialState, + inputs: createInitialInputsState(), +}); + export const reducer = combineReducers({ app: appReducer, dragAndDrop: dragAndDropReducer, diff --git a/x-pack/legacy/plugins/siem/public/utils/default_date_settings.test.ts b/x-pack/legacy/plugins/siem/public/utils/default_date_settings.test.ts index 2507c2cf81c518..9dc179ba7a6e2d 100644 --- a/x-pack/legacy/plugins/siem/public/utils/default_date_settings.test.ts +++ b/x-pack/legacy/plugins/siem/public/utils/default_date_settings.test.ts @@ -4,19 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import chrome from 'ui/chrome'; +import moment from 'moment'; + import { - getDefaultFromString, + getTimeRangeSettings, + getIntervalSettings, DefaultTimeRangeSetting, - getDefaultToString, - getDefaultFromValue, - getDefaultToValue, - getDefaultFromMoment, - getDefaultToMoment, DefaultIntervalSetting, - getDefaultIntervalKind, - getDefaultIntervalDuration, - parseDateString, parseDateWithDefault, } from './default_date_settings'; import { @@ -28,8 +22,8 @@ import { DEFAULT_INTERVAL_VALUE, DEFAULT_INTERVAL_TYPE, } from '../../common/constants'; +import { KibanaServices } from '../lib/kibana'; import { Policy } from '../store/inputs/model'; -import moment from 'moment'; // Change the constants to be static values so we can test against those instead of // relative sliding date times. Jest cannot access these outer scoped variables so @@ -46,6 +40,9 @@ jest.mock('../../common/constants', () => ({ DEFAULT_SIEM_TIME_RANGE: 'siem:timeDefaults', })); +jest.mock('../lib/kibana'); +const mockGetServices = KibanaServices.get as jest.Mock; + /** * We utilize the internal chrome mocking that is built in to be able to mock different time range * scenarios here or the absence of a time range setting. @@ -59,16 +56,20 @@ const mockTimeRange = ( value: DEFAULT_INTERVAL_VALUE, } ) => { - chrome.getUiSettingsClient().get.mockImplementation((key: string) => { - switch (key) { - case DEFAULT_SIEM_TIME_RANGE: - return timeRange; - case DEFAULT_SIEM_REFRESH_INTERVAL: - return interval; - default: - throw new Error(`Unexpected config key: ${key}`); - } - }); + mockGetServices.mockImplementation(() => ({ + uiSettings: { + get: (key: string) => { + switch (key) { + case DEFAULT_SIEM_TIME_RANGE: + return timeRange; + case DEFAULT_SIEM_REFRESH_INTERVAL: + return interval; + default: + throw new Error(`Unexpected config key: ${key}`); + } + }, + }, + })); }; /** @@ -87,445 +88,392 @@ const isMalformedTimeRange = (timeRange: unknown): timeRange is DefaultTimeRange const isMalformedInterval = (interval: unknown): interval is DefaultIntervalSetting => typeof interval === 'object'; -describe('default_date_settings', () => { - beforeEach(() => { - chrome.getUiSettingsClient().get.mockClear(); - }); - - describe('#getDefaultFromString', () => { +describe('getTimeRangeSettings', () => { + describe('fromStr', () => { test('should return the DEFAULT_FROM constant by default', () => { mockTimeRange(); - const stringDefault = getDefaultFromString(); - expect(stringDefault).toBe(DEFAULT_FROM); + const { fromStr } = getTimeRangeSettings(); + expect(fromStr).toBe(DEFAULT_FROM); }); test('should return a custom from range', () => { mockTimeRange({ from: 'now-15m' }); - const stringDefault = getDefaultFromString(); - expect(stringDefault).toBe('now-15m'); + const { fromStr } = getTimeRangeSettings(); + expect(fromStr).toBe('now-15m'); }); test('should return the DEFAULT_FROM when the whole object is null', () => { mockTimeRange(null); - const stringDefault = getDefaultFromString(); - expect(stringDefault).toBe(DEFAULT_FROM); + const { fromStr } = getTimeRangeSettings(); + expect(fromStr).toBe(DEFAULT_FROM); }); test('should return the DEFAULT_FROM when the whole object is undefined', () => { mockTimeRange(null); - const stringDefault = getDefaultFromString(); - expect(stringDefault).toBe(DEFAULT_FROM); + const { fromStr } = getTimeRangeSettings(); + expect(fromStr).toBe(DEFAULT_FROM); }); test('should return the DEFAULT_FROM when the from value is null', () => { mockTimeRange({ from: null }); - const stringDefault = getDefaultFromString(); - expect(stringDefault).toBe(DEFAULT_FROM); + const { fromStr } = getTimeRangeSettings(); + expect(fromStr).toBe(DEFAULT_FROM); }); test('should return the DEFAULT_FROM when the from value is undefined', () => { mockTimeRange({ from: undefined }); - const stringDefault = getDefaultFromString(); - expect(stringDefault).toBe(DEFAULT_FROM); + const { fromStr } = getTimeRangeSettings(); + expect(fromStr).toBe(DEFAULT_FROM); }); test('should return the DEFAULT_FROM when the from value is malformed', () => { const malformedTimeRange = { from: true }; if (isMalformedTimeRange(malformedTimeRange)) { mockTimeRange(malformedTimeRange); - const stringDefault = getDefaultFromString(); - expect(stringDefault).toBe(DEFAULT_FROM); + const { fromStr } = getTimeRangeSettings(); + expect(fromStr).toBe(DEFAULT_FROM); } else { throw Error('Was expecting an object to be used for the malformed time range'); } }); + + describe('without UISettings', () => { + beforeEach(() => { + mockGetServices.mockImplementation(() => { + throw new Error('should not have been called'); + }); + }); + + it('is DEFAULT_FROM', () => { + const { fromStr } = getTimeRangeSettings(false); + expect(fromStr).toBe(DEFAULT_FROM); + }); + }); }); - describe('#getDefaultToString', () => { + describe('toStr', () => { test('should return the DEFAULT_TO constant by default', () => { mockTimeRange(); - const stringDefault = getDefaultToString(); - expect(stringDefault).toBe(DEFAULT_TO); + const { toStr } = getTimeRangeSettings(); + expect(toStr).toBe(DEFAULT_TO); }); test('should return a custom from range', () => { mockTimeRange({ to: 'now-15m' }); - const stringDefault = getDefaultToString(); - expect(stringDefault).toBe('now-15m'); + const { toStr } = getTimeRangeSettings(); + expect(toStr).toBe('now-15m'); }); test('should return the DEFAULT_TO when the whole object is null', () => { mockTimeRange(null); - const stringDefault = getDefaultToString(); - expect(stringDefault).toBe(DEFAULT_TO); + const { toStr } = getTimeRangeSettings(); + expect(toStr).toBe(DEFAULT_TO); }); test('should return the DEFAULT_TO when the whole object is undefined', () => { mockTimeRange(null); - const stringDefault = getDefaultToString(); - expect(stringDefault).toBe(DEFAULT_TO); + const { toStr } = getTimeRangeSettings(); + expect(toStr).toBe(DEFAULT_TO); }); test('should return the DEFAULT_TO when the to value is null', () => { mockTimeRange({ from: null }); - const stringDefault = getDefaultToString(); - expect(stringDefault).toBe(DEFAULT_TO); + const { toStr } = getTimeRangeSettings(); + expect(toStr).toBe(DEFAULT_TO); }); test('should return the DEFAULT_TO when the from value is undefined', () => { mockTimeRange({ to: undefined }); - const stringDefault = getDefaultToString(); - expect(stringDefault).toBe(DEFAULT_TO); + const { toStr } = getTimeRangeSettings(); + expect(toStr).toBe(DEFAULT_TO); }); test('should return the DEFAULT_TO when the to value is malformed', () => { const malformedTimeRange = { to: true }; if (isMalformedTimeRange(malformedTimeRange)) { mockTimeRange(malformedTimeRange); - const stringDefault = getDefaultToString(); - expect(stringDefault).toBe(DEFAULT_TO); + const { toStr } = getTimeRangeSettings(); + expect(toStr).toBe(DEFAULT_TO); } else { throw Error('Was expecting an object to be used for the malformed time range'); } }); + + describe('without UISettings', () => { + beforeEach(() => { + mockGetServices.mockImplementation(() => { + throw new Error('should not have been called'); + }); + }); + + it('is DEFAULT_TO', () => { + const { toStr } = getTimeRangeSettings(false); + expect(toStr).toBe(DEFAULT_TO); + }); + }); }); - describe('#getDefaultFromValue', () => { + describe('from', () => { test('should return DEFAULT_FROM', () => { mockTimeRange(); - const value = getDefaultFromValue(); - expect(value).toBe(new Date(DEFAULT_FROM_DATE).valueOf()); + const { from } = getTimeRangeSettings(); + expect(from).toBe(new Date(DEFAULT_FROM_DATE).valueOf()); }); test('should return a custom from range', () => { - const from = '2019-08-30T17:49:18.396Z'; - mockTimeRange({ from }); - const value = getDefaultFromValue(); - expect(value).toBe(new Date(from).valueOf()); + const mockFrom = '2019-08-30T17:49:18.396Z'; + mockTimeRange({ from: mockFrom }); + const { from } = getTimeRangeSettings(); + expect(from).toBe(new Date(mockFrom).valueOf()); }); test('should return the DEFAULT_FROM when the whole object is null', () => { mockTimeRange(null); - const stringDefault = getDefaultFromValue(); - expect(stringDefault).toBe(new Date(DEFAULT_FROM_DATE).valueOf()); + const { from } = getTimeRangeSettings(); + expect(from).toBe(new Date(DEFAULT_FROM_DATE).valueOf()); }); test('should return the DEFAULT_FROM when the whole object is undefined', () => { mockTimeRange(null); - const stringDefault = getDefaultFromValue(); - expect(stringDefault).toBe(new Date(DEFAULT_FROM_DATE).valueOf()); + const { from } = getTimeRangeSettings(); + expect(from).toBe(new Date(DEFAULT_FROM_DATE).valueOf()); }); test('should return the DEFAULT_FROM when the from value is null', () => { mockTimeRange({ from: null }); - const stringDefault = getDefaultFromValue(); - expect(stringDefault).toBe(new Date(DEFAULT_FROM_DATE).valueOf()); + const { from } = getTimeRangeSettings(); + expect(from).toBe(new Date(DEFAULT_FROM_DATE).valueOf()); }); test('should return the DEFAULT_FROM when the from value is undefined', () => { mockTimeRange({ from: undefined }); - const stringDefault = getDefaultFromValue(); - expect(stringDefault).toBe(new Date(DEFAULT_FROM_DATE).valueOf()); + const { from } = getTimeRangeSettings(); + expect(from).toBe(new Date(DEFAULT_FROM_DATE).valueOf()); }); test('should return the DEFAULT_FROM when the from value is malformed', () => { const malformedTimeRange = { from: true }; if (isMalformedTimeRange(malformedTimeRange)) { mockTimeRange(malformedTimeRange); - const stringDefault = getDefaultFromValue(); - expect(stringDefault).toBe(new Date(DEFAULT_FROM_DATE).valueOf()); + const { from } = getTimeRangeSettings(); + expect(from).toBe(new Date(DEFAULT_FROM_DATE).valueOf()); } else { throw Error('Was expecting an object to be used for the malformed time range'); } }); + + describe('without UISettings', () => { + beforeEach(() => { + mockGetServices.mockImplementation(() => { + throw new Error('should not have been called'); + }); + }); + + it('is DEFAULT_FROM in epoch', () => { + const { from } = getTimeRangeSettings(false); + expect(from).toBe(new Date(DEFAULT_FROM_DATE).valueOf()); + }); + }); }); - describe('#getDefaultToValue', () => { + describe('to', () => { test('should return DEFAULT_TO', () => { mockTimeRange(); - const value = getDefaultToValue(); - expect(value).toBe(new Date(DEFAULT_TO_DATE).valueOf()); + const { to } = getTimeRangeSettings(); + expect(to).toBe(new Date(DEFAULT_TO_DATE).valueOf()); }); test('should return a custom from range', () => { - const to = '2000-08-30T17:49:18.396Z'; - mockTimeRange({ to }); - const value = getDefaultToValue(); - expect(value).toBe(new Date(to).valueOf()); + const mockTo = '2000-08-30T17:49:18.396Z'; + mockTimeRange({ to: mockTo }); + const { to } = getTimeRangeSettings(); + expect(to).toBe(new Date(mockTo).valueOf()); }); test('should return the DEFAULT_TO_DATE when the whole object is null', () => { mockTimeRange(null); - const stringDefault = getDefaultToValue(); - expect(stringDefault).toBe(new Date(DEFAULT_TO_DATE).valueOf()); + const { to } = getTimeRangeSettings(); + expect(to).toBe(new Date(DEFAULT_TO_DATE).valueOf()); }); test('should return the DEFAULT_TO_DATE when the whole object is undefined', () => { mockTimeRange(null); - const stringDefault = getDefaultToValue(); - expect(stringDefault).toBe(new Date(DEFAULT_TO_DATE).valueOf()); + const { to } = getTimeRangeSettings(); + expect(to).toBe(new Date(DEFAULT_TO_DATE).valueOf()); }); test('should return the DEFAULT_TO_DATE when the from value is null', () => { mockTimeRange({ from: null }); - const stringDefault = getDefaultToValue(); - expect(stringDefault).toBe(new Date(DEFAULT_TO_DATE).valueOf()); + const { to } = getTimeRangeSettings(); + expect(to).toBe(new Date(DEFAULT_TO_DATE).valueOf()); }); test('should return the DEFAULT_TO_DATE when the from value is undefined', () => { mockTimeRange({ from: undefined }); - const stringDefault = getDefaultToValue(); - expect(stringDefault).toBe(new Date(DEFAULT_TO_DATE).valueOf()); + const { to } = getTimeRangeSettings(); + expect(to).toBe(new Date(DEFAULT_TO_DATE).valueOf()); }); test('should return the DEFAULT_TO_DATE when the from value is malformed', () => { const malformedTimeRange = { from: true }; if (isMalformedTimeRange(malformedTimeRange)) { mockTimeRange(malformedTimeRange); - const stringDefault = getDefaultToValue(); - expect(stringDefault).toBe(new Date(DEFAULT_TO_DATE).valueOf()); - } else { - throw Error('Was expecting an object to be used for the malformed time range'); - } - }); - }); - - describe('#getDefaultFromMoment', () => { - test('should return DEFAULT_FROM', () => { - mockTimeRange(); - const value = getDefaultFromMoment(); - expect(value.valueOf()).toBe(new Date(DEFAULT_FROM_DATE).valueOf()); - }); - - test('should return a custom from range', () => { - const from = '2019-08-30T17:49:18.396Z'; - mockTimeRange({ from }); - const value = getDefaultFromMoment(); - expect(value.valueOf()).toBe(new Date(from).valueOf()); - }); - - test('should return the DEFAULT_FROM when the whole object is null', () => { - mockTimeRange(null); - const defaultMoment = getDefaultFromMoment(); - expect(defaultMoment.valueOf()).toBe(new Date(DEFAULT_FROM_DATE).valueOf()); - }); - - test('should return the DEFAULT_FROM when the whole object is undefined', () => { - mockTimeRange(null); - const defaultMoment = getDefaultFromMoment(); - expect(defaultMoment.valueOf()).toBe(new Date(DEFAULT_FROM_DATE).valueOf()); - }); - - test('should return the DEFAULT_FROM when the from value is null', () => { - mockTimeRange({ from: null }); - const defaultMoment = getDefaultFromMoment(); - expect(defaultMoment.valueOf()).toBe(new Date(DEFAULT_FROM_DATE).valueOf()); - }); - - test('should return the DEFAULT_FROM when the from value is undefined', () => { - mockTimeRange({ from: undefined }); - const defaultMoment = getDefaultFromMoment(); - expect(defaultMoment.valueOf()).toBe(new Date(DEFAULT_FROM_DATE).valueOf()); - }); - - test('should return the DEFAULT_FROM when the from value is malformed', () => { - const malformedTimeRange = { from: true }; - if (isMalformedTimeRange(malformedTimeRange)) { - mockTimeRange(malformedTimeRange); - const defaultMoment = getDefaultFromMoment(); - expect(defaultMoment.valueOf()).toBe(new Date(DEFAULT_FROM_DATE).valueOf()); + const { to } = getTimeRangeSettings(); + expect(to).toBe(new Date(DEFAULT_TO_DATE).valueOf()); } else { throw Error('Was expecting an object to be used for the malformed time range'); } }); - }); - - describe('#getDefaultToMoment', () => { - test('should return DEFAULT_TO', () => { - mockTimeRange(); - const value = getDefaultToMoment(); - expect(value.valueOf()).toBe(new Date(DEFAULT_TO).valueOf()); - }); - - test('should return a custom range', () => { - const to = '2019-08-30T17:49:18.396Z'; - mockTimeRange({ to }); - const value = getDefaultToMoment(); - expect(value.valueOf()).toBe(new Date(to).valueOf()); - }); - test('should return the DEFAULT_TO when the whole object is null', () => { - mockTimeRange(null); - const defaultMoment = getDefaultToMoment(); - expect(defaultMoment.valueOf()).toBe(new Date(DEFAULT_TO).valueOf()); - }); - - test('should return the DEFAULT_TO when the whole object is undefined', () => { - mockTimeRange(null); - const defaultMoment = getDefaultToMoment(); - expect(defaultMoment.valueOf()).toBe(new Date(DEFAULT_TO).valueOf()); - }); - - test('should return the DEFAULT_TO when the from value is null', () => { - mockTimeRange({ from: null }); - const defaultMoment = getDefaultToMoment(); - expect(defaultMoment.valueOf()).toBe(new Date(DEFAULT_TO).valueOf()); - }); - - test('should return the DEFAULT_TO when the from value is undefined', () => { - mockTimeRange({ from: undefined }); - const defaultMoment = getDefaultToMoment(); - expect(defaultMoment.valueOf()).toBe(new Date(DEFAULT_TO).valueOf()); - }); + describe('without UISettings', () => { + beforeEach(() => { + mockGetServices.mockImplementation(() => { + throw new Error('should not have been called'); + }); + }); - test('should return the DEFAULT_TO when the from value is malformed', () => { - const malformedTimeRange = { from: true }; - if (isMalformedTimeRange(malformedTimeRange)) { - mockTimeRange(malformedTimeRange); - const defaultMoment = getDefaultToMoment(); - expect(defaultMoment.valueOf()).toBe(new Date(DEFAULT_TO).valueOf()); - } else { - throw Error('Was expecting an object to be used for the malformed time range'); - } + it('is DEFAULT_TO in epoch', () => { + const { to } = getTimeRangeSettings(false); + expect(to).toBe(new Date(DEFAULT_TO_DATE).valueOf()); + }); }); }); +}); - describe('#getDefaultIntervalKind', () => { +describe('getIntervalSettings', () => { + describe('kind', () => { test('should return default', () => { mockTimeRange(); - const value = getDefaultIntervalKind(); - expect(value).toBe(DEFAULT_INTERVAL_TYPE); + const { kind } = getIntervalSettings(); + expect(kind).toBe(DEFAULT_INTERVAL_TYPE); }); test('should return an interval when given non paused value', () => { const interval: DefaultIntervalSetting = { pause: false }; mockTimeRange(undefined, interval); - const value = getDefaultIntervalKind(); + const { kind } = getIntervalSettings(); const expected: Policy['kind'] = 'interval'; - expect(value).toBe(expected); + expect(kind).toBe(expected); }); test('should return a manual when given a paused value', () => { const interval: DefaultIntervalSetting = { pause: true }; mockTimeRange(undefined, interval); - const value = getDefaultIntervalKind(); + const { kind } = getIntervalSettings(); const expected: Policy['kind'] = 'manual'; - expect(value).toBe(expected); + expect(kind).toBe(expected); }); test('should return the default when the whole object is null', () => { mockTimeRange(undefined, null); - const value = getDefaultIntervalKind(); - expect(value).toBe(DEFAULT_INTERVAL_TYPE); + const { kind } = getIntervalSettings(); + expect(kind).toBe(DEFAULT_INTERVAL_TYPE); }); test('should return the default when the whole object is undefined', () => { mockTimeRange(undefined, undefined); - const value = getDefaultIntervalKind(); - expect(value).toBe(DEFAULT_INTERVAL_TYPE); + const { kind } = getIntervalSettings(); + expect(kind).toBe(DEFAULT_INTERVAL_TYPE); }); test('should return the default when the value is null', () => { mockTimeRange(undefined, { pause: null }); - const value = getDefaultIntervalKind(); - expect(value).toBe(DEFAULT_INTERVAL_TYPE); + const { kind } = getIntervalSettings(); + expect(kind).toBe(DEFAULT_INTERVAL_TYPE); }); test('should return the default when the value is undefined', () => { mockTimeRange(undefined, { pause: undefined }); - const value = getDefaultIntervalKind(); - expect(value).toBe(DEFAULT_INTERVAL_TYPE); + const { kind } = getIntervalSettings(); + expect(kind).toBe(DEFAULT_INTERVAL_TYPE); }); test('should return the default when the from value is malformed', () => { const malformedInterval = { pause: 'whoops a string' }; if (isMalformedInterval(malformedInterval)) { mockTimeRange(undefined, malformedInterval); - const value = getDefaultIntervalKind(); - expect(value).toBe(DEFAULT_INTERVAL_TYPE); + const { kind } = getIntervalSettings(); + expect(kind).toBe(DEFAULT_INTERVAL_TYPE); } else { throw Error('Was expecting an object to be used for the malformed interval'); } }); + + describe('without UISettings', () => { + beforeEach(() => { + mockGetServices.mockImplementation(() => { + throw new Error('should not have been called'); + }); + }); + + it('is DEFAULT_INTERVAL_TYPE', () => { + const { kind } = getIntervalSettings(false); + expect(kind).toBe(DEFAULT_INTERVAL_TYPE); + }); + }); }); - describe('#getDefaultIntervalDuration', () => { + describe('duration', () => { test('should return default', () => { mockTimeRange(); - const value = getDefaultIntervalDuration(); - expect(value).toBe(DEFAULT_INTERVAL_VALUE); + const { duration } = getIntervalSettings(); + expect(duration).toBe(DEFAULT_INTERVAL_VALUE); }); test('should return a value when given a paused value', () => { const interval: DefaultIntervalSetting = { value: 5 }; mockTimeRange(undefined, interval); - const value = getDefaultIntervalDuration(); + const { duration } = getIntervalSettings(); const expected: Policy['duration'] = 5; - expect(value).toBe(expected); + expect(duration).toBe(expected); }); test('should return the default when the whole object is null', () => { mockTimeRange(undefined, null); - const value = getDefaultIntervalDuration(); - expect(value).toBe(DEFAULT_INTERVAL_VALUE); + const { duration } = getIntervalSettings(); + expect(duration).toBe(DEFAULT_INTERVAL_VALUE); }); test('should return the default when the whole object is undefined', () => { mockTimeRange(undefined, undefined); - const value = getDefaultIntervalDuration(); - expect(value).toBe(DEFAULT_INTERVAL_VALUE); + const { duration } = getIntervalSettings(); + expect(duration).toBe(DEFAULT_INTERVAL_VALUE); }); test('should return the default when the value is null', () => { mockTimeRange(undefined, { value: null }); - const value = getDefaultIntervalDuration(); - expect(value).toBe(DEFAULT_INTERVAL_VALUE); + const { duration } = getIntervalSettings(); + expect(duration).toBe(DEFAULT_INTERVAL_VALUE); }); test('should return the default when the value is undefined', () => { mockTimeRange(undefined, { value: undefined }); - const value = getDefaultIntervalDuration(); - expect(value).toBe(DEFAULT_INTERVAL_VALUE); + const { duration } = getIntervalSettings(); + expect(duration).toBe(DEFAULT_INTERVAL_VALUE); }); test('should return the default when the value is malformed', () => { const malformedInterval = { value: 'whoops a string' }; if (isMalformedInterval(malformedInterval)) { mockTimeRange(undefined, malformedInterval); - const value = getDefaultIntervalDuration(); - expect(value).toBe(DEFAULT_INTERVAL_VALUE); + const { duration } = getIntervalSettings(); + expect(duration).toBe(DEFAULT_INTERVAL_VALUE); } else { throw Error('Was expecting an object to be used for the malformed interval'); } }); - }); - describe('#parseDateString', () => { - test('should return the first value as a moment if everything is ok', () => { - const value = parseDateString( - '1930-05-31T13:03:54.234Z', - '1940-05-31T13:03:54.234Z', - moment('1950-05-31T13:03:54.234Z') - ); - expect(value.valueOf()).toBe(new Date('1930-05-31T13:03:54.234Z').valueOf()); - }); + describe('without UISettings', () => { + beforeEach(() => { + mockGetServices.mockImplementation(() => { + throw new Error('should not have been called'); + }); + }); - test('should return the second value as a moment if the first is null', () => { - const value = parseDateString( - null, - '1940-05-31T13:03:54.234Z', - moment('1950-05-31T13:03:54.234Z') - ); - expect(value.valueOf()).toBe(new Date('1940-05-31T13:03:54.234Z').valueOf()); - }); - - test('should return the second value as a moment if the first is undefined', () => { - const value = parseDateString( - null, - '1940-05-31T13:03:54.234Z', - moment('1950-05-31T13:03:54.234Z') - ); - expect(value.valueOf()).toBe(new Date('1940-05-31T13:03:54.234Z').valueOf()); + it('is DEFAULT_INTERVAL_VALUE', () => { + const { duration } = getIntervalSettings(false); + expect(duration).toBe(DEFAULT_INTERVAL_VALUE); + }); }); }); diff --git a/x-pack/legacy/plugins/siem/public/utils/default_date_settings.ts b/x-pack/legacy/plugins/siem/public/utils/default_date_settings.ts index 273988c1672554..c4869a4851ae50 100644 --- a/x-pack/legacy/plugins/siem/public/utils/default_date_settings.ts +++ b/x-pack/legacy/plugins/siem/public/utils/default_date_settings.ts @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import chrome from 'ui/chrome'; import dateMath from '@elastic/datemath'; import moment from 'moment'; -import { isString, isBoolean, isNumber } from 'lodash/fp'; +import { isBoolean, isNumber, isString } from 'lodash/fp'; + import { DEFAULT_SIEM_TIME_RANGE, DEFAULT_SIEM_REFRESH_INTERVAL, @@ -16,6 +16,7 @@ import { DEFAULT_INTERVAL_TYPE, DEFAULT_INTERVAL_VALUE, } from '../../common/constants'; +import { KibanaServices } from '../lib/kibana'; import { Policy } from '../store/inputs/model'; interface DefaultTimeRange { @@ -29,7 +30,6 @@ interface DefaultInterval { } export type DefaultTimeRangeSetting = DefaultTimeRange | null | undefined; - export type DefaultIntervalSetting = DefaultInterval | null | undefined; // Defaults for if everything fails including dateMath.parse(DEFAULT_FROM) or dateMath.parse(DEFAULT_TO) @@ -38,117 +38,37 @@ const DEFAULT_FROM_MOMENT = moment().subtract(24, 'hours'); const DEFAULT_TO_MOMENT = moment(); /** - * Returns the default SIEM time range "from" string. This should be used only in - * non-ReactJS code. For ReactJS code, use the settings context hook instead - */ -export const getDefaultFromString = (): string => { - const defaultTimeRange: DefaultTimeRangeSetting = chrome - .getUiSettingsClient() - .get(DEFAULT_SIEM_TIME_RANGE); - if (defaultTimeRange != null && isString(defaultTimeRange.from)) { - return defaultTimeRange.from; - } else { - return DEFAULT_FROM; - } -}; - -/** - * Returns the default SIEM time range "to" string. This should be used only in - * non-ReactJS code. For ReactJS code, use the settings context hook instead + * Retrieves timeRange settings to populate filters + * + * @param {Boolean} uiSettings Whether to respect the user's UI settings. Defaults to true. */ -export const getDefaultToString = (): string => { - const defaultTimeRange: DefaultTimeRangeSetting = chrome - .getUiSettingsClient() - .get(DEFAULT_SIEM_TIME_RANGE); - if (defaultTimeRange != null && isString(defaultTimeRange.to)) { - return defaultTimeRange.to; - } else { - return DEFAULT_TO; - } -}; - -/** - * Returns the default SIEM time range "from" Epoch. This should be used only in - * non-ReactJS code. For ReactJS code, use the settings context hook instead - */ -export const getDefaultFromValue = (): number => getDefaultFromMoment().valueOf(); - -/** - * Returns the default SIEM time range "from" moment. This should be used only in - * non-ReactJS code. For ReactJS code, use the settings context hook instead - */ -export const getDefaultFromMoment = (): moment.Moment => { - const defaultTimeRange: DefaultTimeRangeSetting = chrome - .getUiSettingsClient() - .get(DEFAULT_SIEM_TIME_RANGE); - if (defaultTimeRange != null && isString(defaultTimeRange.from)) { - return parseDateString(defaultTimeRange.from, DEFAULT_FROM, DEFAULT_FROM_MOMENT); - } else { - return parseDateWithDefault(DEFAULT_FROM, DEFAULT_FROM_MOMENT); - } -}; +export const getTimeRangeSettings = (uiSettings = true) => { + const timeRange = uiSettings + ? KibanaServices.get().uiSettings.get(DEFAULT_SIEM_TIME_RANGE) + : null; -/** - * Returns the default SIEM time range "to" Epoch. This should be used only in - * non-ReactJS code. For ReactJS code, use the settings context hook instead - */ -export const getDefaultToValue = (): number => getDefaultToMoment().valueOf(); + const fromStr = (isString(timeRange?.from) && timeRange?.from) || DEFAULT_FROM; + const toStr = (isString(timeRange?.to) && timeRange?.to) || DEFAULT_TO; + const from = parseDateWithDefault(fromStr, DEFAULT_FROM_MOMENT).valueOf(); + const to = parseDateWithDefault(toStr, DEFAULT_TO_MOMENT).valueOf(); -/** - * Returns the default SIEM time range "to" moment. This should be used only in - * non-ReactJS code. For ReactJS code, use the settings context hook instead - */ -export const getDefaultToMoment = (): moment.Moment => { - const defaultTimeRange: DefaultTimeRangeSetting = chrome - .getUiSettingsClient() - .get(DEFAULT_SIEM_TIME_RANGE); - if (defaultTimeRange != null && isString(defaultTimeRange.to)) { - return parseDateString(defaultTimeRange.to, DEFAULT_TO, DEFAULT_TO_MOMENT); - } else { - return parseDateWithDefault(DEFAULT_TO, DEFAULT_TO_MOMENT); - } + return { from, fromStr, to, toStr }; }; /** - * Returns the default SIEM interval "kind". This should be used only in - * non-ReactJS code. For ReactJS code, use the settings context hook instead + * Retrieves refreshInterval settings to populate filters + * + * @param {Boolean} uiSettings Whether to respect the user's UI settings. Defaults to true. */ -export const getDefaultIntervalKind = (): Policy['kind'] => { - const defaultInterval: DefaultIntervalSetting = chrome - .getUiSettingsClient() - .get(DEFAULT_SIEM_REFRESH_INTERVAL); - if (defaultInterval != null && isBoolean(defaultInterval.pause)) { - return defaultInterval.pause ? 'manual' : 'interval'; - } else { - return DEFAULT_INTERVAL_TYPE; - } -}; +export const getIntervalSettings = (uiSettings = true): Policy => { + const interval = uiSettings + ? KibanaServices.get().uiSettings.get(DEFAULT_SIEM_REFRESH_INTERVAL) + : null; -/** - * Returns the default SIEM interval "duration". This should be used only in - * non-ReactJS code. For ReactJS code, use the settings context hook instead - */ -export const getDefaultIntervalDuration = (): Policy['duration'] => { - const defaultInterval: DefaultIntervalSetting = chrome - .getUiSettingsClient() - .get(DEFAULT_SIEM_REFRESH_INTERVAL); - if (defaultInterval != null && isNumber(defaultInterval.value)) { - return defaultInterval.value; - } else { - return DEFAULT_INTERVAL_VALUE; - } -}; + const duration = (isNumber(interval?.value) && interval?.value) || DEFAULT_INTERVAL_VALUE; + const kind = isBoolean(interval?.pause) && !interval?.pause ? 'interval' : DEFAULT_INTERVAL_TYPE; -export const parseDateString = ( - dateString: string | null | undefined, - defaultDateString: string, - defaultDate: moment.Moment -): moment.Moment => { - if (dateString != null) { - return parseDateWithDefault(dateString, defaultDate); - } else { - return parseDateWithDefault(defaultDateString, defaultDate); - } + return { kind, duration }; }; export const parseDateWithDefault = ( diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/_mock_server.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/_mock_server.ts index 4bf7b3279374b8..5b85012fd9f084 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/_mock_server.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/_mock_server.ts @@ -58,7 +58,6 @@ export const createMockServer = (config: Record = defaultConfig) })), }; server.decorate('request', 'getAlertsClient', () => alertsClient); - server.decorate('request', 'getBasePath', () => '/s/default'); server.plugins.elasticsearch = (elasticsearch as unknown) as ElasticsearchPlugin; server.plugins.spaces = { getSpaceId: () => 'default' }; server.plugins.actions = { @@ -91,7 +90,6 @@ export const createMockServerWithoutAlertClientDecoration = ( } as any; // The types have really bad conflicts at the moment so I have to use any const actionsClient = actionsClientMock.create(); - serverWithoutAlertClient.decorate('request', 'getBasePath', () => '/s/default'); return { serverWithoutAlertClient: serverWithoutAlertClient as ServerFacade & Hapi.Server, diff --git a/yarn.lock b/yarn.lock index e663f98afceffd..8ed111a4025c2a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16355,12 +16355,12 @@ icss-replace-symbols@^1.1.0: resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0= -icss-utils@^4.0.0, icss-utils@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" - integrity sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA== +icss-utils@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.0.0.tgz#d52cf4bcdcfa1c45c2dbefb4ffdf6b00ef608098" + integrity sha512-bA/xGiwWM17qjllIs9X/y0EjsB7e0AV08F3OL8UPsoNkNRibIuu8f1eKTnQ8QO1DteKKTxTUAn+IEWUToIwGOA== dependencies: - postcss "^7.0.14" + postcss "^7.0.5" icss-utils@^4.1.0: version "4.1.0" @@ -16369,6 +16369,13 @@ icss-utils@^4.1.0: dependencies: postcss "^7.0.14" +icss-utils@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" + integrity sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA== + dependencies: + postcss "^7.0.14" + idx@^2.5.6: version "2.5.6" resolved "https://registry.yarnpkg.com/idx/-/idx-2.5.6.tgz#1f824595070100ae9ad585c86db08dc74f83a59d"