Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SIEM][Exceptions] - ExceptionsViewer cleanup #68739

Merged
merged 39 commits into from
Jun 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
24ed91f
moved and_or_badge to common folder and added tests
yctercero Jun 2, 2020
629b832
updated references to new and_or_badge location
yctercero Jun 2, 2020
335a720
built out exception item component for viewer
yctercero Jun 2, 2020
dd2b8c8
added exception item component stories for easy testing
yctercero Jun 2, 2020
990d656
updated text to use i18n in spots missed
yctercero Jun 2, 2020
bac11c9
added tests for exception detail component and updated braking tests
yctercero Jun 2, 2020
9496085
finished adding tests and fixing lint issue
yctercero Jun 3, 2020
f3a1fba
fixed test timestamps to be UTC
yctercero Jun 3, 2020
a8a68fa
fix failing test that was passing locally
yctercero Jun 3, 2020
f0aeda8
tests
yctercero Jun 3, 2020
b19237f
fix dates
yctercero Jun 3, 2020
3a31452
fixes per feedback
yctercero Jun 3, 2020
7ad2a23
Merge branch 'master' of github.com:yctercero/kibana into all-exc-view
yctercero Jun 3, 2020
053c5d7
cleanup
yctercero Jun 3, 2020
c24c1ff
updated tests
yctercero Jun 3, 2020
8b91e10
Merge branch 'master' of github.com:yctercero/kibana into all-exc-view
yctercero Jun 4, 2020
878547e
updated translations to use securitySolution after rename
yctercero Jun 4, 2020
410ecb9
Merge branch 'all-exc-view' of https://github.com/yctercero/kibana in…
yctercero Jun 4, 2020
bc7b81e
updated useExceptionList hook to use different refresh pattern, updat…
yctercero Jun 4, 2020
798b1dd
moved exception item component into its own folder
yctercero Jun 4, 2020
29810e7
added search bar, toggle, and buttons
yctercero Jun 4, 2020
da467bf
added exceptions viewer component
yctercero Jun 4, 2020
c953d0f
Merge branch 'master' of github.com:yctercero/kibana into exceptions-…
yctercero Jun 4, 2020
32f66d2
fix merge issue
yctercero Jun 4, 2020
8de7469
some cleanup
yctercero Jun 4, 2020
c333567
modified list scripts for help with testing
yctercero Jun 5, 2020
8a51155
added exceptions hook for general api usage, updated exception item
yctercero Jun 8, 2020
9196387
updated per feedback to allow for different filtering
yctercero Jun 8, 2020
3117815
Merge branch 'master' of github.com:yctercero/kibana into exceptions-…
yctercero Jun 8, 2020
aea3b16
temp disabled complexity linter for rules/details/index, this file is…
yctercero Jun 8, 2020
854892a
cleanup
yctercero Jun 8, 2020
5303cc9
updated hook tests and clean up
yctercero Jun 8, 2020
6027cbb
updated and added more tests, cleaned up linter issues and per page s…
yctercero Jun 9, 2020
d25ccdf
updating viewer ux per feedback
yctercero Jun 9, 2020
700e6bf
added tests and cleaned up
yctercero Jun 10, 2020
22d3e09
Merge branch 'master' of github.com:yctercero/kibana into exception-v…
yctercero Jun 10, 2020
b4b61a8
cleaned up viewer index component
yctercero Jun 10, 2020
ad1dbbb
Merge branch 'master' of github.com:yctercero/kibana into exception-v…
yctercero Jun 10, 2020
5cde937
updated stories and unit tests
yctercero Jun 10, 2020
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
15 changes: 15 additions & 0 deletions x-pack/plugins/lists/public/exceptions/__mocks__/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,18 @@ export const fetchExceptionListItemById = async ({
signal,
}: ApiCallByIdProps): Promise<ExceptionListItemSchema> =>
Promise.resolve(getExceptionListItemSchemaMock());

