diff --git a/x-pack/plugins/security_solution/common/types/timeline/index.ts b/x-pack/plugins/security_solution/common/types/timeline/index.ts index ba4a8a13501fa..d608ae4f84408 100644 --- a/x-pack/plugins/security_solution/common/types/timeline/index.ts +++ b/x-pack/plugins/security_solution/common/types/timeline/index.ts @@ -438,6 +438,7 @@ export type ImportTimelineResultSchema = runtimeTypes.TypeOf, plugins: SetupPlugins @@ -145,6 +154,20 @@ export class Plugin implements IPlugin { // required to show the alert table inside cases const { alertsTableConfigurationRegistry } = plugins.triggersActionsUi; + setScopedHistory(params.history); + setHeaderActionMenuMounter(params.setHeaderActionMenu); + syncHistoryLocations(); const { registerAlertsTableConfiguration } = await this.lazyRegisterAlertsTableConfiguration(); registerAlertsTableConfiguration(alertsTableConfigurationRegistry, this.storage); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/index.tsx new file mode 100644 index 0000000000000..abfbe0350f82e --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/index.tsx @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { DiscoverMainRoute } from '../../../../../../../../src/plugins/discover/public/application/main/index'; + +const DiscoverTabContent = () => { + return ( +
+ +
+ ); +}; + +// eslint-disable-next-line import/no-default-export +export default DiscoverTabContent; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx index 0d77cfe739b3d..57225208d2d75 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx @@ -52,6 +52,7 @@ const GraphTabContent = lazy(() => import('../graph_tab_content')); const NotesTabContent = lazy(() => import('../notes_tab_content')); const PinnedTabContent = lazy(() => import('../pinned_tab_content')); const SessionTabContent = lazy(() => import('../session_tab_content')); +const DiscoverTabContent = lazy(() => import('../discover_tab_content')); interface BasicTimelineTab { renderCellValue: (props: CellValueElementProps) => React.ReactNode; @@ -130,6 +131,13 @@ const PinnedTab: React.FC<{ )); PinnedTab.displayName = 'PinnedTab'; +const DiscoverTab: React.FC = memo(() => ( + }> + + +)); +DiscoverTab.displayName = 'DiscoverTab'; + type ActiveTimelineTabProps = BasicTimelineTab & { activeTimelineTab: TimelineTabs }; const ActiveTimelineTab = memo( @@ -143,6 +151,8 @@ const ActiveTimelineTab = memo( return ; case TimelineTabs.session: return ; + case TimelineTabs.discover: + return ; default: return null; } @@ -183,6 +193,12 @@ const ActiveTimelineTab = memo( timelineId={timelineId} /> + + + {timelineType === TimelineType.default && ( = ({ setActiveTab(TimelineTabs.session); }, [setActiveTab]); + const setDiscoverAsActiveTab = useCallback(() => { + setActiveTab(TimelineTabs.discover); + }, [setActiveTab]); + useEffect(() => { if (!graphEventId && activeTab === TimelineTabs.graph) { setQueryAsActiveTab(); @@ -389,6 +409,15 @@ const TabsContentComponent: React.FC = ({ )} + + {i18n.DISCOVER_TAB} + )} diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/translations.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/translations.ts index e3a53675389b7..3f1da8aa387ca 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/translations.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/translations.ts @@ -45,3 +45,10 @@ export const SESSION_TAB = i18n.translate( defaultMessage: 'Session View', } ); + +export const DISCOVER_TAB = i18n.translate( + 'xpack.securitySolution.timeline.tabs.discoverTabTimelineTitle', + { + defaultMessage: 'Discover', + } +); diff --git a/x-pack/plugins/security_solution/public/types.ts b/x-pack/plugins/security_solution/public/types.ts index 76351fde26135..fadf88d71c4ce 100644 --- a/x-pack/plugins/security_solution/public/types.ts +++ b/x-pack/plugins/security_solution/public/types.ts @@ -6,17 +6,34 @@ */ import type { BehaviorSubject, Observable } from 'rxjs'; +import type { History } from 'history'; -import type { AppLeaveHandler, CoreStart } from '@kbn/core/public'; +import type { + AppLeaveHandler, + ApplicationStart, + Capabilities, + ChromeStart, + CoreStart, + DocLinksStart, + PluginInitializerContext, + HttpStart, + IUiSettingsClient, + NotificationsStart, + ToastsStart, +} from '@kbn/core/public'; import type { HomePublicPluginSetup } from '@kbn/home-plugin/public'; -import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import type { + DataPublicPluginStart, + FilterManager, + TimefilterContract, +} from '@kbn/data-plugin/public'; import type { EmbeddableStart } from '@kbn/embeddable-plugin/public'; import type { LensPublicStart } from '@kbn/lens-plugin/public'; import type { NewsfeedPublicPluginStart } from '@kbn/newsfeed-plugin/public'; import type { Start as InspectorStart } from '@kbn/inspector-plugin/public'; import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; -import type { Storage } from '@kbn/kibana-utils-plugin/public'; +import { Storage } from '@kbn/kibana-utils-plugin/public'; import type { FleetStart } from '@kbn/fleet-plugin/public'; import type { PluginStart as ListsPluginStart } from '@kbn/lists-plugin/public'; import type { SpacesPluginStart } from '@kbn/spaces-plugin/public'; @@ -49,6 +66,21 @@ import type { GuidedOnboardingPluginStart } from '@kbn/guided-onboarding-plugin/ import type { DataViewsServicePublic } from '@kbn/data-views-plugin/public'; import type { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public'; +import type { UiCounterMetricType } from '@kbn/analytics'; +import type { ChartsPluginStart } from '@kbn/charts-plugin/public'; +import type { SettingsStart } from '@kbn/core-ui-settings-browser'; +import type { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public'; +import type { DiscoverAppLocator } from '@kbn/discover-plugin/common'; +import type { DiscoverContextAppLocator } from '@kbn/discover-plugin/public/application/context/services/locator'; +import type { DiscoverSingleDocLocator } from '@kbn/discover-plugin/public/application/doc/locator'; +import type { HistoryLocationState } from '@kbn/discover-plugin/public/build_services'; +import type { ExpressionsStart } from '@kbn/expressions-plugin/public'; +import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; +import type { NavigationPublicPluginStart } from '@kbn/navigation-plugin/public'; +import type { SharePluginStart } from '@kbn/share-plugin/public'; +import type { UrlForwardingStart } from '@kbn/url-forwarding-plugin/public'; +import { memoize, once } from 'lodash'; +import { useHistory } from 'react-router-dom'; import type { ResolverPluginSetup } from './resolver/types'; import type { Inspect } from '../common/search_strategy'; import type { Detections } from './detections'; @@ -75,45 +107,79 @@ export interface SetupPlugins { home?: HomePublicPluginSetup; licensing: LicensingPluginSetup; security: SecurityPluginSetup; + share: SharePluginStart; triggersActionsUi: TriggersActionsSetup; usageCollection?: UsageCollectionSetup; ml?: MlPluginSetup; } export interface StartPlugins { + addBasePath: (path: string) => string; + application: ApplicationStart; + capabilities: Capabilities; + charts: ChartsPluginStart; + chrome: ChromeStart; + contextLocator: DiscoverContextAppLocator; + core: CoreStart; + dataViewEditor: DataViewEditorStart; + docLinks: DocLinksStart; + expressions: ExpressionsStart; + fieldFormats: FieldFormatsStart; + filterManager: FilterManager; + history: () => History; + http: HttpStart; + locator: DiscoverAppLocator; + metadata: { branch: string }; + navigation: NavigationPublicPluginStart; + notifications: NotificationsStart; + savedObjectsManagement: SavedObjectsManagementPluginStart; + savedObjectsTagging?: SavedObjectsTaggingApi; + settings: SettingsStart; + share?: SharePluginStart; + singleDocLocator: DiscoverSingleDocLocator; + storage: Storage; + theme: ChartsPluginStart['theme']; + timefilter: TimefilterContract; + toastNotifications: ToastsStart; + trackUiMetric?: (metricType: UiCounterMetricType, eventName: string | string[]) => void; + uiSettings: IUiSettingsClient; + urlForwarding: UrlForwardingStart; + + // Previous Existing cases: CasesUiStart; - data: DataPublicPluginStart; - unifiedSearch: UnifiedSearchPublicPluginStart; + cloud?: CloudStart; + cloudDefend: CloudDefendPluginStart; + cloudExperiments?: CloudExperimentsPluginStart; + cloudSecurityPosture: CspClientPluginStart; dashboard?: DashboardStart; + data: DataPublicPluginStart; + dataViewFieldEditor: IndexPatternFieldEditorStart; + dataViews: DataViewsServicePublic; embeddable: EmbeddableStart; - inspector: InspectorStart; fleet?: FleetStart; guidedOnboarding: GuidedOnboardingPluginStart; + inspector: InspectorStart; kubernetesSecurity: KubernetesSecurityStart; lens: LensPublicStart; - lists?: ListsPluginStart; licensing: LicensingPluginStart; - newsfeed?: NewsfeedPublicPluginStart; - triggersActionsUi: TriggersActionsStart; - timelines: TimelinesUIStart; - sessionView: SessionViewStart; - uiActions: UiActionsStart; + lists?: ListsPluginStart; ml?: MlPluginStart; - spaces?: SpacesPluginStart; - dataViewFieldEditor: IndexPatternFieldEditorStart; + newsfeed?: NewsfeedPublicPluginStart; osquery: OsqueryPluginStart; + savedObjectsTaggingOss: SavedObjectTaggingOssPluginStart; security: SecurityPluginStart; - cloud?: CloudStart; - cloudDefend: CloudDefendPluginStart; - cloudSecurityPosture: CspClientPluginStart; + sessionView: SessionViewStart; + spaces?: SpacesPluginStart; threatIntelligence: ThreatIntelligencePluginStart; - cloudExperiments?: CloudExperimentsPluginStart; - dataViews: DataViewsServicePublic; + timelines: TimelinesUIStart; + triggersActionsUi: TriggersActionsStart; + uiActions: UiActionsStart; + usageCollection: UsageCollectionSetup; + unifiedSearch: UnifiedSearchPublicPluginStart; } export interface StartPluginsDependencies extends StartPlugins { savedObjectsManagement: SavedObjectsManagementPluginStart; - savedObjectsTaggingOss: SavedObjectTaggingOssPluginStart; } export type StartServices = CoreStart & @@ -186,3 +252,67 @@ export interface StartedSubPlugins { threatIntelligence: ReturnType; timelines: ReturnType; } + +export const useGetHistory = once(() => { + const history = useHistory(); + history.listen(() => { + // keep at least one listener so that `history.location` always in sync + }); + return history; +}); + +export const buildDiscoverServices = memoize(function ( + core: CoreStart, + plugins: StartPlugins, + context: PluginInitializerContext, + locator: DiscoverAppLocator, + contextLocator: DiscoverContextAppLocator, + singleDocLocator: DiscoverSingleDocLocator +) { + const { usageCollection } = plugins; + const storage = new Storage(localStorage); + + return { + application: core.application, + addBasePath: core.http.basePath.prepend, + capabilities: core.application.capabilities, + chrome: core.chrome, + core, + data: plugins.data, + docLinks: core.docLinks, + embeddable: plugins.embeddable, + theme: plugins.charts.theme, + fieldFormats: plugins.fieldFormats, + filterManager: plugins.data.query.filterManager, + history: useGetHistory, + dataViews: plugins.data.dataViews, + inspector: plugins.inspector, + metadata: { + branch: context.env.packageInfo.branch, + }, + navigation: plugins.navigation, + share: plugins.share, + urlForwarding: plugins.urlForwarding, + timefilter: plugins.data.query.timefilter.timefilter, + toastNotifications: core.notifications.toasts, + notifications: core.notifications, + uiSettings: core.uiSettings, + settings: core.settings, + storage, + trackUiMetric: usageCollection?.reportUiCounter.bind(usageCollection, 'discover'), + dataViewFieldEditor: plugins.dataViewFieldEditor, + http: core.http, + spaces: plugins.spaces, + dataViewEditor: plugins.dataViewEditor, + triggersActionsUi: plugins.triggersActionsUi, + locator, + contextLocator, + singleDocLocator, + expressions: plugins.expressions, + charts: plugins.charts, + savedObjectsTagging: plugins.savedObjectsTaggingOss?.getTaggingApi(), + savedObjectsManagement: plugins.savedObjectsManagement, + unifiedSearch: plugins.unifiedSearch, + lens: plugins.lens, + }; +});