Skip to content

Commit

Permalink
[RAM] Alert Table from triggers_actions_ui plugin (#131883)
Browse files Browse the repository at this point in the history
* wip I

* add alert table state in case

* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'

* add new API to get FeatureID form registrationContext and update UI to use this new API

* rm dead code

* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'

* remove unnecessary memo

* adds tests for case view helpers

* Move http call to API and add tests for getFeatureIds

* fix type + unit test

* add unit tests + cleanup

* add new api integration test for _feature_ids

* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'

* Fix small type creating typescript slowness

* remove console log

* use import type for validfeatureId

* force any to improve typescript performance

* Update APM (#132270)

Co-authored-by: Renovate Bot <[email protected]>

* [ResponseOps][Docs] Updating ServiceNow docs with OAuth setup instructions (#131344)

* Updating ServiceNow docs. Need screenshots

* Adding screenshots

* Fix nested screenshots and lists

* Tweaks and screenshots

* Updates

* blergh

* Apply suggestions from code review

Co-authored-by: Lisa Cawley <[email protected]>

* Apply suggestions from code review

Co-authored-by: Mike Côté <[email protected]>

Co-authored-by: lcawl <[email protected]>
Co-authored-by: Kibana Machine <[email protected]>
Co-authored-by: Mike Côté <[email protected]>

* Show polling options when 'Data streams' option is selected in the Console Settings modal. (#132277)

* [Osquery] Make Osquery All with All base privillege (#130523)

* [XY] Add normalizeTable function to correct works with esdocs (#131917)

* Add normalizeTable function to correct works with esdocs

* Fix types

* Fix types

* Fix CI

* Fix CI

* Some fixes

* Remove fallback with min/max value for domain

* Added tests

* Some refactoring

Co-authored-by: Kibana Machine <[email protected]>
Co-authored-by: Yaroslav Kuznietsov <[email protected]>

* [Osquery] Add default osquery_saved_query objects (#129461)

* [Unified Search] Show error message for invalid date filter value (#131290)

* feat: added show error message for invalid date

* refact: move logic in HOC

* feat: refactoring code and added translation

* refact show error

* refact: show error message

* refact: remove translation

* refactor: changed menu for show FilterEdit

* fix: open/close popover

* feat: field.type => KBN_FIELD_TYPES

* feat: remove extra code with with input check and refactored filter item

* feat: added tests and refactoring code

* refact: getFieldValidityAndErrorMessage

* feat: return isInvalid checking in valur input type for string, ip

* Update navigation landing pages to use appLinks config (#132027)

* Update navigation landing pages to use appLinks config

* Please code review

* align app links changes

* Update links descriptions

* Rollback title changes

* Fix wrong links descriptions

* Fix unit tests

* Fix description

Co-authored-by: semd <[email protected]>

* [Cloud Posture] add resource findings page flyout  (#132243)

* [Discover] Add a tour for Document Explorer (#131125)

* [Discover] Add "Take a tour" button to the Document Explorer callout

* [Discover] Tmp

* [Discover] Add a first Document Explorer tour step

* [Discover] Add other Document Explorer tour steps

* [Discover] Update tour steps positioning

* [Discover] Add gifs to tour steps

* [Discover] Refactor how tour steps are registered

* [Discover] Add new step to the tour. Update tour steps text.

* [Discover] Improve steps positioning

* [Discover] Fix positioning for Add field step

* [Discover] Add icons to tour steps

* [Discover] Reorganize components

* [Discover] Skip Columns step when it's not available

* [Discover] Rename components

* [Discover] Add some tests

* [Discover] Fix positioning

* [Discover] Fix props

* [Discover] Render steps only if the tour is active

* [Discover] Update gifs

* [Discover] Add image alt text for gifs

* [Discover] Tag the Take tour button

* [Discover] Update text and tests

* [Discover] Add more tests

* [Discover] Rename assets directory

* [Discover] Fix tour in mobile view. Improve steps positioning and animation.

* [Discover] Update text in tour steps

* [Discover] Update sort.gif

* [Discover] Update image width

* Update src/plugins/discover/public/components/discover_tour/discover_tour_provider.tsx

Co-authored-by: gchaps <[email protected]>

* Update src/plugins/discover/public/components/discover_tour/discover_tour_provider.tsx

Co-authored-by: gchaps <[email protected]>

* [Discover] Update sort.gif

* [Discover] Fix code style

Co-authored-by: gchaps <[email protected]>
Co-authored-by: Kibana Machine <[email protected]>

* [XY] Add `minTimeBarInterval` arg (#128726)

* Added `xAxisInterval` arg

* Add validation

* Add tests

* Rename xAxisInterval to minTimeBarInterval and add validation

* Fix imports

* Add tests to validation

* Fix conflicts

* [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix'

* Fix tests

Co-authored-by: Kibana Machine <[email protected]>

* do not use barrel imports

* do not use barrel import

* do not use barrel import

* do not use barrel imports

* do not use barrel import

* import types

* Add tests

* Fix cases bundle size

* Add more tests

* [Fleet] Add new API to get current upgrades (#132276)

* Add support of Data View switching for Agg-Based visualizations (#132184)

* Add support of Data View switching for Agg-Based visualizations

* fix CI

* add use_date_view_updates

* implement sync with state

* cleanup

* cleanup

* cleanup

* Update index.ts

* fix PR comments

* Update use_data_view_updates.ts

* Update use_data_view_updates.ts

Co-authored-by: Kibana Machine <[email protected]>

* [Security Solution] Responsive styling fixes (#131951)

* [Discover] Add Analytics No Data Page (#131965)

* [Discover] Add Analytics No Data Page

* Make showEmptyPrompt parameter optional

* Remove unused import

* Remove unnecessary test

* Fix test

* Update failing test?

* Update failing test

* Changing the order of functional tests

* Fix error handling

* Addressing PR comments

Co-authored-by: Kibana Machine <[email protected]>

* Remove barrel export from public index file

* remove barrel export

* Re-export missing exports

* Turn off feature flag

Co-authored-by: kibanamachine <[email protected]>
Co-authored-by: Esteban Beltran <[email protected]>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Renovate Bot <[email protected]>
Co-authored-by: Ying Mao <[email protected]>
Co-authored-by: lcawl <[email protected]>
Co-authored-by: Mike Côté <[email protected]>
Co-authored-by: CJ Cenizal <[email protected]>
Co-authored-by: Tomasz Ciecierski <[email protected]>
Co-authored-by: Uladzislau Lasitsa <[email protected]>
Co-authored-by: Yaroslav Kuznietsov <[email protected]>
Co-authored-by: Nodir Latipov <[email protected]>
Co-authored-by: Pablo Machado <[email protected]>
Co-authored-by: semd <[email protected]>
Co-authored-by: Or Ouziel <[email protected]>
Co-authored-by: Julia Rechkunova <[email protected]>
Co-authored-by: gchaps <[email protected]>
Co-authored-by: Christos Nasikas <[email protected]>
Co-authored-by: Nicolas Chaulet <[email protected]>
Co-authored-by: Alexey Antonov <[email protected]>
Co-authored-by: Steph Milovic <[email protected]>
Co-authored-by: Maja Grubic <[email protected]>
  • Loading branch information
1 parent ad60119 commit 859a795
Show file tree
Hide file tree
Showing 47 changed files with 2,045 additions and 160 deletions.
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;

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)}</>;
};
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

0 comments on commit 859a795

Please sign in to comment.