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

Add a flyout to alert list. #57926

Merged
merged 23 commits into from
Feb 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion x-pack/plugins/endpoint/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export interface EndpointResultList {
}

export interface AlertData {
'@timestamp': Date;
'@timestamp': string;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agent: {
id: string;
version: string;
Expand Down
15 changes: 3 additions & 12 deletions x-pack/plugins/endpoint/public/applications/endpoint/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,14 @@ import * as React from 'react';
import ReactDOM from 'react-dom';
import { CoreStart, AppMountParameters } from 'kibana/public';
import { I18nProvider, FormattedMessage } from '@kbn/i18n/react';
import { Route, Switch, BrowserRouter, useLocation } from 'react-router-dom';
import { Provider, useDispatch } from 'react-redux';
import { Route, Switch, BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { Store } from 'redux';
import { memo } from 'react';
import { RouteCapture } from './view/route_capture';
import { appStoreFactory } from './store';
import { AlertIndex } from './view/alerts';
import { ManagementList } from './view/managing';
import { PolicyList } from './view/policy';
import { AppAction } from './store/action';
import { EndpointAppLocation } from './types';

/**
* This module will be loaded asynchronously to reduce the bundle size of your plugin's main bundle.
Expand All @@ -33,13 +31,6 @@ export function renderApp(coreStart: CoreStart, { appBasePath, element }: AppMou
};
}

const RouteCapture = memo(({ children }) => {
const location: EndpointAppLocation = useLocation();
const dispatch: (action: AppAction) => unknown = useDispatch();
dispatch({ type: 'userChangedUrl', payload: location });
return <>{children}</>;
});

interface RouterProps {
basename: string;
store: Store;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { coreMock } from 'src/core/public/mocks';
import { AlertResultList } from '../../../../../common/types';
import { isOnAlertPage } from './selectors';
import { createBrowserHistory } from 'history';
import { mockAlertResultList } from './mock_alert_result_list';

describe('alert list tests', () => {
let store: Store<AlertListState, AppAction>;
Expand All @@ -28,37 +29,7 @@ describe('alert list tests', () => {
describe('when the user navigates to the alert list page', () => {
beforeEach(() => {
coreStart.http.get.mockImplementation(async () => {
const response: AlertResultList = {
alerts: [
{
'@timestamp': new Date(1542341895000),
agent: {
id: 'ced9c68e-b94a-4d66-bb4c-6106514f0a2f',
version: '3.0.0',
},
event: {
action: 'open',
},
file_classification: {
malware_classification: {
score: 3,
},
},
host: {
hostname: 'HD-c15-bc09190a',
ip: '10.179.244.14',
os: {
name: 'Windows',
},
},
thread: {},
},
],
total: 1,
request_page_size: 10,
request_page_index: 0,
result_from_index: 0,
};
const response: AlertResultList = mockAlertResultList();
return response;
});

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whitespace

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,47 @@
import { Store, createStore, applyMiddleware } from 'redux';
import { History } from 'history';
import { alertListReducer } from './reducer';
import { AlertListState } from '../../types';
import { AlertListState, AlertingIndexUIQueryParams } from '../../types';
import { alertMiddlewareFactory } from './middleware';
import { AppAction } from '../action';
import { coreMock } from 'src/core/public/mocks';
import { createBrowserHistory } from 'history';
import {
urlFromNewPageSizeParam,
paginationDataFromUrl,
urlFromNewPageIndexParam,
} from './selectors';
import { uiQueryParams } from './selectors';
import { urlFromQueryParams } from '../../view/alerts/url_from_query_params';

describe('alert list pagination', () => {
let store: Store<AlertListState, AppAction>;
let coreStart: ReturnType<typeof coreMock.createStart>;
let history: History<never>;
let queryParams: () => AlertingIndexUIQueryParams;
/**
* Update the history with a new `AlertingIndexUIQueryParams`
*/
let historyPush: (params: AlertingIndexUIQueryParams) => void;
beforeEach(() => {
coreStart = coreMock.createStart();
history = createBrowserHistory();

const middleware = alertMiddlewareFactory(coreStart);
store = createStore(alertListReducer, applyMiddleware(middleware));

history.listen(location => {
store.dispatch({ type: 'userChangedUrl', payload: location });
});

queryParams = () => uiQueryParams(store.getState());

historyPush = (nextQueryParams: AlertingIndexUIQueryParams): void => {
return history.push(urlFromQueryParams(nextQueryParams));
};
});
describe('when the user navigates to the alert list page', () => {
describe('when a new page size is passed', () => {
beforeEach(() => {
const urlPageSizeSelector = urlFromNewPageSizeParam(store.getState());
history.push(urlPageSizeSelector(1));
store.dispatch({ type: 'userChangedUrl', payload: history.location });
historyPush({ ...queryParams(), page_size: '1' });
});
it('should modify the url correctly', () => {
const actualPaginationQuery = paginationDataFromUrl(store.getState());
expect(actualPaginationQuery).toMatchInlineSnapshot(`
expect(queryParams()).toMatchInlineSnapshot(`
Object {
"page_size": "1",
}
Expand All @@ -46,13 +56,10 @@ describe('alert list pagination', () => {

describe('and then a new page index is passed', () => {
beforeEach(() => {
const urlPageIndexSelector = urlFromNewPageIndexParam(store.getState());
history.push(urlPageIndexSelector(1));
store.dispatch({ type: 'userChangedUrl', payload: history.location });
historyPush({ ...queryParams(), page_index: '1' });
});
it('should modify the url in the correct order', () => {
const actualPaginationQuery = paginationDataFromUrl(store.getState());
expect(actualPaginationQuery).toMatchInlineSnapshot(`
expect(queryParams()).toMatchInlineSnapshot(`
Object {
"page_index": "1",
"page_size": "1",
Expand All @@ -64,35 +71,15 @@ describe('alert list pagination', () => {

describe('when a new page index is passed', () => {
beforeEach(() => {
const urlPageIndexSelector = urlFromNewPageIndexParam(store.getState());
history.push(urlPageIndexSelector(1));
store.dispatch({ type: 'userChangedUrl', payload: history.location });
historyPush({ ...queryParams(), page_index: '1' });
});
it('should modify the url correctly', () => {
const actualPaginationQuery = paginationDataFromUrl(store.getState());
expect(actualPaginationQuery).toMatchInlineSnapshot(`
expect(queryParams()).toMatchInlineSnapshot(`
Object {
"page_index": "1",
}
`);
});

describe('and then a new page size is passed', () => {
beforeEach(() => {
const urlPageSizeSelector = urlFromNewPageSizeParam(store.getState());
history.push(urlPageSizeSelector(1));
store.dispatch({ type: 'userChangedUrl', payload: history.location });
});
it('should modify the url correctly and reset index to `0`', () => {
const actualPaginationQuery = paginationDataFromUrl(store.getState());
expect(actualPaginationQuery).toMatchInlineSnapshot(`
Object {
"page_index": "0",
"page_size": "1",
}
`);
});
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,18 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { HttpFetchQuery } from 'kibana/public';
import { AlertResultList } from '../../../../../common/types';
import { AppAction } from '../action';
import { MiddlewareFactory, AlertListState } from '../../types';
import { isOnAlertPage, paginationDataFromUrl } from './selectors';
import { isOnAlertPage, apiQueryParams } from './selectors';

export const alertMiddlewareFactory: MiddlewareFactory<AlertListState> = coreStart => {
return api => next => async (action: AppAction) => {
next(action);
const state = api.getState();
if (action.type === 'userChangedUrl' && isOnAlertPage(state)) {
const response: AlertResultList = await coreStart.http.get(`/api/endpoint/alerts`, {
query: paginationDataFromUrl(state) as HttpFetchQuery,
query: apiQueryParams(state),
});
api.dispatch({ type: 'serverReturnedAlertsData', payload: response });
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* 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 { AlertResultList } from '../../../../../common/types';

export const mockAlertResultList: (options?: {
total?: number;
request_page_size?: number;
request_page_index?: number;
}) => AlertResultList = (options = {}) => {
const {
total = 1,
request_page_size: requestPageSize = 10,
request_page_index: requestPageIndex = 0,
} = options;

// Skip any that are before the page we're on
const numberToSkip = requestPageSize * requestPageIndex;

// total - numberToSkip is the count of non-skipped ones, but return no more than a pageSize, and no less than 0
const actualCountToReturn = Math.max(Math.min(total - numberToSkip, requestPageSize), 0);

const alerts = [];
for (let index = 0; index < actualCountToReturn; index++) {
alerts.push({
'@timestamp': new Date(1542341895000).toString(),
agent: {
id: 'ced9c68e-b94a-4d66-bb4c-6106514f0a2f',
version: '3.0.0',
},
event: {
action: 'open',
},
file_classification: {
malware_classification: {
score: 3,
},
},
host: {
hostname: 'HD-c15-bc09190a',
ip: '10.179.244.14',
os: {
name: 'Windows',
},
},
thread: {},
});
}
const mock: AlertResultList = {
alerts,
total,
request_page_size: requestPageSize,
request_page_index: requestPageIndex,
result_from_index: 0,
};
return mock;
};
Loading