export const deleteExceptionListById = async ({
http,
id,
namespaceType,
signal,
}: ApiCallByIdProps): Promise<ExceptionListSchema> => Promise.resolve(getExceptionListSchemaMock());

export const deleteExceptionListItemById = async ({
http,
id,
namespaceType,
signal,
}: ApiCallByIdProps): Promise<ExceptionListItemSchema> =>
Promise.resolve(getExceptionListItemSchemaMock());
257 changes: 257 additions & 0 deletions x-pack/plugins/lists/public/exceptions/hooks/use_api.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { act, renderHook } from '@testing-library/react-hooks';

import * as api from '../api';
import { createKibanaCoreStartMock } from '../../common/mocks/kibana_core';
import { getExceptionListSchemaMock } from '../../../common/schemas/response/exception_list_schema.mock';
import { getExceptionListItemSchemaMock } from '../../../common/schemas/response/exception_list_item_schema.mock';
import { HttpStart } from '../../../../../../src/core/public';
import { ApiCallByIdProps } from '../types';

import { ExceptionsApi, useApi } from './use_api';

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

const mockKibanaHttpService = createKibanaCoreStartMock().http;

describe('useApi', () => {
const onErrorMock = jest.fn();

afterEach(() => {
onErrorMock.mockClear();
jest.clearAllMocks();
});

test('it invokes "deleteExceptionListItemById" when "deleteExceptionItem" used', async () => {
const payload = getExceptionListItemSchemaMock();
const onSuccessMock = jest.fn();
const spyOnDeleteExceptionListItemById = jest
.spyOn(api, 'deleteExceptionListItemById')
.mockResolvedValue(payload);

await act(async () => {
const { result, waitForNextUpdate } = renderHook<HttpStart, ExceptionsApi>(() =>
useApi(mockKibanaHttpService)
);
await waitForNextUpdate();

const { id, namespace_type: namespaceType } = payload;

await result.current.deleteExceptionItem({
id,
namespaceType,
onError: jest.fn(),
onSuccess: onSuccessMock,
});

const expected: ApiCallByIdProps = {
http: mockKibanaHttpService,
id,
namespaceType,
signal: new AbortController().signal,
};

expect(spyOnDeleteExceptionListItemById).toHaveBeenCalledWith(expected);
expect(onSuccessMock).toHaveBeenCalled();
});
});

test('invokes "onError" callback if "deleteExceptionListItemById" fails', async () => {
const mockError = new Error('failed to delete item');
jest.spyOn(api, 'deleteExceptionListItemById').mockRejectedValue(mockError);

await act(async () => {
const { result, waitForNextUpdate } = renderHook<HttpStart, ExceptionsApi>(() =>
useApi(mockKibanaHttpService)
);
await waitForNextUpdate();

const { id, namespace_type: namespaceType } = getExceptionListItemSchemaMock();

await result.current.deleteExceptionItem({
id,
namespaceType,
onError: onErrorMock,
onSuccess: jest.fn(),
});

expect(onErrorMock).toHaveBeenCalledWith(mockError);
});
});

test('it invokes "deleteExceptionListById" when "deleteExceptionList" used', async () => {
const payload = getExceptionListSchemaMock();
const onSuccessMock = jest.fn();
const spyOnDeleteExceptionListById = jest
.spyOn(api, 'deleteExceptionListById')
.mockResolvedValue(payload);

await act(async () => {
const { result, waitForNextUpdate } = renderHook<HttpStart, ExceptionsApi>(() =>
useApi(mockKibanaHttpService)
);
await waitForNextUpdate();

const { id, namespace_type: namespaceType } = payload;

await result.current.deleteExceptionList({
id,
namespaceType,
onError: jest.fn(),
onSuccess: onSuccessMock,
});

const expected: ApiCallByIdProps = {
http: mockKibanaHttpService,
id,
namespaceType,
signal: new AbortController().signal,
};

expect(spyOnDeleteExceptionListById).toHaveBeenCalledWith(expected);
expect(onSuccessMock).toHaveBeenCalled();
});
});

test('invokes "onError" callback if "deleteExceptionListById" fails', async () => {
const mockError = new Error('failed to delete item');
jest.spyOn(api, 'deleteExceptionListById').mockRejectedValue(mockError);

await act(async () => {
const { result, waitForNextUpdate } = renderHook<HttpStart, ExceptionsApi>(() =>
useApi(mockKibanaHttpService)
);
await waitForNextUpdate();

const { id, namespace_type: namespaceType } = getExceptionListSchemaMock();

await result.current.deleteExceptionList({
id,
namespaceType,
onError: onErrorMock,
onSuccess: jest.fn(),
});

expect(onErrorMock).toHaveBeenCalledWith(mockError);
});
});

test('it invokes "fetchExceptionListItemById" when "getExceptionItem" used', async () => {
const payload = getExceptionListItemSchemaMock();
const onSuccessMock = jest.fn();
const spyOnFetchExceptionListItemById = jest
.spyOn(api, 'fetchExceptionListItemById')
.mockResolvedValue(payload);

await act(async () => {
const { result, waitForNextUpdate } = renderHook<HttpStart, ExceptionsApi>(() =>
useApi(mockKibanaHttpService)
);
await waitForNextUpdate();

const { id, namespace_type: namespaceType } = payload;

await result.current.getExceptionItem({
id,
namespaceType,
onError: jest.fn(),
onSuccess: onSuccessMock,
});

const expected: ApiCallByIdProps = {
http: mockKibanaHttpService,
id,
namespaceType,
signal: new AbortController().signal,
};

expect(spyOnFetchExceptionListItemById).toHaveBeenCalledWith(expected);
expect(onSuccessMock).toHaveBeenCalled();
});
});

test('invokes "onError" callback if "fetchExceptionListItemById" fails', async () => {
const mockError = new Error('failed to delete item');
jest.spyOn(api, 'fetchExceptionListItemById').mockRejectedValue(mockError);

await act(async () => {
const { result, waitForNextUpdate } = renderHook<HttpStart, ExceptionsApi>(() =>
useApi(mockKibanaHttpService)
);
await waitForNextUpdate();

const { id, namespace_type: namespaceType } = getExceptionListSchemaMock();

await result.current.getExceptionItem({
id,
namespaceType,
onError: onErrorMock,
onSuccess: jest.fn(),
});

expect(onErrorMock).toHaveBeenCalledWith(mockError);
});
});

test('it invokes "fetchExceptionListById" when "getExceptionList" used', async () => {
const payload = getExceptionListSchemaMock();
const onSuccessMock = jest.fn();
const spyOnFetchExceptionListById = jest
.spyOn(api, 'fetchExceptionListById')
.mockResolvedValue(payload);

await act(async () => {
const { result, waitForNextUpdate } = renderHook<HttpStart, ExceptionsApi>(() =>
useApi(mockKibanaHttpService)
);
await waitForNextUpdate();

const { id, namespace_type: namespaceType } = payload;

await result.current.getExceptionList({
id,
namespaceType,
onError: jest.fn(),
onSuccess: onSuccessMock,
});

const expected: ApiCallByIdProps = {
http: mockKibanaHttpService,
id,
namespaceType,
signal: new AbortController().signal,
};

expect(spyOnFetchExceptionListById).toHaveBeenCalledWith(expected);
expect(onSuccessMock).toHaveBeenCalled();
});
});

test('invokes "onError" callback if "fetchExceptionListById" fails', async () => {
const mockError = new Error('failed to delete item');
jest.spyOn(api, 'fetchExceptionListById').mockRejectedValue(mockError);

await act(async () => {
const { result, waitForNextUpdate } = renderHook<HttpStart, ExceptionsApi>(() =>
useApi(mockKibanaHttpService)
);
await waitForNextUpdate();

const { id, namespace_type: namespaceType } = getExceptionListSchemaMock();

await result.current.getExceptionList({
id,
namespaceType,
onError: onErrorMock,
onSuccess: jest.fn(),
});

expect(onErrorMock).toHaveBeenCalledWith(mockError);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { createKibanaCoreStartMock } from '../../common/mocks/kibana_core';
import { getExceptionListSchemaMock } from '../../../common/schemas/response/exception_list_schema.mock';
import { getExceptionListItemSchemaMock } from '../../../common/schemas/response/exception_list_item_schema.mock';
import { ExceptionListItemSchema } from '../../../common/schemas';
import { ExceptionList, UseExceptionListProps } from '../types';
import { ExceptionList, UseExceptionListProps, UseExceptionListSuccess } from '../types';

import { ReturnExceptionListAndItems, useExceptionList } from './use_exception_list';

Expand Down Expand Up @@ -57,6 +57,7 @@ describe('useExceptionList', () => {

test('fetch exception list and items', async () => {
await act(async () => {
const onSuccessMock = jest.fn();
const { result, waitForNextUpdate } = renderHook<
UseExceptionListProps,
ReturnExceptionListAndItems
Expand All @@ -65,6 +66,7 @@ describe('useExceptionList', () => {
http: mockKibanaHttpService,
lists: [{ id: 'myListId', namespaceType: 'single' }],
onError: onErrorMock,
onSuccess: onSuccessMock,
})
);
await waitForNextUpdate();
Expand All @@ -78,6 +80,12 @@ describe('useExceptionList', () => {
{ ...getExceptionListItemSchemaMock() },
];

const expectedResult: UseExceptionListSuccess = {
exceptions: expectedListItemsResult,
lists: expectedListResult,
pagination: { page: 1, perPage: 20, total: 1 },
};

expect(result.current).toEqual([
false,
expectedListResult,
Expand All @@ -89,6 +97,7 @@ describe('useExceptionList', () => {
},
result.current[4],
]);
expect(onSuccessMock).toHaveBeenCalledWith(expectedResult);
});
});

Expand All @@ -100,13 +109,14 @@ describe('useExceptionList', () => {
UseExceptionListProps,
ReturnExceptionListAndItems
>(
({ filterOptions, http, lists, pagination, onError }) =>
useExceptionList({ filterOptions, http, lists, onError, pagination }),
({ filterOptions, http, lists, pagination, onError, onSuccess }) =>
useExceptionList({ filterOptions, http, lists, onError, onSuccess, pagination }),
{
initialProps: {
http: mockKibanaHttpService,
lists: [{ id: 'myListId', namespaceType: 'single' }],
onError: onErrorMock,
onSuccess: jest.fn(),
},
}
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ export type ReturnExceptionListAndItems = [
* Hook for using to get an ExceptionList and it's ExceptionListItems
*
* @param http Kibana http service
* @param id desired ExceptionList ID (not list_id)
* @param namespaceType list namespaceType determines list space
* @param lists array of ExceptionIdentifiers for all lists to fetch
* @param onError error callback
* @param onSuccess callback when all lists fetched successfully
* @param filterOptions optional - filter by fields or tags
* @param pagination optional
*
Expand All @@ -43,7 +43,7 @@ export const useExceptionList = ({
tags: [],
},
onError,
dispatchListsInReducer,
onSuccess,
}: UseExceptionListProps): ReturnExceptionListAndItems => {
const [exceptionLists, setExceptionLists] = useState<ExceptionList[]>([]);
const [exceptionItems, setExceptionListItems] = useState<ExceptionListItemSchema[]>([]);
Expand Down Expand Up @@ -116,8 +116,8 @@ export const useExceptionList = ({
exceptions = [...exceptions, ...fetchListItemsResult.data];
setExceptionListItems(exceptions);

if (dispatchListsInReducer != null) {
dispatchListsInReducer({
if (onSuccess != null) {
onSuccess({
exceptions,
lists: exceptionListsReturned,
pagination: {
Expand Down
Loading