Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
PhilippeOberti committed Nov 25, 2024
1 parent 2e004f8 commit 4069ba9
Show file tree
Hide file tree
Showing 32 changed files with 872 additions and 175 deletions.
2 changes: 2 additions & 0 deletions packages/kbn-expandable-flyout/src/context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ export const ExpandableFlyoutContextProvider = memo<ExpandableFlyoutContextProvi
[urlKey]
);

// console.log('urlKey', urlKey);

return (
<ExpandableFlyoutContext.Provider value={contextValue}>
{children}
Expand Down
5 changes: 5 additions & 0 deletions packages/kbn-expandable-flyout/src/provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,17 @@ export const UrlSynchronizer = () => {
);

useEffect(() => {
// console.log('useEffect urlKey', urlKey);
if (!urlKey) {
return;
}

const currentValue = urlStorage.get<FlyoutPanels>(urlKey);
// console.log('currentValue', currentValue);

// Dispatch current value to redux store as it does not happen automatically
if (currentValue) {
// console.log('dispatching urlChangedAction', currentValue, urlKey);
dispatch(
urlChangedAction({
...currentValue,
Expand All @@ -57,6 +60,7 @@ export const UrlSynchronizer = () => {
}

const subscription = urlStorage.change$<FlyoutPanels>(urlKey).subscribe((value) => {
// console.log('dispatching urlChangedAction inside subscription', value, urlKey);
dispatch(urlChangedAction({ ...value, preview: value?.preview?.at(-1), id: urlKey }));
});

Expand Down Expand Up @@ -91,6 +95,7 @@ export const ExpandableFlyoutProvider: FC<PropsWithChildren<ExpandableFlyoutProv
children,
urlKey,
}) => {
// console.log('ExpandableFlyoutProvider urlKey', urlKey);
return (
<ExpandableFlyoutContextProvider urlKey={urlKey}>
<ReduxProvider context={Context} store={store}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,18 @@

import React, { type ReactNode, useMemo } from 'react';
import styled from 'styled-components';
import { EuiThemeProvider, useEuiTheme, type EuiThemeComputed } from '@elastic/eui';
import { useEuiTheme, type EuiThemeComputed } from '@elastic/eui';
import { IS_DRAGGING_CLASS_NAME } from '@kbn/securitysolution-t-grid';
import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template';
import type { KibanaPageTemplateProps } from '@kbn/shared-ux-page-kibana-template';
import { ExpandableFlyoutProvider } from '@kbn/expandable-flyout';
import { URL_PARAM_KEY } from '../../../common/hooks/use_url_state';
import { SecuritySolutionFlyout, TimelineFlyout } from '../../../flyout';
import { SecuritySolutionFlyout } from '../../../flyout';
import { useSecuritySolutionNavigation } from '../../../common/components/navigation/use_security_solution_navigation';
import { TimelineId } from '../../../../common/types/timeline';
import { getTimelineShowStatusByIdSelector } from '../../../timelines/store/selectors';
import { useDeepEqualSelector } from '../../../common/hooks/use_selector';
import { GlobalKQLHeader } from './global_kql_header';
import { Timeline } from './timeline';
import { useShowTimeline } from '../../../common/utils/timeline/use_show_timeline';
import { useRouteSpy } from '../../../common/utils/route/use_route_spy';
import { SecurityPageName } from '../../types';
Expand Down Expand Up @@ -103,16 +102,16 @@ export const SecuritySolutionTemplateWrapper: React.FC<SecuritySolutionTemplateW
<SecuritySolutionFlyout />
</ExpandableFlyoutProvider>
</KibanaPageTemplate.Section>
{isTimelineBottomBarVisible && (
<KibanaPageTemplate.BottomBar data-test-subj="timeline-bottom-bar-container">
<EuiThemeProvider colorMode={globalColorMode}>
<ExpandableFlyoutProvider urlKey={URL_PARAM_KEY.timelineFlyout}>
<Timeline />
<TimelineFlyout />
</ExpandableFlyoutProvider>
</EuiThemeProvider>
</KibanaPageTemplate.BottomBar>
)}
{/* {isTimelineBottomBarVisible && (*/}
{/* <KibanaPageTemplate.BottomBar data-test-subj="timeline-bottom-bar-container">*/}
{/* <EuiThemeProvider colorMode={globalColorMode}>*/}
{/* <ExpandableFlyoutProvider urlKey={URL_PARAM_KEY.timelineFlyout}>*/}
{/* <Timeline />*/}
{/* <TimelineFlyout />*/}
{/* </ExpandableFlyoutProvider>*/}
{/* </EuiThemeProvider>*/}
{/* </KibanaPageTemplate.BottomBar>*/}
{/* )}*/}
</>
)}
</StyledKibanaPageTemplate>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,22 @@ import React, { useCallback, useMemo } from 'react';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import type { TableId } from '@kbn/securitysolution-data-table';
import { EuiPanel } from '@elastic/eui';
import {
ANCESTOR_INDEX,
ENTRY_LEADER_ENTITY_ID,
ENTRY_LEADER_START,
} from '../../shared/constants/field_names';
import { getField } from '../../shared/utils';
import type { Process } from '@kbn/session-view-plugin/common';
import { useUserPrivileges } from '../../../../common/components/user_privileges';
import { SESSION_VIEW_TEST_ID } from './test_ids';
import { isActiveTimeline } from '../../../../helpers';
import { useSourcererDataView } from '../../../../sourcerer/containers';
import { DocumentDetailsPreviewPanelKey } from '../../shared/constants/panel_keys';
import {
DocumentDetailsPreviewPanelKey,
DocumentDetailsSessionViewPanelKey,
} from '../../shared/constants/panel_keys';
import { useKibana } from '../../../../common/lib/kibana';
import { useDocumentDetailsContext } from '../../shared/context';
import { SourcererScopeName } from '../../../../sourcerer/store/model';
import { detectionsTimelineIds } from '../../../../timelines/containers/helpers';
import { ALERT_PREVIEW_BANNER } from '../../preview/constants';
import { useLicense } from '../../../../common/hooks/use_license';
import { useSessionPreview } from '../../right/hooks/use_session_preview';
import { useSessionViewConfig } from '../../shared/hooks/use_session_view_config';
import { SessionViewNoDataMessage } from '../../shared/components/session_view_no_data_message';
import { DocumentEventTypes } from '../../../../common/lib/telemetry';

Expand All @@ -37,18 +36,14 @@ export const SESSION_VIEW_ID = 'session-view';
*/
export const SessionView: FC = () => {
const { sessionView, telemetry } = useKibana().services;
const { getFieldsData, indexName, scopeId, dataFormattedForFieldBrowser } =
useDocumentDetailsContext();
const { getFieldsData, scopeId, dataFormattedForFieldBrowser } = useDocumentDetailsContext();

const { canReadPolicyManagement } = useUserPrivileges().endpointPrivileges;

const sessionViewConfig = useSessionPreview({ getFieldsData, dataFormattedForFieldBrowser });
const sessionViewConfig = useSessionViewConfig({ getFieldsData, dataFormattedForFieldBrowser });
const isEnterprisePlus = useLicense().isEnterprise();
const isEnabled = sessionViewConfig && isEnterprisePlus;

const ancestorIndex = getField(getFieldsData(ANCESTOR_INDEX)); // e.g in case of alert, we want to grab it's origin index
const sessionEntityId = getField(getFieldsData(ENTRY_LEADER_ENTITY_ID)) || '';
const sessionStartTime = getField(getFieldsData(ENTRY_LEADER_START)) || '';
const index = ancestorIndex || indexName;

const sourcererScope = useMemo(() => {
if (isActiveTimeline(scopeId)) {
return SourcererScopeName.timeline;
Expand Down Expand Up @@ -83,14 +78,30 @@ export const SessionView: FC = () => {
[openPreviewPanel, eventDetailsIndex, scopeId, telemetry]
);

const openDetails = useCallback(
(selectedProcess: Process | null) =>
openPreviewPanel({
id: DocumentDetailsSessionViewPanelKey,
params: {
selectedProcess,
index: sessionViewConfig?.index,
sessionEntityId: sessionViewConfig?.sessionEntityId,
sessionStartTime: sessionViewConfig?.sessionStartTime,
investigatedAlertId: sessionViewConfig?.investigatedAlertId,
scopeId,
},
}),
[openPreviewPanel, sessionViewConfig, scopeId]
);

return isEnabled ? (
<div data-test-subj={SESSION_VIEW_TEST_ID}>
{sessionView.getSessionView({
index,
sessionEntityId,
sessionStartTime,
...sessionViewConfig,
isFullScreen: true,
loadAlertDetails: openAlertDetailsPreview,
openDetails: (selectedProcess: Process | null) => openDetails(selectedProcess),
canReadPolicyManagement,
})}
</div>
) : (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { useUiSetting$ } from '@kbn/kibana-react-plugin/public';
import { ENABLE_VISUALIZATIONS_IN_FLYOUT_SETTING } from '../../../../../common/constants';
import { useLicense } from '../../../../common/hooks/use_license';
import { SessionPreview } from './session_preview';
import { useSessionPreview } from '../hooks/use_session_preview';
import { useSessionViewConfig } from '../../shared/hooks/use_session_view_config';
import { useInvestigateInTimeline } from '../../../../detections/components/alerts_table/timeline_actions/use_investigate_in_timeline';
import { useDocumentDetailsContext } from '../../shared/context';
import { ALERTS_ACTIONS } from '../../../../common/lib/apm/user_actions';
Expand Down Expand Up @@ -47,7 +47,7 @@ export const SessionPreviewContainer: FC = () => {
);

// decide whether to show the session view or not
const sessionViewConfig = useSessionPreview({ getFieldsData, dataFormattedForFieldBrowser });
const sessionViewConfig = useSessionViewConfig({ getFieldsData, dataFormattedForFieldBrowser });
const isEnterprisePlus = useLicense().isEnterprise();
const isEnabled = sessionViewConfig && isEnterprisePlus;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import type { FC } from 'react';
import React, { useMemo } from 'react';
import type { SessionViewPanelPaths } from '.';
import type { SessionViewPanelTabType } from './tabs';
import { FlyoutBody } from '../../shared/components/flyout_body';

export interface PanelContentProps {
/**
* Id of the tab selected in the parent component to display its content
*/
selectedTabId: SessionViewPanelPaths;
/**
* Tabs display right below the flyout's header
*/
tabs: SessionViewPanelTabType[];
}

/**
* Document details expandable flyout right section, that will display the content
* of the overview, table and json tabs.
*/
export const PanelContent: FC<PanelContentProps> = ({ selectedTabId, tabs }) => {
const selectedTabContent = useMemo(() => {
return tabs.find((tab) => tab.id === selectedTabId)?.content;
}, [selectedTabId, tabs]);

return <FlyoutBody>{selectedTabContent}</FlyoutBody>;
};

PanelContent.displayName = 'PanelContent';
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* 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, { createContext, memo, useContext, useMemo } from 'react';
import type { Process } from '@kbn/session-view-plugin/common';
import { FlyoutError } from '../../shared/components/flyout_error';
import type { SessionViewPanelProps } from '.';

export interface SessionViewPanelContext {
selectedProcess: Process | null;
index: string;
sessionEntityId: string;
sessionStartTime: string;
scopeId: string;
investigatedAlertId: string;
}

export const SessionViewPanelContext = createContext<SessionViewPanelContext | undefined>(
undefined
);

export type SessionViewPanelProviderProps = {
/**
* React components to render
*/
children: React.ReactNode;
} & Partial<SessionViewPanelProps['params']>;

export const SessionViewPanelProvider = memo(
({
selectedProcess,
index,
sessionEntityId,
sessionStartTime,
scopeId,
investigatedAlertId,
children,
}: SessionViewPanelProviderProps) => {
console.group('SessionViewPanelProvider');
console.log('selectedProcess', selectedProcess);
console.log('index', index);
console.log('sessionEntityId', sessionEntityId);
console.log('sessionStartTime', sessionStartTime);
console.log('scopeId', scopeId);
console.log('investigatedAlertId', investigatedAlertId);
console.groupEnd();

const contextValue = useMemo(
() =>
selectedProcess &&
index &&
sessionEntityId &&
sessionStartTime &&
scopeId &&
investigatedAlertId
? {
selectedProcess,
index,
sessionEntityId,
sessionStartTime,
scopeId,
investigatedAlertId,
}
: undefined,
[selectedProcess, index, sessionEntityId, sessionStartTime, scopeId]
);

if (!contextValue) {
return <FlyoutError />;
}

return (
<SessionViewPanelContext.Provider value={contextValue}>
{children}
</SessionViewPanelContext.Provider>
);
}
);

SessionViewPanelProvider.displayName = 'SessionViewPanelProvider';

export const useSessionViewPanelContext = (): SessionViewPanelContext => {
const contextValue = useContext(SessionViewPanelContext);

if (!contextValue) {
throw new Error(
'SessionViewPanelContext can only be used within SessionViewPanelContext provider'
);
}

return contextValue;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import type { EuiFlyoutHeader } from '@elastic/eui';
import { EuiTab } from '@elastic/eui';
import type { FC } from 'react';
import React, { memo } from 'react';
import type { SessionViewPanelTabType } from './tabs';
import type { SessionViewPanelPaths } from '.';
import { FlyoutHeader } from '../../shared/components/flyout_header';
import { FlyoutHeaderTabs } from '../../shared/components/flyout_header_tabs';

export interface PanelHeaderProps extends React.ComponentProps<typeof EuiFlyoutHeader> {
/**
* Id of the tab selected in the parent component to display its content
*/
selectedTabId: SessionViewPanelPaths;
/**
* Callback to set the selected tab id in the parent component
* @param selected
*/
setSelectedTabId: (selected: SessionViewPanelPaths) => void;
/**
* Tabs to display in the header
*/
tabs: SessionViewPanelTabType[];
}

export const PanelHeader: FC<PanelHeaderProps> = memo(
({ selectedTabId, setSelectedTabId, tabs, ...flyoutHeaderProps }) => {
const onSelectedTabChanged = (id: SessionViewPanelPaths) => setSelectedTabId(id);

const renderTabs = tabs.map((tab, index) => (
<EuiTab
onClick={() => onSelectedTabChanged(tab.id)}
isSelected={tab.id === selectedTabId}
key={index}
data-test-subj={tab['data-test-subj']}
>
{tab.name}
</EuiTab>
));

return (
<FlyoutHeader {...flyoutHeaderProps}>
<FlyoutHeaderTabs>{renderTabs}</FlyoutHeaderTabs>
</FlyoutHeader>
);
}
);

PanelHeader.displayName = 'PanelHeader';
Loading

0 comments on commit 4069ba9

Please sign in to comment.