Skip to content

Commit

Permalink
[SIEM][Exceptions] - ExceptionsViewer UI component part 2 (#68294)
Browse files Browse the repository at this point in the history
### Summary 

This PR is a follow up to #68027 . It brings it all together to complete the exceptions viewer component. This component is meant to display all exception items and allow a user to create, edit, delete, and search these exception items.

- Moves ExceptionItem (from part 1) into its own folder
- Adds exceptions_viewer_header component that includes the search, list toggle, and add exception buttons
- Adds actual ExceptionViewer component
- Updates the useExceptionList hook refresh function logic. Noticed that the previous version was creating some issues
  • Loading branch information
yctercero committed Jun 10, 2020
1 parent aec0b97 commit 8095856
Show file tree
Hide file tree
Showing 30 changed files with 2,266 additions and 254 deletions.
111 changes: 111 additions & 0 deletions x-pack/plugins/lists/public/exceptions/hooks/use_api.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* 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 { useMemo } from 'react';

import * as Api from '../api';
import { HttpStart } from '../../../../../../src/core/public';
import { ExceptionListItemSchema, ExceptionListSchema } from '../../../common/schemas';
import { ApiCallMemoProps } from '../types';

export interface ExceptionsApi {
deleteExceptionItem: (arg: ApiCallMemoProps) => Promise<void>;
deleteExceptionList: (arg: ApiCallMemoProps) => Promise<void>;
getExceptionItem: (
arg: ApiCallMemoProps & { onSuccess: (arg: ExceptionListItemSchema) => void }
) => Promise<void>;
getExceptionList: (
arg: ApiCallMemoProps & { onSuccess: (arg: ExceptionListSchema) => void }
) => Promise<void>;
}

export const useApi = (http: HttpStart): ExceptionsApi => {
return useMemo(
(): ExceptionsApi => ({
async deleteExceptionItem({
id,
namespaceType,
onSuccess,
onError,
}: ApiCallMemoProps): Promise<void> {
const abortCtrl = new AbortController();

try {
await Api.deleteExceptionListItemById({
http,
id,
namespaceType,
signal: abortCtrl.signal,
});
onSuccess();
} catch (error) {
onError(error);
}
},
async deleteExceptionList({
id,
namespaceType,
onSuccess,
onError,
}: ApiCallMemoProps): Promise<void> {
const abortCtrl = new AbortController();

try {
await Api.deleteExceptionListById({
http,
id,
namespaceType,
signal: abortCtrl.signal,
});
onSuccess();
} catch (error) {
onError(error);
}
},
async getExceptionItem({
id,
namespaceType,
onSuccess,
onError,
}: ApiCallMemoProps & { onSuccess: (arg: ExceptionListItemSchema) => void }): Promise<void> {
const abortCtrl = new AbortController();

try {
const item = await Api.fetchExceptionListItemById({
http,
id,
namespaceType,
signal: abortCtrl.signal,
});
onSuccess(item);
} catch (error) {
onError(error);
}
},
async getExceptionList({
id,
namespaceType,
onSuccess,
onError,
}: ApiCallMemoProps & { onSuccess: (arg: ExceptionListSchema) => void }): Promise<void> {
const abortCtrl = new AbortController();

try {
const list = await Api.fetchExceptionListById({
http,
id,
namespaceType,
signal: abortCtrl.signal,
});
onSuccess(list);
} catch (error) {
onError(error);
}
},
}),
[http]
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ 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 { ExceptionListAndItems, UseExceptionListProps } from '../types';
import { ExceptionListItemSchema } from '../../../common/schemas';
import { ExceptionList, UseExceptionListProps } from '../types';

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

Expand All @@ -34,15 +35,23 @@ describe('useExceptionList', () => {
>(() =>
useExceptionList({
http: mockKibanaHttpService,
id: 'myListId',
namespaceType: 'single',
lists: [{ id: 'myListId', namespaceType: 'single' }],
onError: onErrorMock,
})
);
await waitForNextUpdate();

expect(result.current).toEqual([true, null, result.current[2]]);
expect(typeof result.current[2]).toEqual('function');
expect(result.current).toEqual([
true,
[],
[],
{
page: 1,
perPage: 20,
total: 0,
},
null,
]);
});
});

Expand All @@ -54,27 +63,32 @@ describe('useExceptionList', () => {
>(() =>
useExceptionList({
http: mockKibanaHttpService,
id: 'myListId',
namespaceType: 'single',
lists: [{ id: 'myListId', namespaceType: 'single' }],
onError: onErrorMock,
})
);
await waitForNextUpdate();
await waitForNextUpdate();

const expectedResult: ExceptionListAndItems = {
...getExceptionListSchemaMock(),
exceptionItems: {
items: [{ ...getExceptionListItemSchemaMock() }],
pagination: {
page: 1,
perPage: 20,
total: 1,
},
},
};
const expectedListResult: ExceptionList[] = [
{ ...getExceptionListSchemaMock(), totalItems: 1 },
];

const expectedListItemsResult: ExceptionListItemSchema[] = [
{ ...getExceptionListItemSchemaMock() },
];

expect(result.current).toEqual([false, expectedResult, result.current[2]]);
expect(result.current).toEqual([
false,
expectedListResult,
expectedListItemsResult,
{
page: 1,
perPage: 20,
total: 1,
},
result.current[4],
]);
});
});

Expand All @@ -86,22 +100,20 @@ describe('useExceptionList', () => {
UseExceptionListProps,
ReturnExceptionListAndItems
>(
({ filterOptions, http, id, namespaceType, pagination, onError }) =>
useExceptionList({ filterOptions, http, id, namespaceType, onError, pagination }),
({ filterOptions, http, lists, pagination, onError }) =>
useExceptionList({ filterOptions, http, lists, onError, pagination }),
{
initialProps: {
http: mockKibanaHttpService,
id: 'myListId',
namespaceType: 'single',
lists: [{ id: 'myListId', namespaceType: 'single' }],
onError: onErrorMock,
},
}
);
await waitForNextUpdate();
rerender({
http: mockKibanaHttpService,
id: 'newListId',
namespaceType: 'single',
lists: [{ id: 'newListId', namespaceType: 'single' }],
onError: onErrorMock,
});
await waitForNextUpdate();
Expand All @@ -121,14 +133,19 @@ describe('useExceptionList', () => {
>(() =>
useExceptionList({
http: mockKibanaHttpService,
id: 'myListId',
namespaceType: 'single',
lists: [{ id: 'myListId', namespaceType: 'single' }],
onError: onErrorMock,
})
);
await waitForNextUpdate();
await waitForNextUpdate();
result.current[2]();

expect(typeof result.current[4]).toEqual('function');

if (result.current[4] != null) {
result.current[4]();
}

await waitForNextUpdate();

expect(spyOnfetchExceptionListById).toHaveBeenCalledTimes(2);
Expand All @@ -147,8 +164,7 @@ describe('useExceptionList', () => {
() =>
useExceptionList({
http: mockKibanaHttpService,
id: 'myListId',
namespaceType: 'single',
lists: [{ id: 'myListId', namespaceType: 'single' }],
onError: onErrorMock,
})
);
Expand All @@ -170,8 +186,7 @@ describe('useExceptionList', () => {
() =>
useExceptionList({
http: mockKibanaHttpService,
id: 'myListId',
namespaceType: 'single',
lists: [{ id: 'myListId', namespaceType: 'single' }],
onError: onErrorMock,
})
);
Expand Down
Loading

0 comments on commit 8095856

Please sign in to comment.