Skip to content

Commit

Permalink
[ResponseOps][Alerts] Add configuration option for alerts table to ge…
Browse files Browse the repository at this point in the history
…tCases (#207038)

## Summary

> [!IMPORTANT]
> 🚧 Merging to the `alerts-table-refactor` feature branch, no review
needed from teams other than `response-ops`

> [!WARNING]
> Some tests are intentionally left failing since the converted usages
of the alerts table still have to be validated/improved by solutions and
may change significantly

Adds a `renderAlertsTable` prop to `getCases` to allow solutions to
customize the alerts table shown in the `Alerts` tab of their case view.
  • Loading branch information
umbopepato committed Jan 23, 2025
1 parent aef2052 commit 60043c8
Show file tree
Hide file tree
Showing 10 changed files with 61 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export const getCasesLazy = ({
timelineIntegration,
features,
releasePhase,
renderAlertsTable,
}: GetCasesPropsInternal) => (
<CasesProvider
value={{
Expand All @@ -59,6 +60,7 @@ export const getCasesLazy = ({
onAlertsTableLoaded={onAlertsTableLoaded}
refreshRef={refreshRef}
timelineIntegration={timelineIntegration}
renderAlertsTable={renderAlertsTable}
/>
</Suspense>
</CasesProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const CasesRoutesComponent: React.FC<CasesRoutesProps> = ({
onAlertsTableLoaded,
refreshRef,
timelineIntegration,
renderAlertsTable,
}) => {
const { basePath, permissions } = useCasesContext();
const { navigateToAllCases } = useAllCasesNavigation();
Expand Down Expand Up @@ -90,6 +91,7 @@ const CasesRoutesComponent: React.FC<CasesRoutesProps> = ({
onAlertsTableLoaded={onAlertsTableLoaded}
refreshRef={refreshRef}
timelineIntegration={timelineIntegration}
renderAlertsTable={renderAlertsTable}
/>
</Suspense>
</Route>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
* 2.0.
*/

import type { MutableRefObject } from 'react';
import type { ComponentType, MutableRefObject } from 'react';
import type { CaseViewAlertsTableProps } from '../case_view/types';
import type { CaseViewRefreshPropInterface, UseFetchAlertData } from '../../../common/ui/types';
import type { CasesNavigation } from '../links';
import type { CasesTimelineIntegration } from '../timeline_context';
Expand All @@ -22,4 +23,5 @@ export interface CasesRoutesProps {
refreshRef?: MutableRefObject<CaseViewRefreshPropInterface>;
timelineIntegration?: CasesTimelineIntegration;
onAlertsTableLoaded?: (eventIds: Array<Partial<{ _id: string }>>) => void;
renderAlertsTable?: ComponentType<CaseViewAlertsTableProps>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export const CaseViewPage = React.memo<CaseViewPageProps>(
showAlertDetails,
useFetchAlertData,
onAlertsTableLoaded,
renderAlertsTable,
}) => {
const { features } = useCasesContext();
const { urlParams } = useUrlParams();
Expand Down Expand Up @@ -122,7 +123,11 @@ export const CaseViewPage = React.memo<CaseViewPageProps>(
/>
)}
{activeTabId === CASE_VIEW_PAGE_TABS.ALERTS && features.alerts.enabled && (
<CaseViewAlerts caseData={caseData} onAlertsTableLoaded={onAlertsTableLoaded} />
<CaseViewAlerts
caseData={caseData}
renderAlertsTable={renderAlertsTable}
onAlertsTableLoaded={onAlertsTableLoaded}
/>
)}
{activeTabId === CASE_VIEW_PAGE_TABS.FILES && <CaseViewFiles caseData={caseData} />}
{activeTabId === CASE_VIEW_PAGE_TABS.OBSERVABLES && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
* 2.0.
*/

import React, { useMemo } from 'react';
import React, { type ComponentType, useMemo } from 'react';

import { EuiFlexItem, EuiFlexGroup, EuiProgress } from '@elastic/eui';
import { SECURITY_SOLUTION_RULE_TYPE_IDS } from '@kbn/securitysolution-rules';
import { AlertsTable } from '@kbn/response-ops-alerts-table';
import { AlertsTable as DefaultAlertsTable } from '@kbn/response-ops-alerts-table';
import type { SetRequired } from 'type-fest';
import type { CaseViewAlertsTableProps } from '../types';
import { SECURITY_SOLUTION_OWNER } from '../../../../common/constants';
import type { CaseUI } from '../../../../common';
import { getManualAlertIds } from './helpers';
Expand All @@ -23,9 +24,14 @@ import { useKibana } from '../../../common/lib/kibana';
interface CaseViewAlertsProps {
caseData: CaseUI;
onAlertsTableLoaded?: (eventIds: Array<Partial<{ _id: string }>>) => void;
renderAlertsTable?: ComponentType<CaseViewAlertsTableProps>;
}

export const CaseViewAlerts = ({ caseData, onAlertsTableLoaded }: CaseViewAlertsProps) => {
export const CaseViewAlerts = ({
caseData,
renderAlertsTable: CustomAlertsTable,
onAlertsTableLoaded,
}: CaseViewAlertsProps) => {
const { services } = useKibana();
const { data, http, notifications, fieldFormats, application, licensing, settings } =
services as SetRequired<typeof services, 'licensing'>;
Expand Down Expand Up @@ -55,6 +61,10 @@ export const CaseViewAlerts = ({ caseData, onAlertsTableLoaded }: CaseViewAlerts
);
}

const AlertsTable =
CustomAlertsTable ??
(DefaultAlertsTable as NonNullable<CaseViewAlertsProps['renderAlertsTable']>);

return isLoadingAlertFeatureIds ? (
<EuiFlexGroup>
<EuiFlexItem>
Expand All @@ -75,17 +85,24 @@ export const CaseViewAlerts = ({ caseData, onAlertsTableLoaded }: CaseViewAlerts
query={alertIdsQuery}
showAlertStatusWithFlapping={caseData.owner !== SECURITY_SOLUTION_OWNER}
onLoaded={onAlertsTableLoaded}
services={{
data,
http,
notifications,
fieldFormats,
application,
settings,
// In the Cases UI the licensing service is defined
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
licensing: licensing!,
}}
// Only provide the services to the default alerts table.
// Spreading from object to avoid incorrectly overriding
// services to `undefined` in custom solution tables
{...(CustomAlertsTable
? {}
: {
services: {
data,
http,
notifications,
fieldFormats,
application,
settings,
// In the Cases UI the licensing service is defined
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
licensing: licensing!,
},
})}
/>
</EuiFlexItem>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const CaseView = React.memo(
useFetchAlertData,
onAlertsTableLoaded,
refreshRef,
renderAlertsTable,
}: CaseViewProps) => {
const { spaces: spacesApi } = useKibana().services;
const { detailName: caseId } = useCaseViewParams();
Expand Down Expand Up @@ -88,6 +89,7 @@ export const CaseView = React.memo(
useFetchAlertData={useFetchAlertData}
onAlertsTableLoaded={onAlertsTableLoaded}
refreshRef={refreshRef}
renderAlertsTable={renderAlertsTable}
/>
</CasesTimelineIntegrationProvider>
) : null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { MutableRefObject } from 'react';
import type { ComponentType, MutableRefObject } from 'react';
import type { AlertsTableProps } from '@kbn/response-ops-alerts-table/types';
import type { CasesTimelineIntegration } from '../timeline_context';
import type { CasesNavigation } from '../links';
import type { CaseViewRefreshPropInterface, CaseUI } from '../../../common';
Expand All @@ -16,6 +17,7 @@ export interface CaseViewBaseProps {
ruleDetailsNavigation?: CasesNavigation<string | null | undefined, 'configurable'>;
showAlertDetails?: (alertId: string, index: string) => void;
useFetchAlertData: UseFetchAlertData;
renderAlertsTable?: ComponentType<CaseViewAlertsTableProps>;
onAlertsTableLoaded?: (eventIds: Array<Partial<{ _id: string }>>) => void;
/**
* A React `Ref` that Exposes data refresh callbacks.
Expand All @@ -39,3 +41,10 @@ export interface OnUpdateFields {
onSuccess?: () => void;
onError?: () => void;
}

export type CaseViewAlertsTableProps = Pick<
AlertsTableProps,
'id' | 'ruleTypeIds' | 'consumers' | 'query' | 'showAlertStatusWithFlapping' | 'onLoaded'
> & {
services?: AlertsTableProps['services'];
};
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { useKibana } from '../../../utils/kibana_react';
import { usePluginContext } from '../../../hooks/use_plugin_context';
import { useFetchAlertDetail } from '../../../hooks/use_fetch_alert_detail';
import { useFetchAlertData } from '../../../hooks/use_fetch_alert_data';
import { LazyAlertsFlyout } from '../../..';
import { LazyAlertsFlyout, ObservabilityAlertsTable } from '../../..';
import { CASES_PATH, paths } from '../../../../common/locators/paths';

export interface CasesProps {
Expand Down Expand Up @@ -63,6 +63,7 @@ export function Cases({ permissions }: CasesProps) {
}}
showAlertDetails={handleShowAlertDetails}
useFetchAlertData={useFetchAlertData}
renderAlertsTable={(props) => <ObservabilityAlertsTable {...props} />}
/>

{alertDetail && selectedAlertId !== '' && !alertLoading ? (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { useDispatch } from 'react-redux';
import type { CaseViewRefreshPropInterface } from '@kbn/cases-plugin/common';
import { CaseMetricsFeature } from '@kbn/cases-plugin/common';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { AlertsTableComponent } from '../../detections/components/alerts_table';
import { CaseDetailsRefreshContext } from '../../common/components/endpoint';
import { DocumentDetailsRightPanelKey } from '../../flyout/document_details/shared/constants/panel_keys';
import { RulePanelKey } from '../../flyout/rule_details/right';
Expand Down Expand Up @@ -145,6 +146,7 @@ const CaseContainerComponent: React.FC = () => {
useFetchAlertData,
onAlertsTableLoaded,
permissions: userCasesPermissions,
renderAlertsTable: (props) => <AlertsTableComponent {...props} />,
})}
</CaseDetailsRefreshContext.Provider>
<SpyRoute pageName={SecurityPageName.case} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ const EuiDataGridContainer = styled.div<GridContainerProps>`
interface DetectionEngineAlertTableProps
extends SetOptional<SecurityAlertsTableProps, 'id' | 'ruleTypeIds' | 'query'> {
inputFilters?: Filter[];
tableType: TableId;
tableType?: TableId;
sourcererScope?: SourcererScopeName;
isLoading?: boolean;
onRuleChange?: () => void;
Expand Down

0 comments on commit 60043c8

Please sign in to comment.