Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
ef36295
wip I
XavierM Apr 19, 2022
e7f2db1
Merge branch 'main' of github.com:elastic/kibana into alert_hook
XavierM Apr 19, 2022
1a2c01b
Merge branch 'main' of github.com:elastic/kibana into alert_hook
XavierM Apr 28, 2022
101f207
Merge branch 'main' of github.com:elastic/kibana into alert_hook
XavierM May 3, 2022
97f0724
add alert table state in case
XavierM May 10, 2022
34743f4
Merge branch 'main' of github.com:elastic/kibana into alert_hook
XavierM May 10, 2022
8ad955b
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine May 10, 2022
0f631fd
add new API to get FeatureID form registrationContext and update UI t…
XavierM May 12, 2022
64c9934
Merge branch 'main' of github.com:elastic/kibana into alert_hook
XavierM May 12, 2022
751c424
Merge branch 'alert_hook' of github.com:XavierM/kibana into alert_hook
XavierM May 12, 2022
e4a9428
rm dead code
XavierM May 12, 2022
722ec2b
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine May 12, 2022
18ae8e6
Merge remote-tracking branch 'upstream/main' into alert_hook
May 12, 2022
d857e79
remove unnecessary memo
May 12, 2022
fb97e17
adds tests for case view helpers
May 12, 2022
e3200d0
Move http call to API and add tests for getFeatureIds
May 12, 2022
d9d0362
fix type + unit test
XavierM May 16, 2022
1159965
Merge branch 'main' of github.com:elastic/kibana into alert_hook
XavierM May 16, 2022
a5d8dfc
add unit tests + cleanup
XavierM May 16, 2022
6844821
Merge branch 'main' of github.com:elastic/kibana into alert_hook
XavierM May 16, 2022
0eb04cc
add new api integration test for _feature_ids
XavierM May 17, 2022
dcc63d2
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine May 17, 2022
7b21712
Fix small type creating typescript slowness
May 12, 2022
1371478
remove console log
May 17, 2022
30cc5c7
use import type for validfeatureId
May 17, 2022
08ae759
force any to improve typescript performance
May 17, 2022
e6c8a92
Update APM (#132270)
renovate[bot] May 16, 2022
016aaf4
[ResponseOps][Docs] Updating ServiceNow docs with OAuth setup instruc…
ymao1 May 16, 2022
29b56a6
Show polling options when 'Data streams' option is selected in the Co…
May 17, 2022
0eecdb1
[Osquery] Make Osquery All with All base privillege (#130523)
tomsonpl May 17, 2022
9c2f618
[XY] Add normalizeTable function to correct works with esdocs (#131917)
VladLasitsa May 17, 2022
a06e781
[Osquery] Add default osquery_saved_query objects (#129461)
tomsonpl May 17, 2022
59ffe0d
[Unified Search] Show error message for invalid date filter value (#1…
nlatipov May 17, 2022
b5dc585
Update navigation landing pages to use appLinks config (#132027)
machadoum May 17, 2022
1160ae5
[Cloud Posture] add resource findings page flyout (#132243)
orouz May 17, 2022
0f66dcf
[Discover] Add a tour for Document Explorer (#131125)
jughosta May 17, 2022
1724ab7
[XY] Add `minTimeBarInterval` arg (#128726)
VladLasitsa May 17, 2022
430264d
Merge remote-tracking branch 'upstream/main' into alert_hook
May 17, 2022
dc41578
do not use barrel imports
May 17, 2022
94fd76a
do not use barrel import
May 17, 2022
9545f0e
do not use barrel import
May 17, 2022
b4e82ca
do not use barrel imports
May 17, 2022
08b760c
do not use barrel import
May 17, 2022
c7dc373
import types
May 17, 2022
cbf0be3
Add tests
cnasikas May 17, 2022
229e75b
Merge branch 'alert_hook' of github.com:XavierM/kibana into alert_hook
cnasikas May 17, 2022
66abc65
Fix cases bundle size
May 17, 2022
62bb244
Add more tests
cnasikas May 17, 2022
20f58cb
Merge branch 'alert_hook' of github.com:XavierM/kibana into alert_hook
cnasikas May 17, 2022
f40928b
[Fleet] Add new API to get current upgrades (#132276)
nchaulet May 17, 2022
fc6cd8c
Add support of Data View switching for Agg-Based visualizations (#132…
alexwizp May 17, 2022
6bf622a
[Security Solution] Responsive styling fixes (#131951)
stephmilovic May 17, 2022
6202f2f
[Discover] Add Analytics No Data Page (#131965)
May 17, 2022
1bf83a3
Remove barrel export from public index file
May 17, 2022
09f7ca1
remove barrel export
May 17, 2022
f8b4913
Merge remote-tracking branch 'upstream/main' into alert_hook
May 17, 2022
afb43aa
Re-export missing exports
May 17, 2022
f44390c
Turn off feature flag
May 17, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,7 @@
* 2.0.
*/

import {
EuiEmptyPrompt,
EuiFlexGroup,
EuiFlexItem,
EuiLoadingLogo,
EuiSpacer,
EuiTab,
EuiTabs,
} from '@elastic/eui';
import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTab, EuiTabs } from '@elastic/eui';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Case, UpdateKey } from '../../../common/ui';
import { useCaseViewNavigation, useUrlParams } from '../../common/navigation';
Expand All @@ -28,6 +20,7 @@ import { useTimelineContext } from '../timeline_context/use_timeline_context';
import { useCasesTitleBreadcrumbs } from '../use_breadcrumbs';
import { WhitePageWrapperNoBorder } from '../wrappers';
import { CaseViewActivity } from './components/case_view_activity';
import { CaseViewAlerts } from './components/case_view_alerts';
import { CaseViewMetrics } from './metrics';
import { ACTIVITY_TAB, ALERTS_TAB } from './translations';
import { CaseViewPageProps, CASE_VIEW_PAGE_TABS } from './types';
Expand All @@ -36,7 +29,7 @@ import { useOnUpdateField } from './use_on_update_field';
// This hardcoded constant is left here intentionally
// as a way to hide a wip functionality
// that will be merge in the 8.3 release.
const ENABLE_ALERTS_TAB = false;
const ENABLE_ALERTS_TAB = true;
Comment thread
XavierM marked this conversation as resolved.

export const CaseViewPage = React.memo<CaseViewPageProps>(
({
Expand Down Expand Up @@ -194,12 +187,7 @@ export const CaseViewPage = React.memo<CaseViewPageProps>(
{
id: CASE_VIEW_PAGE_TABS.ALERTS,
name: ALERTS_TAB,
content: (
<EuiEmptyPrompt
icon={<EuiLoadingLogo logo="logoKibana" size="xl" />}
title={<h2>{'Alerts table placeholder'}</h2>}
/>
),
content: <CaseViewAlerts caseData={caseData} />,
},
]
: []),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* 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 { waitFor } from '@testing-library/dom';
import { alertCommentWithIndices, basicCase } from '../../../containers/mock';
import { AppMockRenderer, createAppMockRenderer } from '../../../common/mock';
import { Case } from '../../../../common';
import { CaseViewAlerts } from './case_view_alerts';
import * as api from '../../../containers/api';

jest.mock('../../../containers/api');

const caseData: Case = {
...basicCase,
comments: [...basicCase.comments, alertCommentWithIndices],
};

describe('Case View Page activity tab', () => {
const getAlertsStateTableMock = jest.fn();
let appMockRender: AppMockRenderer;

beforeEach(() => {
appMockRender = createAppMockRenderer();
appMockRender.coreStart.triggersActionsUi.getAlertsStateTable =
getAlertsStateTableMock.mockReturnValue(<div data-test-subj="alerts-table" />);
jest.clearAllMocks();
});

it('should render the alerts table', async () => {
const result = appMockRender.render(<CaseViewAlerts caseData={caseData} />);
await waitFor(async () => {
expect(result.getByTestId('alerts-table')).toBeTruthy();
});
});

it('should call the alerts table with correct props', async () => {
appMockRender.render(<CaseViewAlerts caseData={caseData} />);
await waitFor(async () => {
expect(getAlertsStateTableMock).toHaveBeenCalledWith({
alertsTableConfigurationRegistry: expect.anything(),
configurationId: 'securitySolution',
featureIds: ['siem', 'observability'],
id: 'case-details-alerts-securitySolution',
query: {
ids: {
values: ['alert-id-1'],
},
},
});
});
});

it('should call the getFeatureIds with the correct registration context', async () => {
const getFeatureIdsMock = jest.spyOn(api, 'getFeatureIds');
appMockRender.render(<CaseViewAlerts caseData={caseData} />);
await waitFor(async () => {
expect(getFeatureIdsMock).toHaveBeenCalledWith(
{ registrationContext: ['matchme'] },
expect.anything()
);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* 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, { useMemo } from 'react';

import { Case } from '../../../../common';
import { useKibana } from '../../../common/lib/kibana';
import { getManualAlertIds, getRegistrationContextFromAlerts } from './helpers';
import { useGetFeatureIds } from '../../../containers/use_get_feature_ids';

interface CaseViewAlertsProps {
caseData: Case;
}
export const CaseViewAlerts = ({ caseData }: CaseViewAlertsProps) => {
const { triggersActionsUi } = useKibana().services;

const alertIdsQuery = useMemo(
() => ({
ids: {
values: getManualAlertIds(caseData.comments),
},
}),
[caseData.comments]
);
const alertRegistrationContexts = useMemo(
() => getRegistrationContextFromAlerts(caseData.comments),
[caseData.comments]
);

const alertFeatureIds = useGetFeatureIds(alertRegistrationContexts);

const alertStateProps = {
alertsTableConfigurationRegistry: triggersActionsUi.alertsTableConfigurationRegistry,
configurationId: caseData.owner,
id: `case-details-alerts-${caseData.owner}`,
featureIds: alertFeatureIds,
query: alertIdsQuery,
};

return <>{triggersActionsUi.getAlertsStateTable(alertStateProps)}</>;
Comment thread
academo marked this conversation as resolved.
};
CaseViewAlerts.displayName = 'CaseViewAlerts';
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* 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 { alertComment } from '../../../containers/mock';
import { getManualAlertIds, getRegistrationContextFromAlerts } from './helpers';

const comment = {
...alertComment,
alertId: 'alert-id-1',
index: '.alerts-matchme.alerts',
};
const comment2 = {
...alertComment,
alertId: 'alert-id-2',
index: '.alerts-another.alerts',
};

const comment3 = {
...alertComment,
alertId: ['nested1', 'nested2', 'nested3'],
};

const commentSiemSignal = {
...alertComment,
alertId: 'alert-id-siem',
index: '.siem-signals-default-000008',
};

const commentIsBad = {
...alertComment,
alertId: 'alert-id-bad',
index: 'bad-siem-signals-default-000008',
};

const multipleIndices = {
...alertComment,
alertId: ['test-id-1', 'test-id-2', 'test-id-3', 'test-id-4', 'test-id-5', 'test-id-6'],
index: [
'.internal.alerts-security.alerts-default-000001',
'.internal.alerts-observability.logs.alerts-default-000001',
'.internal.alerts-observability.uptime.alerts-default-000001',
'.internal.alerts-observability.metrics.alerts-default-000001',
'.internal.alerts-observability.apm.alerts-space2-000001',
'.internal.alerts-observability.logs.alerts-space1-000001',
],
};

describe('Case view helpers', () => {
describe('getRegistrationContextFromAlerts', () => {
it('returns the correct registration context', () => {
const result = getRegistrationContextFromAlerts([comment, comment2, multipleIndices]);
expect(result).toEqual([
'matchme',
'another',
'security',
'observability.logs',
'observability.uptime',
'observability.metrics',
'observability.apm',
]);
});

it('dedupes contexts', () => {
const result = getRegistrationContextFromAlerts([comment, comment]);
expect(result).toEqual(['matchme']);
});

it('returns the correct registration when find a .siem-signals* index', () => {
const result = getRegistrationContextFromAlerts([commentSiemSignal, comment2]);
expect(result).toEqual(['security', 'another']);
});

it('returns empty when the index is not formatted as expected', () => {
const result = getRegistrationContextFromAlerts([commentIsBad]);
expect(result).toEqual([]);
});
});

describe('getManualAlertIds', () => {
it('returns the alert ids', () => {
const result = getManualAlertIds([comment, comment2]);
expect(result).toEqual(['alert-id-1', 'alert-id-2']);
});

it('returns the alerts id from multiple alerts in a comment', () => {
const result = getManualAlertIds([comment, comment2, comment3]);
expect(result).toEqual(['alert-id-1', 'alert-id-2', 'nested1', 'nested2', 'nested3']);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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 { CommentType } from '../../../../common/api';
import type { Comment } from '../../../containers/types';

export const getManualAlertIds = (comments: Comment[]): string[] => {
const dedupeAlerts = comments.reduce((alertIds, comment: Comment) => {
if (comment.type === CommentType.alert) {
const ids = Array.isArray(comment.alertId) ? comment.alertId : [comment.alertId];
ids.forEach((id) => alertIds.add(id));
return alertIds;
}
return alertIds;
}, new Set<string>());
return Array.from(dedupeAlerts);
};

export const getRegistrationContextFromAlerts = (comments: Comment[]): string[] => {
const dedupeRegistrationContext = comments.reduce((registrationContexts, comment: Comment) => {
if (comment.type === CommentType.alert) {
const indices = Array.isArray(comment.index) ? comment.index : [comment.index];
indices.forEach((index) => {
// That's legacy code, we created some index alias so everything should work as expected
if (index.startsWith('.siem-signals')) {
registrationContexts.add('security');
} else {
const registrationContext = getRegistrationContextFromIndex(index);
if (registrationContext) {
registrationContexts.add(registrationContext);
}
}
});
return registrationContexts;
}
return registrationContexts;
}, new Set<string>());
return Array.from(dedupeRegistrationContext);
};

export const getRegistrationContextFromIndex = (indexName: string): string | null => {
const found = indexName.match(/\.alerts-(.*?).alerts/);
if (found && found.length > 1) {
return `${found[1]}`;
}
return null;
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import { isEmpty } from 'lodash';

import { CommentType } from '../../../common/api';
import type { Comment } from '../../containers/types';
import { SUPPORTED_ACTION_TYPES } from './constants';
Expand All @@ -23,5 +24,5 @@ export const getManualAlertIdsWithNoRuleId = (comments: Comment[]): string[] =>
}
return alertIds;
}, new Set<string>());
return [...dedupeAlerts];
return Array.from(dedupeAlerts);
};
6 changes: 6 additions & 0 deletions x-pack/plugins/cases/public/containers/__mocks__/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
CaseStatuses,
SingleCaseMetricsResponse,
} from '../../../common/api';
import type { ValidFeatureId } from '@kbn/rule-data-utils';

export const getCase = async (
caseId: string,
Expand Down Expand Up @@ -133,3 +134,8 @@ export const pushCase = async (

export const getActionLicense = async (signal: AbortSignal): Promise<ActionLicense[]> =>
Promise.resolve(actionLicenses);

export const getFeatureIds = async (
_query: { registrationContext: string[] },
_signal: AbortSignal
): Promise<ValidFeatureId[]> => Promise.resolve(['siem', 'observability']);
23 changes: 23 additions & 0 deletions x-pack/plugins/cases/public/containers/api.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import { httpServiceMock } from '@kbn/core/public/mocks';
import { BASE_RAC_ALERTS_API_PATH } from '@kbn/rule-registry-plugin/common';
import { KibanaServices } from '../common/lib/kibana';

import { ConnectorTypes, CommentType, CaseStatuses, CaseSeverity } from '../../common/api';
Expand All @@ -31,6 +32,7 @@ import {
createAttachments,
pushCase,
resolveCase,
getFeatureIds,
} from './api';

import {
Expand Down Expand Up @@ -605,4 +607,25 @@ describe('Case Configuration API', () => {
expect(resp).toBe(undefined);
});
});

describe('getFeatureIds', () => {
beforeEach(() => {
fetchMock.mockClear();
fetchMock.mockResolvedValue(['siem', 'observability']);
});

test('should be called with correct check url, method, signal', async () => {
const resp = await getFeatureIds(
{ registrationContext: ['security', 'observability.logs'] },
abortCtrl.signal
);

expect(fetchMock).toHaveBeenCalledWith(`${BASE_RAC_ALERTS_API_PATH}/_feature_ids`, {
query: { registrationContext: ['security', 'observability.logs'] },
signal: abortCtrl.signal,
});

expect(resp).toEqual(['siem', 'observability']);
});
});
});
Loading