=> {
+ const queryParams = {
dateRangeStart,
dateRangeEnd,
...(filters && { filters }),
...(statusFilter && { statusFilter }),
};
- const urlParams = new URLSearchParams(params).toString();
- const response = await fetch(`${url}?${urlParams}`);
- if (!response.ok) {
- throw new Error(response.statusText);
- }
- const responseData = await response.json();
- const decoded = SnapshotType.decode(responseData);
- ThrowReporter.report(decoded);
- if (isRight(decoded)) {
- return decoded.right;
- }
- throw new Error('`getSnapshotCount` response did not correspond to expected type');
+
+ return await apiService.get(API_URLS.SNAPSHOT_COUNT, queryParams, SnapshotType);
};
diff --git a/x-pack/legacy/plugins/uptime/public/state/api/types.ts b/x-pack/legacy/plugins/uptime/public/state/api/types.ts
index a148f1c7d7ae3..4232751cbc032 100644
--- a/x-pack/legacy/plugins/uptime/public/state/api/types.ts
+++ b/x-pack/legacy/plugins/uptime/public/state/api/types.ts
@@ -5,7 +5,6 @@
*/
export interface BaseParams {
- basePath: string;
dateStart: string;
dateEnd: string;
filters?: string;
@@ -14,4 +13,4 @@ export interface BaseParams {
monitorId?: string;
}
-export type APIFn = (params: { basePath: string } & P) => Promise;
+export type APIFn = (params: P) => Promise;
diff --git a/x-pack/legacy/plugins/uptime/public/state/api/utils.ts b/x-pack/legacy/plugins/uptime/public/state/api/utils.ts
new file mode 100644
index 0000000000000..e67efa8570c11
--- /dev/null
+++ b/x-pack/legacy/plugins/uptime/public/state/api/utils.ts
@@ -0,0 +1,80 @@
+/*
+ * 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 { PathReporter } from 'io-ts/lib/PathReporter';
+import { isRight } from 'fp-ts/lib/Either';
+import { HttpFetchQuery, HttpSetup } from '../../../../../../../target/types/core/public';
+
+class ApiService {
+ private static instance: ApiService;
+ private _http!: HttpSetup;
+
+ public get http() {
+ return this._http;
+ }
+
+ public set http(httpSetup: HttpSetup) {
+ this._http = httpSetup;
+ }
+
+ private constructor() {}
+
+ static getInstance(): ApiService {
+ if (!ApiService.instance) {
+ ApiService.instance = new ApiService();
+ }
+
+ return ApiService.instance;
+ }
+
+ public async get(apiUrl: string, params?: HttpFetchQuery, decodeType?: any) {
+ const response = await this._http!.get(apiUrl, { query: params });
+
+ if (decodeType) {
+ const decoded = decodeType.decode(response);
+ if (isRight(decoded)) {
+ return decoded.right;
+ } else {
+ // eslint-disable-next-line no-console
+ console.error(
+ `API ${apiUrl} is not returning expected response, ${PathReporter.report(decoded)}`
+ );
+ }
+ }
+
+ return response;
+ }
+
+ public async post(apiUrl: string, data?: any, decodeType?: any) {
+ const response = await this._http!.post(apiUrl, {
+ method: 'POST',
+ body: JSON.stringify(data),
+ });
+
+ if (decodeType) {
+ const decoded = decodeType.decode(response);
+ if (isRight(decoded)) {
+ return decoded.right;
+ } else {
+ // eslint-disable-next-line no-console
+ console.warn(
+ `API ${apiUrl} is not returning expected response, ${PathReporter.report(decoded)}`
+ );
+ }
+ }
+ return response;
+ }
+
+ public async delete(apiUrl: string) {
+ const response = await this._http!.delete(apiUrl);
+ if (response instanceof Error) {
+ throw response;
+ }
+ return response;
+ }
+}
+
+export const apiService = ApiService.getInstance();
diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/fetch_effect.ts b/x-pack/legacy/plugins/uptime/public/state/effects/fetch_effect.ts
index ea389ff0a6745..d1d7626b2eab3 100644
--- a/x-pack/legacy/plugins/uptime/public/state/effects/fetch_effect.ts
+++ b/x-pack/legacy/plugins/uptime/public/state/effects/fetch_effect.ts
@@ -4,9 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { call, put, select } from 'redux-saga/effects';
+import { call, put } from 'redux-saga/effects';
import { Action } from 'redux-actions';
-import { getBasePath } from '../selectors';
/**
* Factory function for a fetch effect. It expects three action creators,
@@ -25,15 +24,17 @@ export function fetchEffectFactory(
fail: (error: Error) => Action
) {
return function*(action: Action) {
- try {
- const {
- payload: { ...params },
- } = action;
- const basePath = yield select(getBasePath);
- const response = yield call(fetch, { ...params, basePath });
+ const {
+ payload: { ...params },
+ } = action;
+ const response = yield call(fetch, params);
+ if (response instanceof Error) {
+ // eslint-disable-next-line no-console
+ console.error(response);
+
+ yield put(fail(response));
+ } else {
yield put(success(response));
- } catch (error) {
- yield put(fail(error));
}
};
}
diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/monitor.ts b/x-pack/legacy/plugins/uptime/public/state/effects/monitor.ts
index 1cac7424b4e5b..ed21f315476d4 100644
--- a/x-pack/legacy/plugins/uptime/public/state/effects/monitor.ts
+++ b/x-pack/legacy/plugins/uptime/public/state/effects/monitor.ts
@@ -4,48 +4,34 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { call, put, takeLatest, select } from 'redux-saga/effects';
-import { Action } from 'redux-actions';
+import { takeLatest } from 'redux-saga/effects';
import {
- FETCH_MONITOR_DETAILS,
- FETCH_MONITOR_DETAILS_SUCCESS,
- FETCH_MONITOR_DETAILS_FAIL,
- FETCH_MONITOR_LOCATIONS,
- FETCH_MONITOR_LOCATIONS_SUCCESS,
- FETCH_MONITOR_LOCATIONS_FAIL,
+ getMonitorDetailsAction,
+ getMonitorDetailsActionSuccess,
+ getMonitorDetailsActionFail,
+ getMonitorLocationsAction,
+ getMonitorLocationsActionSuccess,
+ getMonitorLocationsActionFail,
} from '../actions/monitor';
import { fetchMonitorDetails, fetchMonitorLocations } from '../api';
-import { getBasePath } from '../selectors';
-import { MonitorDetailsActionPayload } from '../actions/types';
-
-function* monitorDetailsEffect(action: Action) {
- const { monitorId, dateStart, dateEnd }: MonitorDetailsActionPayload = action.payload;
- try {
- const basePath = yield select(getBasePath);
- const response = yield call(fetchMonitorDetails, {
- monitorId,
- basePath,
- dateStart,
- dateEnd,
- });
- yield put({ type: FETCH_MONITOR_DETAILS_SUCCESS, payload: response });
- } catch (error) {
- yield put({ type: FETCH_MONITOR_DETAILS_FAIL, payload: error.message });
- }
-}
-
-function* monitorLocationsEffect(action: Action) {
- const payload = action.payload;
- try {
- const basePath = yield select(getBasePath);
- const response = yield call(fetchMonitorLocations, { basePath, ...payload });
- yield put({ type: FETCH_MONITOR_LOCATIONS_SUCCESS, payload: response });
- } catch (error) {
- yield put({ type: FETCH_MONITOR_LOCATIONS_FAIL, payload: error.message });
- }
-}
+import { fetchEffectFactory } from './fetch_effect';
export function* fetchMonitorDetailsEffect() {
- yield takeLatest(FETCH_MONITOR_DETAILS, monitorDetailsEffect);
- yield takeLatest(FETCH_MONITOR_LOCATIONS, monitorLocationsEffect);
+ yield takeLatest(
+ getMonitorDetailsAction,
+ fetchEffectFactory(
+ fetchMonitorDetails,
+ getMonitorDetailsActionSuccess,
+ getMonitorDetailsActionFail
+ )
+ );
+
+ yield takeLatest(
+ getMonitorLocationsAction,
+ fetchEffectFactory(
+ fetchMonitorLocations,
+ getMonitorLocationsActionSuccess,
+ getMonitorLocationsActionFail
+ )
+ );
}
diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/monitor_status.ts b/x-pack/legacy/plugins/uptime/public/state/effects/monitor_status.ts
index cab32092a14cd..1207ab20bc711 100644
--- a/x-pack/legacy/plugins/uptime/public/state/effects/monitor_status.ts
+++ b/x-pack/legacy/plugins/uptime/public/state/effects/monitor_status.ts
@@ -4,50 +4,34 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { call, put, takeLatest, select } from 'redux-saga/effects';
-import { Action } from 'redux-actions';
+import { takeLatest } from 'redux-saga/effects';
import {
- getSelectedMonitor,
- getSelectedMonitorSuccess,
- getSelectedMonitorFail,
- getMonitorStatus,
- getMonitorStatusSuccess,
- getMonitorStatusFail,
-} from '../actions/monitor_status';
+ getSelectedMonitorAction,
+ getSelectedMonitorActionSuccess,
+ getSelectedMonitorActionFail,
+ getMonitorStatusAction,
+ getMonitorStatusActionSuccess,
+ getMonitorStatusActionFail,
+} from '../actions';
import { fetchSelectedMonitor, fetchMonitorStatus } from '../api';
-import { getBasePath } from '../selectors';
-
-function* selectedMonitorEffect(action: Action) {
- const { monitorId } = action.payload;
- try {
- const basePath = yield select(getBasePath);
- const response = yield call(fetchSelectedMonitor, {
- monitorId,
- basePath,
- });
- yield put({ type: getSelectedMonitorSuccess, payload: response });
- } catch (error) {
- yield put({ type: getSelectedMonitorFail, payload: error.message });
- }
-}
-
-function* monitorStatusEffect(action: Action) {
- const { monitorId, dateStart, dateEnd } = action.payload;
- try {
- const basePath = yield select(getBasePath);
- const response = yield call(fetchMonitorStatus, {
- monitorId,
- basePath,
- dateStart,
- dateEnd,
- });
- yield put({ type: getMonitorStatusSuccess, payload: response });
- } catch (error) {
- yield put({ type: getMonitorStatusFail, payload: error.message });
- }
-}
+import { fetchEffectFactory } from './fetch_effect';
export function* fetchMonitorStatusEffect() {
- yield takeLatest(getMonitorStatus, monitorStatusEffect);
- yield takeLatest(getSelectedMonitor, selectedMonitorEffect);
+ yield takeLatest(
+ getMonitorStatusAction,
+ fetchEffectFactory(
+ fetchMonitorStatus,
+ getMonitorStatusActionSuccess,
+ getMonitorStatusActionFail
+ )
+ );
+
+ yield takeLatest(
+ getSelectedMonitorAction,
+ fetchEffectFactory(
+ fetchSelectedMonitor,
+ getSelectedMonitorActionSuccess,
+ getSelectedMonitorActionFail
+ )
+ );
}
diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/snapshot.ts b/x-pack/legacy/plugins/uptime/public/state/effects/snapshot.ts
index 91df43dd9e826..10010004d47a0 100644
--- a/x-pack/legacy/plugins/uptime/public/state/effects/snapshot.ts
+++ b/x-pack/legacy/plugins/uptime/public/state/effects/snapshot.ts
@@ -6,16 +6,20 @@
import { takeLatest } from 'redux-saga/effects';
import {
- FETCH_SNAPSHOT_COUNT,
- fetchSnapshotCountFail,
- fetchSnapshotCountSuccess,
+ getSnapshotCountAction,
+ getSnapshotCountActionFail,
+ getSnapshotCountActionSuccess,
} from '../actions';
import { fetchSnapshotCount } from '../api';
import { fetchEffectFactory } from './fetch_effect';
export function* fetchSnapshotCountEffect() {
yield takeLatest(
- FETCH_SNAPSHOT_COUNT,
- fetchEffectFactory(fetchSnapshotCount, fetchSnapshotCountSuccess, fetchSnapshotCountFail)
+ getSnapshotCountAction,
+ fetchEffectFactory(
+ fetchSnapshotCount,
+ getSnapshotCountActionSuccess,
+ getSnapshotCountActionFail
+ )
);
}
diff --git a/x-pack/legacy/plugins/uptime/public/state/kibana_service.ts b/x-pack/legacy/plugins/uptime/public/state/kibana_service.ts
new file mode 100644
index 0000000000000..4fd2d446daa17
--- /dev/null
+++ b/x-pack/legacy/plugins/uptime/public/state/kibana_service.ts
@@ -0,0 +1,34 @@
+/*
+ * 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 { CoreStart } from 'kibana/public';
+import { apiService } from './api/utils';
+
+class KibanaService {
+ private static instance: KibanaService;
+ private _core!: CoreStart;
+
+ public get core() {
+ return this._core;
+ }
+
+ public set core(coreStart: CoreStart) {
+ this._core = coreStart;
+ apiService.http = this._core.http;
+ }
+
+ private constructor() {}
+
+ static getInstance(): KibanaService {
+ if (!KibanaService.instance) {
+ KibanaService.instance = new KibanaService();
+ }
+
+ return KibanaService.instance;
+ }
+}
+
+export const kibanaService = KibanaService.getInstance();
diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/__tests__/snapshot.test.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/__tests__/snapshot.test.ts
index 95c576e0fd72e..3650422571ce8 100644
--- a/x-pack/legacy/plugins/uptime/public/state/reducers/__tests__/snapshot.test.ts
+++ b/x-pack/legacy/plugins/uptime/public/state/reducers/__tests__/snapshot.test.ts
@@ -5,19 +5,20 @@
*/
import { snapshotReducer } from '../snapshot';
-import { SnapshotActionTypes } from '../../actions';
+import {
+ getSnapshotCountAction,
+ getSnapshotCountActionSuccess,
+ getSnapshotCountActionFail,
+} from '../../actions';
describe('snapshot reducer', () => {
it('updates existing state', () => {
- const action: SnapshotActionTypes = {
- type: 'FETCH_SNAPSHOT_COUNT',
- payload: {
- dateRangeStart: 'now-15m',
- dateRangeEnd: 'now',
- filters: 'foo: bar',
- statusFilter: 'up',
- },
- };
+ const action = getSnapshotCountAction({
+ dateRangeStart: 'now-15m',
+ dateRangeEnd: 'now',
+ filters: 'foo: bar',
+ statusFilter: 'up',
+ });
expect(
snapshotReducer(
{
@@ -31,33 +32,28 @@ describe('snapshot reducer', () => {
});
it(`sets the state's status to loading during a fetch`, () => {
- const action: SnapshotActionTypes = {
- type: 'FETCH_SNAPSHOT_COUNT',
- payload: {
- dateRangeStart: 'now-15m',
- dateRangeEnd: 'now',
- },
- };
+ const action = getSnapshotCountAction({
+ dateRangeStart: 'now-15m',
+ dateRangeEnd: 'now',
+ });
expect(snapshotReducer(undefined, action)).toMatchSnapshot();
});
it('changes the count when a snapshot fetch succeeds', () => {
- const action: SnapshotActionTypes = {
- type: 'FETCH_SNAPSHOT_COUNT_SUCCESS',
- payload: {
- up: 10,
- down: 15,
- total: 25,
- },
- };
+ const action = getSnapshotCountActionSuccess({
+ up: 10,
+ down: 15,
+ total: 25,
+ });
+
expect(snapshotReducer(undefined, action)).toMatchSnapshot();
});
it('appends a current error to existing errors list', () => {
- const action: SnapshotActionTypes = {
- type: 'FETCH_SNAPSHOT_COUNT_FAIL',
- payload: new Error(`I couldn't get your data because the server denied the request`),
- };
+ const action = getSnapshotCountActionFail(
+ new Error(`I couldn't get your data because the server denied the request`)
+ );
+
expect(snapshotReducer(undefined, action)).toMatchSnapshot();
});
});
diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/monitor.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/monitor.ts
index aac8a90598d0c..632f3a270e1a1 100644
--- a/x-pack/legacy/plugins/uptime/public/state/reducers/monitor.ts
+++ b/x-pack/legacy/plugins/uptime/public/state/reducers/monitor.ts
@@ -4,15 +4,15 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import { Action } from 'redux-actions';
import {
- MonitorActionTypes,
MonitorDetailsState,
- FETCH_MONITOR_DETAILS,
- FETCH_MONITOR_DETAILS_SUCCESS,
- FETCH_MONITOR_DETAILS_FAIL,
- FETCH_MONITOR_LOCATIONS,
- FETCH_MONITOR_LOCATIONS_SUCCESS,
- FETCH_MONITOR_LOCATIONS_FAIL,
+ getMonitorDetailsAction,
+ getMonitorLocationsAction,
+ getMonitorDetailsActionSuccess,
+ getMonitorDetailsActionFail,
+ getMonitorLocationsActionSuccess,
+ getMonitorLocationsActionFail,
} from '../actions/monitor';
import { MonitorLocations } from '../../../common/runtime_types';
@@ -32,14 +32,14 @@ const initialState: MonitorState = {
errors: [],
};
-export function monitorReducer(state = initialState, action: MonitorActionTypes): MonitorState {
+export function monitorReducer(state = initialState, action: Action): MonitorState {
switch (action.type) {
- case FETCH_MONITOR_DETAILS:
+ case String(getMonitorDetailsAction):
return {
...state,
loading: true,
};
- case FETCH_MONITOR_DETAILS_SUCCESS:
+ case String(getMonitorDetailsActionSuccess):
const { monitorId } = action.payload;
return {
...state,
@@ -49,17 +49,17 @@ export function monitorReducer(state = initialState, action: MonitorActionTypes)
},
loading: false,
};
- case FETCH_MONITOR_DETAILS_FAIL:
+ case String(getMonitorDetailsActionFail):
return {
...state,
errors: [...state.errors, action.payload],
};
- case FETCH_MONITOR_LOCATIONS:
+ case String(getMonitorLocationsAction):
return {
...state,
loading: true,
};
- case FETCH_MONITOR_LOCATIONS_SUCCESS:
+ case String(getMonitorLocationsActionSuccess):
const monLocations = state.monitorLocationsList;
monLocations.set(action.payload.monitorId, action.payload);
return {
@@ -67,7 +67,7 @@ export function monitorReducer(state = initialState, action: MonitorActionTypes)
monitorLocationsList: monLocations,
loading: false,
};
- case FETCH_MONITOR_LOCATIONS_FAIL:
+ case String(getMonitorLocationsActionFail):
return {
...state,
errors: [...state.errors, action.payload],
diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/monitor_status.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/monitor_status.ts
index 2688a0946dd61..c2dfbd7f90ff2 100644
--- a/x-pack/legacy/plugins/uptime/public/state/reducers/monitor_status.ts
+++ b/x-pack/legacy/plugins/uptime/public/state/reducers/monitor_status.ts
@@ -5,12 +5,12 @@
*/
import { handleActions, Action } from 'redux-actions';
import {
- getSelectedMonitor,
- getSelectedMonitorSuccess,
- getSelectedMonitorFail,
- getMonitorStatus,
- getMonitorStatusSuccess,
- getMonitorStatusFail,
+ getSelectedMonitorAction,
+ getSelectedMonitorActionSuccess,
+ getSelectedMonitorActionFail,
+ getMonitorStatusAction,
+ getMonitorStatusActionSuccess,
+ getMonitorStatusActionFail,
} from '../actions';
import { Ping } from '../../../common/graphql/types';
import { QueryParams } from '../actions/types';
@@ -31,34 +31,34 @@ type MonitorStatusPayload = QueryParams & Ping;
export const monitorStatusReducer = handleActions(
{
- [String(getSelectedMonitor)]: (state, action: Action) => ({
+ [String(getSelectedMonitorAction)]: (state, action: Action) => ({
...state,
loading: true,
}),
- [String(getSelectedMonitorSuccess)]: (state, action: Action) => ({
+ [String(getSelectedMonitorActionSuccess)]: (state, action: Action) => ({
...state,
loading: false,
monitor: { ...action.payload } as Ping,
}),
- [String(getSelectedMonitorFail)]: (state, action: Action) => ({
+ [String(getSelectedMonitorActionFail)]: (state, action: Action) => ({
...state,
loading: false,
}),
- [String(getMonitorStatus)]: (state, action: Action) => ({
+ [String(getMonitorStatusAction)]: (state, action: Action) => ({
...state,
loading: true,
}),
- [String(getMonitorStatusSuccess)]: (state, action: Action) => ({
+ [String(getMonitorStatusActionSuccess)]: (state, action: Action) => ({
...state,
loading: false,
status: { ...action.payload } as Ping,
}),
- [String(getMonitorStatusFail)]: (state, action: Action) => ({
+ [String(getMonitorStatusActionFail)]: (state, action: Action) => ({
...state,
loading: false,
}),
diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/overview_filters.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/overview_filters.ts
index b219421f4f4dc..0b67d8b0e7689 100644
--- a/x-pack/legacy/plugins/uptime/public/state/reducers/overview_filters.ts
+++ b/x-pack/legacy/plugins/uptime/public/state/reducers/overview_filters.ts
@@ -49,6 +49,7 @@ export function overviewFiltersReducer(
return {
...state,
errors: [...state.errors, action.payload],
+ loading: false,
};
default:
return state;
diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/snapshot.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/snapshot.ts
index 2155d0e3a74e3..3ba1ef84d41a5 100644
--- a/x-pack/legacy/plugins/uptime/public/state/reducers/snapshot.ts
+++ b/x-pack/legacy/plugins/uptime/public/state/reducers/snapshot.ts
@@ -4,12 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import { Action } from 'redux-actions';
import { Snapshot } from '../../../common/runtime_types';
import {
- FETCH_SNAPSHOT_COUNT,
- FETCH_SNAPSHOT_COUNT_FAIL,
- FETCH_SNAPSHOT_COUNT_SUCCESS,
- SnapshotActionTypes,
+ getSnapshotCountAction,
+ getSnapshotCountActionSuccess,
+ getSnapshotCountActionFail,
} from '../actions';
export interface SnapshotState {
@@ -28,20 +28,20 @@ const initialState: SnapshotState = {
loading: false,
};
-export function snapshotReducer(state = initialState, action: SnapshotActionTypes): SnapshotState {
+export function snapshotReducer(state = initialState, action: Action): SnapshotState {
switch (action.type) {
- case FETCH_SNAPSHOT_COUNT:
+ case String(getSnapshotCountAction):
return {
...state,
loading: true,
};
- case FETCH_SNAPSHOT_COUNT_SUCCESS:
+ case String(getSnapshotCountActionSuccess):
return {
...state,
count: action.payload,
loading: false,
};
- case FETCH_SNAPSHOT_COUNT_FAIL:
+ case String(getSnapshotCountActionFail):
return {
...state,
errors: [...state.errors, action.payload],
diff --git a/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts b/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts
index adba288b8b145..4767c25e8f52f 100644
--- a/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts
+++ b/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts
@@ -13,11 +13,11 @@ export const isIntegrationsPopupOpen = ({ ui: { integrationsPopoverOpen } }: App
integrationsPopoverOpen;
// Monitor Selectors
-export const getMonitorDetails = (state: AppState, summary: any) => {
+export const monitorDetailsSelector = (state: AppState, summary: any) => {
return state.monitor.monitorDetailsList[summary.monitor_id];
};
-export const selectMonitorLocations = (state: AppState, monitorId: string) => {
+export const monitorLocationsSelector = (state: AppState, monitorId: string) => {
return state.monitor.monitorLocationsList?.get(monitorId);
};
diff --git a/x-pack/legacy/plugins/uptime/public/uptime_app.tsx b/x-pack/legacy/plugins/uptime/public/uptime_app.tsx
index 427870797a206..09156db9ca7d2 100644
--- a/x-pack/legacy/plugins/uptime/public/uptime_app.tsx
+++ b/x-pack/legacy/plugins/uptime/public/uptime_app.tsx
@@ -23,6 +23,7 @@ import { CommonlyUsedRange } from './components/functional/uptime_date_picker';
import { store } from './state';
import { setBasePath } from './state/actions';
import { PageRouter } from './routes';
+import { kibanaService } from './state/kibana_service';
export interface UptimeAppColors {
danger: string;
@@ -83,6 +84,8 @@ const Application = (props: UptimeAppProps) => {
);
}, [canSave, renderGlobalHelpControls, setBadge]);
+ kibanaService.core = core;
+
// @ts-ignore
store.dispatch(setBasePath(basePath));
diff --git a/x-pack/legacy/plugins/xpack_main/public/components/index.js b/x-pack/legacy/plugins/xpack_main/public/components/index.js
index e57bd6af189f8..871d86e642dec 100644
--- a/x-pack/legacy/plugins/xpack_main/public/components/index.js
+++ b/x-pack/legacy/plugins/xpack_main/public/components/index.js
@@ -4,11 +4,11 @@
* you may not use this file except in compliance with the Elastic License.
*/
-export { LicenseStatus } from '../../../license_management/public/np_ready/application/sections/license_dashboard/license_status/license_status';
+export { LicenseStatus } from '../../../../../plugins/license_management/public/application/sections/license_dashboard/license_status/license_status';
-export { AddLicense } from '../../../license_management/public/np_ready/application/sections/license_dashboard/add_license/add_license';
+export { AddLicense } from '../../../../../plugins/license_management/public/application/sections/license_dashboard/add_license/add_license';
/*
* For to link to management
*/
-export { BASE_PATH as MANAGEMENT_BASE_PATH } from '../../../license_management/common/constants';
+export { BASE_PATH as MANAGEMENT_BASE_PATH } from '../../../../../plugins/license_management/common/constants';
diff --git a/x-pack/package.json b/x-pack/package.json
index 268434a35d705..02ee9deb0d12f 100644
--- a/x-pack/package.json
+++ b/x-pack/package.json
@@ -23,7 +23,7 @@
}
},
"resolutions": {
- "**/@types/node": "10.12.27"
+ "**/@types/node": ">=10.17.17 <10.20.0"
},
"devDependencies": {
"@cypress/webpack-preprocessor": "^4.1.0",
@@ -80,7 +80,7 @@
"@types/mime": "^2.0.1",
"@types/mocha": "^5.2.7",
"@types/nock": "^10.0.3",
- "@types/node": "^10.12.27",
+ "@types/node": ">=10.17.17 <10.20.0",
"@types/node-fetch": "^2.5.0",
"@types/nodemailer": "^6.2.1",
"@types/object-hash": "^1.3.0",
@@ -179,7 +179,7 @@
"@babel/runtime": "^7.5.5",
"@elastic/apm-rum-react": "^0.3.2",
"@elastic/datemath": "5.0.2",
- "@elastic/ems-client": "7.6.0",
+ "@elastic/ems-client": "7.7.0",
"@elastic/eui": "20.0.2",
"@elastic/filesaver": "1.1.2",
"@elastic/maki": "6.1.0",
@@ -317,7 +317,7 @@
"react-sticky": "^6.0.3",
"react-syntax-highlighter": "^5.7.0",
"react-tiny-virtual-list": "^2.2.0",
- "react-use": "^13.13.0",
+ "react-use": "^13.27.0",
"react-vis": "^1.8.1",
"react-visibility-sensor": "^5.1.1",
"recompose": "^0.26.0",
diff --git a/x-pack/plugins/actions/server/builtin_action_types/es_index.test.ts b/x-pack/plugins/actions/server/builtin_action_types/es_index.test.ts
index 0be1983477256..7eded9bb40964 100644
--- a/x-pack/plugins/actions/server/builtin_action_types/es_index.test.ts
+++ b/x-pack/plugins/actions/server/builtin_action_types/es_index.test.ts
@@ -43,18 +43,46 @@ describe('actionTypeRegistry.get() works', () => {
describe('config validation', () => {
test('config validation succeeds when config is valid', () => {
- const config: Record = {};
+ const config: Record = {
+ index: 'testing-123',
+ refresh: false,
+ };
expect(validateConfig(actionType, config)).toEqual({
...config,
- index: null,
+ index: 'testing-123',
+ refresh: false,
});
- config.index = 'testing-123';
+ config.executionTimeField = 'field-123';
expect(validateConfig(actionType, config)).toEqual({
...config,
index: 'testing-123',
+ refresh: false,
+ executionTimeField: 'field-123',
});
+
+ delete config.index;
+
+ expect(() => {
+ validateConfig(actionType, { index: 666 });
+ }).toThrowErrorMatchingInlineSnapshot(
+ `"error validating action type config: [index]: expected value of type [string] but got [number]"`
+ );
+ delete config.executionTimeField;
+
+ expect(() => {
+ validateConfig(actionType, { index: 'testing-123', executionTimeField: true });
+ }).toThrowErrorMatchingInlineSnapshot(
+ `"error validating action type config: [executionTimeField]: expected value of type [string] but got [boolean]"`
+ );
+
+ delete config.refresh;
+ expect(() => {
+ validateConfig(actionType, { index: 'testing-123', refresh: 'foo' });
+ }).toThrowErrorMatchingInlineSnapshot(
+ `"error validating action type config: [refresh]: expected value of type [boolean] but got [string]"`
+ );
});
test('config validation fails when config is not valid', () => {
@@ -65,46 +93,16 @@ describe('config validation', () => {
expect(() => {
validateConfig(actionType, baseConfig);
}).toThrowErrorMatchingInlineSnapshot(
- `"error validating action type config: [indeX]: definition for this key is missing"`
+ `"error validating action type config: [index]: expected value of type [string] but got [undefined]"`
);
-
- delete baseConfig.user;
- baseConfig.index = 666;
-
- expect(() => {
- validateConfig(actionType, baseConfig);
- }).toThrowErrorMatchingInlineSnapshot(`
-"error validating action type config: [index]: types that failed validation:
-- [index.0]: expected value of type [string] but got [number]
-- [index.1]: expected value to equal [null]"
-`);
});
});
describe('params validation', () => {
test('params validation succeeds when params is valid', () => {
const params: Record = {
- index: 'testing-123',
- executionTimeField: 'field-used-for-time',
- refresh: true,
documents: [{ rando: 'thing' }],
};
- expect(validateParams(actionType, params)).toMatchInlineSnapshot(`
- Object {
- "documents": Array [
- Object {
- "rando": "thing",
- },
- ],
- "executionTimeField": "field-used-for-time",
- "index": "testing-123",
- "refresh": true,
- }
- `);
-
- delete params.index;
- delete params.refresh;
- delete params.executionTimeField;
expect(validateParams(actionType, params)).toMatchInlineSnapshot(`
Object {
"documents": Array [
@@ -129,24 +127,6 @@ describe('params validation', () => {
`"error validating action params: [documents]: expected value of type [array] but got [undefined]"`
);
- expect(() => {
- validateParams(actionType, { index: 666 });
- }).toThrowErrorMatchingInlineSnapshot(
- `"error validating action params: [index]: expected value of type [string] but got [number]"`
- );
-
- expect(() => {
- validateParams(actionType, { executionTimeField: true });
- }).toThrowErrorMatchingInlineSnapshot(
- `"error validating action params: [executionTimeField]: expected value of type [string] but got [boolean]"`
- );
-
- expect(() => {
- validateParams(actionType, { refresh: 'foo' });
- }).toThrowErrorMatchingInlineSnapshot(
- `"error validating action params: [refresh]: expected value of type [boolean] but got [string]"`
- );
-
expect(() => {
validateParams(actionType, { documents: ['should be an object'] });
}).toThrowErrorMatchingInlineSnapshot(
@@ -162,13 +142,10 @@ describe('execute()', () => {
let params: ActionParamsType;
let executorOptions: ActionTypeExecutorOptions;
- // minimal params, index via param
- config = { index: null };
+ // minimal params
+ config = { index: 'index-value', refresh: false, executionTimeField: undefined };
params = {
- index: 'index-via-param',
documents: [{ jim: 'bob' }],
- executionTimeField: undefined,
- refresh: undefined,
};
const actionId = 'some-id';
@@ -190,19 +167,17 @@ describe('execute()', () => {
"jim": "bob",
},
],
- "index": "index-via-param",
+ "index": "index-value",
+ "refresh": false,
},
],
]
`);
- // full params (except index), index via config
- config = { index: 'index-via-config' };
+ // full params
+ config = { index: 'index-value', executionTimeField: 'field_to_use_for_time', refresh: true };
params = {
- index: undefined,
documents: [{ jimbob: 'jr' }],
- executionTimeField: 'field_to_use_for_time',
- refresh: true,
};
executorOptions = { actionId, config, secrets, params, services };
@@ -226,20 +201,17 @@ describe('execute()', () => {
"jimbob": "jr",
},
],
- "index": "index-via-config",
+ "index": "index-value",
"refresh": true,
},
],
]
`);
- // minimal params, index via config and param
- config = { index: 'index-via-config' };
+ // minimal params
+ config = { index: 'index-value', executionTimeField: undefined, refresh: false };
params = {
- index: 'index-via-param',
documents: [{ jim: 'bob' }],
- executionTimeField: undefined,
- refresh: undefined,
};
executorOptions = { actionId, config, secrets, params, services };
@@ -259,19 +231,17 @@ describe('execute()', () => {
"jim": "bob",
},
],
- "index": "index-via-config",
+ "index": "index-value",
+ "refresh": false,
},
],
]
`);
// multiple documents
- config = { index: null };
+ config = { index: 'index-value', executionTimeField: undefined, refresh: false };
params = {
- index: 'index-via-param',
documents: [{ a: 1 }, { b: 2 }],
- executionTimeField: undefined,
- refresh: undefined,
};
executorOptions = { actionId, config, secrets, params, services };
@@ -297,7 +267,8 @@ describe('execute()', () => {
"b": 2,
},
],
- "index": "index-via-param",
+ "index": "index-value",
+ "refresh": false,
},
],
]
diff --git a/x-pack/plugins/actions/server/builtin_action_types/es_index.ts b/x-pack/plugins/actions/server/builtin_action_types/es_index.ts
index f8217046b2ea5..b1fe5e3af2d11 100644
--- a/x-pack/plugins/actions/server/builtin_action_types/es_index.ts
+++ b/x-pack/plugins/actions/server/builtin_action_types/es_index.ts
@@ -8,7 +8,6 @@ import { curry } from 'lodash';
import { i18n } from '@kbn/i18n';
import { schema, TypeOf } from '@kbn/config-schema';
-import { nullableType } from './lib/nullable';
import { Logger } from '../../../../../src/core/server';
import { ActionType, ActionTypeExecutorOptions, ActionTypeExecutorResult } from '../types';
@@ -17,7 +16,9 @@ import { ActionType, ActionTypeExecutorOptions, ActionTypeExecutorResult } from
export type ActionTypeConfigType = TypeOf;
const ConfigSchema = schema.object({
- index: nullableType(schema.string()),
+ index: schema.string(),
+ refresh: schema.boolean({ defaultValue: false }),
+ executionTimeField: schema.maybe(schema.string()),
});
// params definition
@@ -28,9 +29,6 @@ export type ActionParamsType = TypeOf;
// - timeout not added here, as this seems to be a generic thing we want to do
// eventually: https://github.com/elastic/kibana/projects/26#card-24087404
const ParamsSchema = schema.object({
- index: schema.maybe(schema.string()),
- executionTimeField: schema.maybe(schema.string()),
- refresh: schema.maybe(schema.boolean()),
documents: schema.arrayOf(schema.recordOf(schema.string(), schema.any())),
});
@@ -60,27 +58,12 @@ async function executor(
const params = execOptions.params as ActionParamsType;
const services = execOptions.services;
- if (config.index == null && params.index == null) {
- const message = i18n.translate('xpack.actions.builtin.esIndex.indexParamRequiredErrorMessage', {
- defaultMessage: 'index param needs to be set because not set in config for action',
- });
- return {
- status: 'error',
- actionId,
- message,
- };
- }
-
- if (config.index != null && params.index != null) {
- logger.debug(`index passed in params overridden by index set in config for action ${actionId}`);
- }
-
- const index = config.index || params.index;
+ const index = config.index;
const bulkBody = [];
for (const document of params.documents) {
- if (params.executionTimeField != null) {
- document[params.executionTimeField] = new Date();
+ if (config.executionTimeField != null) {
+ document[config.executionTimeField] = new Date();
}
bulkBody.push({ index: {} });
@@ -92,9 +75,7 @@ async function executor(
body: bulkBody,
};
- if (params.refresh != null) {
- bulkParams.refresh = params.refresh;
- }
+ bulkParams.refresh = config.refresh;
let result;
try {
@@ -103,6 +84,7 @@ async function executor(
const message = i18n.translate('xpack.actions.builtin.esIndex.errorIndexingErrorMessage', {
defaultMessage: 'error indexing documents',
});
+ logger.error(`error indexing documents: ${err.message}`);
return {
status: 'error',
actionId,
diff --git a/x-pack/plugins/alerting/server/plugin.ts b/x-pack/plugins/alerting/server/plugin.ts
index 885391325fcd6..b4b2de19ef24f 100644
--- a/x-pack/plugins/alerting/server/plugin.ts
+++ b/x-pack/plugins/alerting/server/plugin.ts
@@ -173,16 +173,6 @@ export class AlertingPlugin {
muteAlertInstanceRoute(router, this.licenseState);
unmuteAlertInstanceRoute(router, this.licenseState);
- alertTypeRegistry.register({
- id: 'test',
- actionGroups: [{ id: 'default', name: 'Default' }],
- defaultActionGroupId: 'default',
- name: 'Test',
- executor: async options => {
- return { status: 'ok' };
- },
- });
-
return {
registerType: alertTypeRegistry.register.bind(alertTypeRegistry),
};
diff --git a/x-pack/plugins/case/server/routes/api/cases/configure/patch_connector.ts b/x-pack/plugins/case/server/routes/api/cases/configure/patch_connector.ts
deleted file mode 100644
index a9fbe0ef4f721..0000000000000
--- a/x-pack/plugins/case/server/routes/api/cases/configure/patch_connector.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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 { schema } from '@kbn/config-schema';
-import Boom from 'boom';
-import { pipe } from 'fp-ts/lib/pipeable';
-import { fold } from 'fp-ts/lib/Either';
-import { identity } from 'fp-ts/lib/function';
-
-import { ActionResult } from '../../../../../../actions/common';
-import { CasesConnectorConfigurationRT, throwErrors } from '../../../../../common/api';
-import { RouteDeps } from '../../types';
-import { wrapError, escapeHatch } from '../../utils';
-
-export function initCaseConfigurePatchActionConnector({ caseService, router }: RouteDeps) {
- router.patch(
- {
- path: '/api/cases/configure/connectors/{connector_id}',
- validate: {
- params: schema.object({
- connector_id: schema.string(),
- }),
- body: escapeHatch,
- },
- },
- async (context, request, response) => {
- try {
- const query = pipe(
- CasesConnectorConfigurationRT.decode(request.body),
- fold(throwErrors(Boom.badRequest), identity)
- );
-
- const client = context.core.savedObjects.client;
- const { connector_id: connectorId } = request.params;
- const { cases_configuration: casesConfiguration } = query;
-
- const normalizedMapping = casesConfiguration.mapping.map(m => ({
- source: m.source,
- target: m.target,
- actionType: m.action_type,
- }));
-
- const action = await client.get('action', connectorId);
-
- const { config } = action.attributes;
- const res = await client.update('action', connectorId, {
- config: {
- ...config,
- casesConfiguration: { ...casesConfiguration, mapping: normalizedMapping },
- },
- });
-
- return response.ok({
- body: CasesConnectorConfigurationRT.encode({
- cases_configuration:
- res.attributes.config?.casesConfiguration ??
- action.attributes.config.casesConfiguration,
- }),
- });
- } catch (error) {
- return response.customError(wrapError(error));
- }
- }
- );
-}
diff --git a/x-pack/plugins/case/server/routes/api/index.ts b/x-pack/plugins/case/server/routes/api/index.ts
index 956f410c9c10a..60ee57a0efea7 100644
--- a/x-pack/plugins/case/server/routes/api/index.ts
+++ b/x-pack/plugins/case/server/routes/api/index.ts
@@ -26,7 +26,6 @@ import { initGetTagsApi } from './cases/tags/get_tags';
import { RouteDeps } from './types';
import { initCaseConfigureGetActionConnector } from './cases/configure/get_connectors';
-import { initCaseConfigurePatchActionConnector } from './cases/configure/patch_connector';
import { initGetCaseConfigure } from './cases/configure/get_configure';
import { initPatchCaseConfigure } from './cases/configure/patch_configure';
import { initPostCaseConfigure } from './cases/configure/post_configure';
@@ -48,7 +47,6 @@ export function initCaseApi(deps: RouteDeps) {
initPostCommentApi(deps);
// Cases Configure
initCaseConfigureGetActionConnector(deps);
- initCaseConfigurePatchActionConnector(deps);
initGetCaseConfigure(deps);
initPatchCaseConfigure(deps);
initPostCaseConfigure(deps);
diff --git a/x-pack/plugins/case/server/scripts/README.md b/x-pack/plugins/case/server/scripts/README.md
new file mode 100644
index 0000000000000..2c35eb305282a
--- /dev/null
+++ b/x-pack/plugins/case/server/scripts/README.md
@@ -0,0 +1,90 @@
+README.md for developers working on the Case API on how to get started
+using the CURL scripts in the scripts folder.
+
+The scripts rely on CURL and jq:
+
+- [CURL](https://curl.haxx.se)
+- [jq](https://stedolan.github.io/jq/)
+
+Install curl and jq
+
+```sh
+brew update
+brew install curl
+brew install jq
+```
+
+Open `$HOME/.zshrc` or `${HOME}.bashrc` depending on your SHELL output from `echo $SHELL`
+and add these environment variables:
+
+```sh
+export ELASTICSEARCH_USERNAME=${user}
+export ELASTICSEARCH_PASSWORD=${password}
+export ELASTICSEARCH_URL=https://${ip}:9200
+export KIBANA_URL=http://localhost:5601
+export TASK_MANAGER_INDEX=.kibana-task-manager-${your user id}
+export KIBANA_INDEX=.kibana-${your user id}
+```
+
+source `$HOME/.zshrc` or `${HOME}.bashrc` to ensure variables are set:
+
+```sh
+source ~/.zshrc
+```
+
+Restart Kibana and ensure that you are using `--no-base-path` as changing the base path is a feature but will
+get in the way of the CURL scripts written as is.
+
+Go to the scripts folder `cd kibana/x-pack/plugins/case/server/scripts` and run:
+
+```sh
+./hard_reset.sh
+```
+
+which will:
+
+- Delete any existing cases you have
+- Delete any existing comments you have
+- Posts the sample case from `./mock/case/post_case.json`
+- Posts the sample comment from `./mock/comment/post_comment.json` to the new case
+
+Now you can run
+
+```sh
+./find_cases.sh
+```
+
+You should see the new case created like so:
+
+```sh
+{
+ "page": 1,
+ "per_page": 20,
+ "total": 1,
+ "cases": [
+ {
+ "id": "2e0afbc0-658c-11ea-85c8-1d8f792cbc08",
+ "version": "Wzc5NSwxXQ==",
+ "comments": [],
+ "comment_ids": [
+ "2ecec0f0-658c-11ea-85c8-1d8f792cbc08"
+ ],
+ "created_at": "2020-03-14T00:38:53.004Z",
+ "created_by": {
+ "full_name": "Steph Milovic",
+ "username": "smilovic"
+ },
+ "updated_at": null,
+ "updated_by": null,
+ "description": "This looks not so good",
+ "title": "Bad meanie defacing data",
+ "status": "open",
+ "tags": [
+ "defacement"
+ ]
+ }
+ ],
+ "count_open_cases": 1,
+ "count_closed_cases": 1
+}
+```
diff --git a/x-pack/plugins/case/server/scripts/check_env_variables.sh b/x-pack/plugins/case/server/scripts/check_env_variables.sh
new file mode 100755
index 0000000000000..2f7644051debb
--- /dev/null
+++ b/x-pack/plugins/case/server/scripts/check_env_variables.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# Add this to the start of any scripts to detect if env variables are set
+
+set -e
+
+if [ -z "${ELASTICSEARCH_USERNAME}" ]; then
+ echo "Set ELASTICSEARCH_USERNAME in your environment"
+ exit 1
+fi
+
+if [ -z "${ELASTICSEARCH_PASSWORD}" ]; then
+ echo "Set ELASTICSEARCH_PASSWORD in your environment"
+ exit 1
+fi
+
+if [ -z "${ELASTICSEARCH_URL}" ]; then
+ echo "Set ELASTICSEARCH_URL in your environment"
+ exit 1
+fi
+
+if [ -z "${KIBANA_URL}" ]; then
+ echo "Set KIBANA_URL in your environment"
+ exit 1
+fi
+
+if [ -z "${TASK_MANAGER_INDEX}" ]; then
+ echo "Set TASK_MANAGER_INDEX in your environment"
+ exit 1
+fi
+
+if [ -z "${KIBANA_INDEX}" ]; then
+ echo "Set KIBANA_INDEX in your environment"
+ exit 1
+fi
diff --git a/x-pack/plugins/case/server/scripts/delete_cases.sh b/x-pack/plugins/case/server/scripts/delete_cases.sh
new file mode 100755
index 0000000000000..c04afed5fe679
--- /dev/null
+++ b/x-pack/plugins/case/server/scripts/delete_cases.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# Creates a new case and then gets it if no CASE_ID is specified
+
+# Example:
+# ./delete_cases.sh
+
+# Example with CASE_ID args:
+# ./delete_cases.sh 1234-example-id 5678-example-id
+
+set -e
+./check_env_variables.sh
+
+if [ "$1" ]; then
+ ALL=("$@")
+ i=0
+
+ COUNT=${#ALL[@]}
+ IDS=""
+ for ID in "${ALL[@]}"
+ do
+ let i=i+1
+ if [ $i -eq $COUNT ]; then
+ IDS+="%22${ID}%22"
+ else
+ IDS+="%22${ID}%22,"
+ fi
+ done
+
+ curl -s -k \
+ -H 'kbn-xsrf: 123' \
+ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
+ -X DELETE "${KIBANA_URL}${SPACE_URL}/api/cases?ids=\[${IDS}\]" \
+ | jq .;
+ exit 1
+else
+ CASE_ID=("$(./generate_case_data.sh | jq '.id' -j)")
+ curl -s -k \
+ -H 'Content-Type: application/json' \
+ -H 'kbn-xsrf: 123' \
+ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
+ -X DELETE "${KIBANA_URL}${SPACE_URL}/api/cases?ids=\[%22${CASE_ID}%22\]" \
+ | jq .;
+ exit 1
+fi
diff --git a/x-pack/plugins/case/server/scripts/delete_comment.sh b/x-pack/plugins/case/server/scripts/delete_comment.sh
new file mode 100755
index 0000000000000..a858d9cb11a57
--- /dev/null
+++ b/x-pack/plugins/case/server/scripts/delete_comment.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# Creates a new case and then gets it if no CASE_ID is specified
+
+# Example:
+# ./delete_comment.sh
+
+# Example with CASE_ID and COMMENT_ID arg:
+# ./delete_comment.sh 1234-example-case-id 5678-example-comment-id
+
+set -e
+./check_env_variables.sh
+
+
+if [ "$1" ] && [ "$2" ]; then
+ curl -s -k \
+ -H 'kbn-xsrf: 123' \
+ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
+ -X DELETE "${KIBANA_URL}${SPACE_URL}/api/cases/$1/comments/$2" \
+ | jq .;
+ exit 1
+else
+ DATA="$(./generate_case_and_comment_data.sh | jq '{ caseId: .caseId, commentId: .commentId}' -j)"
+ CASE_ID=$(echo $DATA | jq ".caseId" -j)
+ COMMENT_ID=$(echo $DATA | jq ".commentId" -j)
+ curl -s -k \
+ -H 'kbn-xsrf: 123' \
+ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
+ -X DELETE "${KIBANA_URL}${SPACE_URL}/api/cases/$CASE_ID/comments/$COMMENT_ID" \
+ | jq .;
+ exit 1
+fi
+./delete_case.sh [b6766a90-6559-11ea-9fd5-b52942ab389a]
\ No newline at end of file
diff --git a/x-pack/plugins/case/server/scripts/find_cases.sh b/x-pack/plugins/case/server/scripts/find_cases.sh
new file mode 100755
index 0000000000000..bb4232b0c6c27
--- /dev/null
+++ b/x-pack/plugins/case/server/scripts/find_cases.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# Example:
+# ./find_cases.sh
+
+set -e
+./check_env_variables.sh
+
+curl -s -k \
+ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
+ -X GET ${KIBANA_URL}${SPACE_URL}/api/cases/_find | jq .
diff --git a/x-pack/plugins/case/server/scripts/find_cases_by_filter.sh b/x-pack/plugins/case/server/scripts/find_cases_by_filter.sh
new file mode 100755
index 0000000000000..433311c117e98
--- /dev/null
+++ b/x-pack/plugins/case/server/scripts/find_cases_by_filter.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# Example:
+# ./find_cases_by_filter.sh
+
+# Example get all open cases:
+# ./find_cases_by_filter.sh "cases.attributes.state:%20open"
+
+# Example get all the names that start with Bad*
+# ./find_cases_by_filter.sh "cases.attributes.title:%20Bad*"
+
+# Exampe get everything that has phishing
+# ./find_cases_by_filter.sh "cases.attributes.tags:phishing"
+
+set -e
+./check_env_variables.sh
+
+FILTER=${1:-'cases.attributes.state:%20closed'}
+
+curl -s -k \
+ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
+ -X GET ${KIBANA_URL}${SPACE_URL}/api/cases/_find?filter=$FILTER | jq .
diff --git a/x-pack/plugins/case/server/scripts/find_cases_sort.sh b/x-pack/plugins/case/server/scripts/find_cases_sort.sh
new file mode 100755
index 0000000000000..436b475220102
--- /dev/null
+++ b/x-pack/plugins/case/server/scripts/find_cases_sort.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# Example:
+# ./find_cases_sort.sh
+
+# Example with sort args:
+# ./find_cases_sort.sh createdAt desc
+
+set -e
+./check_env_variables.sh
+
+SORT=${1:-'createdAt'}
+ORDER=${2:-'asc'}
+
+curl -s -k \
+ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
+ -X GET "${KIBANA_URL}${SPACE_URL}/api/cases/_find?sortField=$SORT&sortOrder=$ORDER" \
+ | jq .
diff --git a/x-pack/plugins/case/server/scripts/generate_case_and_comment_data.sh b/x-pack/plugins/case/server/scripts/generate_case_and_comment_data.sh
new file mode 100755
index 0000000000000..9b6f472d798e0
--- /dev/null
+++ b/x-pack/plugins/case/server/scripts/generate_case_and_comment_data.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# returns case/comment data as { commentId, commentVersion, caseId, caseVersion }
+# Example:
+# ./generate_case_and_comment_data.sh
+
+set -e
+./check_env_variables.sh
+
+COMMENT=(${1:-./mock/comment/post_comment.json})
+CASE_ID=$(./post_case.sh | jq ".id" -j)
+
+POSTED_COMMENT="$(curl -s -k \
+ -H 'Content-Type: application/json' \
+ -H 'kbn-xsrf: 123' \
+ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
+ -X POST "${KIBANA_URL}${SPACE_URL}/api/cases/$CASE_ID/comments" \
+ -d @${COMMENT} \
+ | jq '{ commentId: .id, commentVersion: .version }'
+)"
+POSTED_CASE=$(./get_case.sh $CASE_ID | jq '{ caseId: .id, caseVersion: .version }' -j)
+
+echo ${POSTED_COMMENT} ${POSTED_CASE} \
+ | jq -s add;
\ No newline at end of file
diff --git a/x-pack/plugins/case/server/scripts/generate_case_data.sh b/x-pack/plugins/case/server/scripts/generate_case_data.sh
new file mode 100755
index 0000000000000..f8f6142a5d733
--- /dev/null
+++ b/x-pack/plugins/case/server/scripts/generate_case_data.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# returns case data as { id, version }
+# Example:
+# ./generate_case_data.sh
+
+set -e
+./check_env_variables.sh
+./post_case.sh | jq '{ id: .id, version: .version }' -j;
+
diff --git a/x-pack/plugins/case/server/scripts/get_case.sh b/x-pack/plugins/case/server/scripts/get_case.sh
new file mode 100755
index 0000000000000..c0106993fd81e
--- /dev/null
+++ b/x-pack/plugins/case/server/scripts/get_case.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# Creates a new case and then gets it if no CASE_ID is specified
+
+# Example:
+# ./get_case.sh
+
+# Example with CASE_ID arg:
+# ./get_case.sh 1234-example-id
+
+set -e
+./check_env_variables.sh
+
+
+if [ "$1" ]; then
+ curl -s -k \
+ -H 'Content-Type: application/json' \
+ -H 'kbn-xsrf: 123' \
+ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
+ -X GET "${KIBANA_URL}${SPACE_URL}/api/cases/$1" \
+ | jq .;
+ exit 1
+else
+ CASE_ID=("$(./generate_case_data.sh | jq '.id' -j)")
+ curl -s -k \
+ -H 'Content-Type: application/json' \
+ -H 'kbn-xsrf: 123' \
+ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
+ -X GET "${KIBANA_URL}${SPACE_URL}/api/cases/$CASE_ID" \
+ | jq .;
+ exit 1
+fi
diff --git a/x-pack/plugins/case/server/scripts/get_case_comments.sh b/x-pack/plugins/case/server/scripts/get_case_comments.sh
new file mode 100755
index 0000000000000..65b7c43a68824
--- /dev/null
+++ b/x-pack/plugins/case/server/scripts/get_case_comments.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# Creates a new case and comments it if no CASE_ID is specified
+
+# Example:
+# ./get_case_comments.sh
+
+# Example:
+# ./get_case_comments.sh 1234-example-id
+
+set -e
+./check_env_variables.sh
+
+
+if [ "$1" ]; then
+ curl -s -k \
+ -H 'Content-Type: application/json' \
+ -H 'kbn-xsrf: 123' \
+ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
+ -X GET "${KIBANA_URL}${SPACE_URL}/api/cases/$1/comments" \
+ | jq .;
+ exit 1
+else
+ CASE_ID="$(./generate_case_and_comment_data.sh | jq '.caseId' -j)"
+ curl -s -k \
+ -H 'Content-Type: application/json' \
+ -H 'kbn-xsrf: 123' \
+ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
+ -X GET "${KIBANA_URL}${SPACE_URL}/api/cases/$CASE_ID/comments" \
+ | jq .;
+ exit 1
+fi
diff --git a/x-pack/plugins/case/server/scripts/get_comment.sh b/x-pack/plugins/case/server/scripts/get_comment.sh
new file mode 100755
index 0000000000000..9b2f7d6636745
--- /dev/null
+++ b/x-pack/plugins/case/server/scripts/get_comment.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# Creates a new case and then gets it if no CASE_ID is specified
+
+# Example:
+# ./get_comment.sh
+
+# Example with CASE_ID and COMMENT_ID arg:
+# ./get_comment.sh 1234-example-case-id 5678-example-comment-id
+
+set -e
+./check_env_variables.sh
+
+
+if [ "$1" ] && [ "$2" ]; then
+ curl -s -k \
+ -H 'Content-Type: application/json' \
+ -H 'kbn-xsrf: 123' \
+ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
+ -X GET "${KIBANA_URL}${SPACE_URL}/api/cases/$1/comments/$2" \
+ | jq .;
+ exit 1
+else
+ DATA="$(./generate_case_and_comment_data.sh | jq '{ caseId: .caseId, commentId: .commentId}' -j)"
+ CASE_ID=$(echo $DATA | jq ".caseId" -j)
+ COMMENT_ID=$(echo $DATA | jq ".commentId" -j)
+ curl -s -k \
+ -H 'Content-Type: application/json' \
+ -H 'kbn-xsrf: 123' \
+ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
+ -X GET "${KIBANA_URL}${SPACE_URL}/api/cases/$CASE_ID/comments/$COMMENT_ID" \
+ | jq .;
+ exit 1
+fi
diff --git a/x-pack/plugins/case/server/scripts/get_reporters.sh b/x-pack/plugins/case/server/scripts/get_reporters.sh
new file mode 100755
index 0000000000000..2c926269d31f8
--- /dev/null
+++ b/x-pack/plugins/case/server/scripts/get_reporters.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# Creates a new case and then gets it if no CASE_ID is specified
+
+# Example:
+# ./get_tags.sh
+
+
+set -e
+./check_env_variables.sh
+
+curl -s -k \
+-H 'Content-Type: application/json' \
+-H 'kbn-xsrf: 123' \
+-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
+-X GET "${KIBANA_URL}${SPACE_URL}/api/cases/reporters" \
+| jq .;
diff --git a/x-pack/plugins/case/server/scripts/get_status.sh b/x-pack/plugins/case/server/scripts/get_status.sh
new file mode 100755
index 0000000000000..b246a2267a222
--- /dev/null
+++ b/x-pack/plugins/case/server/scripts/get_status.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# Creates a new case and then gets it if no CASE_ID is specified
+
+# Example:
+# ./get_tags.sh
+
+
+set -e
+./check_env_variables.sh
+
+curl -s -k \
+-H 'Content-Type: application/json' \
+-H 'kbn-xsrf: 123' \
+-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
+-X GET "${KIBANA_URL}${SPACE_URL}/api/cases/status" \
+| jq .;
diff --git a/x-pack/plugins/case/server/scripts/get_tags.sh b/x-pack/plugins/case/server/scripts/get_tags.sh
new file mode 100755
index 0000000000000..c5fcf13405e0c
--- /dev/null
+++ b/x-pack/plugins/case/server/scripts/get_tags.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# Creates a new case and then gets it if no CASE_ID is specified
+
+# Example:
+# ./get_tags.sh
+
+
+set -e
+./check_env_variables.sh
+
+curl -s -k \
+-H 'Content-Type: application/json' \
+-H 'kbn-xsrf: 123' \
+-u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
+-X GET "${KIBANA_URL}${SPACE_URL}/api/cases/tags" \
+| jq .;
diff --git a/x-pack/plugins/case/server/scripts/hard_reset.sh b/x-pack/plugins/case/server/scripts/hard_reset.sh
new file mode 100755
index 0000000000000..e5309e0ab7f6c
--- /dev/null
+++ b/x-pack/plugins/case/server/scripts/hard_reset.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# Deletes all current cases and comments and creates one new case with a comment
+# Example:
+# ./hard_reset.sh
+
+set -e
+./check_env_variables.sh
+#
+ALL_CASES=$(curl -s -k \
+ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
+ -X GET "${KIBANA_URL}${SPACE_URL}/api/cases/_find?perPage=500" | jq '.cases' -j)
+
+IDS=""
+for row in $(echo "${ALL_CASES}" | jq -r '.[] | @base64'); do
+ _jq() {
+ echo ${row} | base64 --decode | jq -r ${1}
+ }
+ IDS+="$(_jq '.id') "
+done
+
+./generate_case_and_comment_data.sh
+./delete_cases.sh $IDS
\ No newline at end of file
diff --git a/x-pack/plugins/case/server/scripts/mock/case/post_case.json b/x-pack/plugins/case/server/scripts/mock/case/post_case.json
new file mode 100644
index 0000000000000..25a9780596828
--- /dev/null
+++ b/x-pack/plugins/case/server/scripts/mock/case/post_case.json
@@ -0,0 +1,8 @@
+{
+ "description": "This looks not so good",
+ "title": "Bad meanie defacing data",
+ "status": "open",
+ "tags": [
+ "defacement"
+ ]
+}
diff --git a/x-pack/plugins/case/server/scripts/mock/case/post_case_v2.json b/x-pack/plugins/case/server/scripts/mock/case/post_case_v2.json
new file mode 100644
index 0000000000000..cf066d2c8a1e8
--- /dev/null
+++ b/x-pack/plugins/case/server/scripts/mock/case/post_case_v2.json
@@ -0,0 +1,8 @@
+{
+ "description": "I hope there are some good security engineers at this company...",
+ "title": "Another bad dude",
+ "status": "open",
+ "tags": [
+ "phishing"
+ ]
+}
diff --git a/x-pack/plugins/case/server/scripts/mock/comment/post_comment.json b/x-pack/plugins/case/server/scripts/mock/comment/post_comment.json
new file mode 100644
index 0000000000000..82cf3e7ce7309
--- /dev/null
+++ b/x-pack/plugins/case/server/scripts/mock/comment/post_comment.json
@@ -0,0 +1,3 @@
+{
+ "comment": "Solve this fast!"
+}
diff --git a/x-pack/plugins/case/server/scripts/mock/comment/post_comment_v2.json b/x-pack/plugins/case/server/scripts/mock/comment/post_comment_v2.json
new file mode 100644
index 0000000000000..e753231e36911
--- /dev/null
+++ b/x-pack/plugins/case/server/scripts/mock/comment/post_comment_v2.json
@@ -0,0 +1,3 @@
+{
+ "comment": "This looks bad"
+}
diff --git a/x-pack/plugins/case/server/scripts/patch_cases.sh b/x-pack/plugins/case/server/scripts/patch_cases.sh
new file mode 100755
index 0000000000000..2faa524daac7b
--- /dev/null
+++ b/x-pack/plugins/case/server/scripts/patch_cases.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# A new case will be generated and the title will be updated in the PATCH call
+# Example:
+# ./patch_cases.sh
+
+set -e
+./check_env_variables.sh
+
+PATCH_CASE="$(./generate_case_data.sh | jq '{ cases: [{ id: .id, version: .version, title: "Change the title" }] }' -j)"
+
+curl -s -k \
+ -H 'Content-Type: application/json' \
+ -H 'kbn-xsrf: 123' \
+ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
+ -X PATCH ${KIBANA_URL}${SPACE_URL}/api/cases \
+ -d "$PATCH_CASE" \
+ | jq .;
diff --git a/x-pack/plugins/case/server/scripts/patch_comment.sh b/x-pack/plugins/case/server/scripts/patch_comment.sh
new file mode 100755
index 0000000000000..2f0bbe2883b0f
--- /dev/null
+++ b/x-pack/plugins/case/server/scripts/patch_comment.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# A new case and comment will be generated and the comment will be updated in the PATCH call
+# Example:
+# ./patch_comment.sh
+
+set -e
+./check_env_variables.sh
+
+DATA="$(./generate_case_and_comment_data.sh | jq '{ caseId: .caseId, id: .commentId, version: .commentVersion, comment: "Update the comment" }' -j)"
+CASE_ID=$(echo "${DATA}" | jq ".caseId" -j)
+PATCH_COMMENT=$(echo "${DATA}" | jq 'del(.caseId)')
+
+curl -s -k \
+ -H 'Content-Type: application/json' \
+ -H 'kbn-xsrf: 123' \
+ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
+ -X PATCH "${KIBANA_URL}${SPACE_URL}/api/cases/$CASE_ID/comments" \
+ -d "$PATCH_COMMENT" \
+ | jq .;
diff --git a/x-pack/plugins/case/server/scripts/post_case.sh b/x-pack/plugins/case/server/scripts/post_case.sh
new file mode 100755
index 0000000000000..fff449741fe17
--- /dev/null
+++ b/x-pack/plugins/case/server/scripts/post_case.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# Example:
+# ./post_case.sh
+
+# Example:
+# ./post_case.sh ./mock/case/post_case.json
+
+# Example glob:
+# ./post_case.sh ./mock/case/*
+
+set -e
+./check_env_variables.sh
+
+# Uses a default if no argument is specified
+CASES=(${@:-./mock/case/post_case.json})
+
+for CASE in "${CASES[@]}"
+do {
+ [ -e "$CASE" ] || continue
+ curl -s -k \
+ -H 'Content-Type: application/json' \
+ -H 'kbn-xsrf: 123' \
+ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
+ -X POST ${KIBANA_URL}${SPACE_URL}/api/cases \
+ -d @${CASE} \
+ | jq .;
+} &
+done
+
+wait
diff --git a/x-pack/plugins/case/server/scripts/post_comment.sh b/x-pack/plugins/case/server/scripts/post_comment.sh
new file mode 100755
index 0000000000000..91e07f5bd110c
--- /dev/null
+++ b/x-pack/plugins/case/server/scripts/post_comment.sh
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+#
+# 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.
+#
+
+# Example:
+# ./post_comment.sh
+
+# Example:
+# ./post_comment.sh 92970bf0-64a7-11ea-9979-d394b1de38af ./mock/comment/post_comment.json
+
+# Example glob:
+# ./post_comment.sh 92970bf0-64a7-11ea-9979-d394b1de38af ./mock/comment/*
+
+set -e
+./check_env_variables.sh
+
+# Uses a default if no argument is specified
+COMMENTS=(${2:-./mock/comment/post_comment.json})
+
+if [ "$1" ]; then
+ for COMMENT in "${COMMENTS[@]}"
+ do {
+ [ -e "$COMMENT" ] || continue
+ curl -s -k \
+ -H 'Content-Type: application/json' \
+ -H 'kbn-xsrf: 123' \
+ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
+ -X POST "${KIBANA_URL}${SPACE_URL}/api/cases/$1/comments" \
+ -d @${COMMENT} \
+ | jq .;
+ } &
+ done
+
+ wait
+ exit 1
+else
+ CASE_ID=("$(./generate_case_data.sh | jq '.id' -j)")
+ for COMMENT in "${COMMENTS[@]}"
+ do {
+ [ -e "$COMMENT" ] || continue
+ curl -s -k \
+ -H 'Content-Type: application/json' \
+ -H 'kbn-xsrf: 123' \
+ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \
+ -X POST "${KIBANA_URL}${SPACE_URL}/api/cases/$CASE_ID/comments" \
+ -d @${COMMENT} \
+ | jq .;
+ } &
+ done
+
+ wait
+ exit 1
+fi
\ No newline at end of file
diff --git a/x-pack/plugins/console_extensions/server/spec/generated/ml.estimate_memory_usage.json b/x-pack/plugins/console_extensions/server/spec/generated/ml.estimate_memory_usage.json
index a6ec31465392a..2195b74640c79 100644
--- a/x-pack/plugins/console_extensions/server/spec/generated/ml.estimate_memory_usage.json
+++ b/x-pack/plugins/console_extensions/server/spec/generated/ml.estimate_memory_usage.json
@@ -1,7 +1,7 @@
{
"ml.estimate_memory_usage": {
"methods": [
- "POST"
+ "PUT"
],
"patterns": [
"_ml/data_frame/analytics/_estimate_memory_usage"
diff --git a/x-pack/plugins/data_enhanced/public/search/es_search_strategy.ts b/x-pack/plugins/data_enhanced/public/search/es_search_strategy.ts
index 25c6a789cca93..c493e8ce86781 100644
--- a/x-pack/plugins/data_enhanced/public/search/es_search_strategy.ts
+++ b/x-pack/plugins/data_enhanced/public/search/es_search_strategy.ts
@@ -10,16 +10,17 @@ import {
TSearchStrategyProvider,
ISearchContext,
ISearch,
- SYNC_SEARCH_STRATEGY,
getEsPreference,
} from '../../../../../src/plugins/data/public';
import { IEnhancedEsSearchRequest, EnhancedSearchParams } from '../../common';
+import { ASYNC_SEARCH_STRATEGY } from './async_search_strategy';
+import { IAsyncSearchOptions } from './types';
export const enhancedEsSearchStrategyProvider: TSearchStrategyProvider = (
context: ISearchContext
) => {
- const syncStrategyProvider = context.getSearchStrategy(SYNC_SEARCH_STRATEGY);
- const { search: syncSearch } = syncStrategyProvider(context);
+ const asyncStrategyProvider = context.getSearchStrategy(ASYNC_SEARCH_STRATEGY);
+ const { search: asyncSearch } = asyncStrategyProvider(context);
const search: ISearch = (
request: IEnhancedEsSearchRequest,
@@ -32,9 +33,12 @@ export const enhancedEsSearchStrategyProvider: TSearchStrategyProvider;
+ const asyncOptions: IAsyncSearchOptions = { pollInterval: 0, ...options };
+
+ return asyncSearch(
+ { ...request, serverStrategy: ES_SEARCH_STRATEGY },
+ asyncOptions
+ ) as Observable;
};
return { search };
diff --git a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts
index 69b357196dc32..11f0b9a0dc83c 100644
--- a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts
+++ b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts
@@ -14,10 +14,16 @@ import {
TSearchStrategyProvider,
ISearch,
ISearchOptions,
+ ISearchCancel,
getDefaultSearchParams,
} from '../../../../../src/plugins/data/server';
import { IEnhancedEsSearchRequest } from '../../common';
+export interface AsyncSearchResponse {
+ id: string;
+ response: SearchResponse;
+}
+
export const enhancedEsSearchStrategyProvider: TSearchStrategyProvider = (
context: ISearchContext,
caller: APICaller
@@ -28,28 +34,62 @@ export const enhancedEsSearchStrategyProvider: TSearchStrategyProvider {
const config = await context.config$.pipe(first()).toPromise();
const defaultParams = getDefaultSearchParams(config);
- const params = { ...defaultParams, ...request.params };
+ const params = { ...defaultParams, trackTotalHits: true, ...request.params };
- const rawResponse = (await (request.indexType === 'rollup'
+ const response = await (request.indexType === 'rollup'
? rollupSearch(caller, { ...request, params }, options)
- : caller('search', params, options))) as SearchResponse;
+ : asyncSearch(caller, { ...request, params }, options));
+
+ const rawResponse =
+ request.indexType === 'rollup'
+ ? (response as SearchResponse)
+ : (response as AsyncSearchResponse).response;
+
+ if (typeof rawResponse.hits.total !== 'number') {
+ // @ts-ignore This should be fixed as part of https://github.com/elastic/kibana/issues/26356
+ rawResponse.hits.total = rawResponse.hits.total.value;
+ }
+ const id = (response as AsyncSearchResponse).id;
const { total, failed, successful } = rawResponse._shards;
const loaded = failed + successful;
- return { total, loaded, rawResponse };
+ return { id, total, loaded, rawResponse };
};
- return { search };
+ const cancel: ISearchCancel = async id => {
+ const method = 'DELETE';
+ const path = `_async_search/${id}`;
+ await caller('transport.request', { method, path });
+ };
+
+ return { search, cancel };
};
-function rollupSearch(
+function asyncSearch(
+ caller: APICaller,
+ request: IEnhancedEsSearchRequest,
+ options?: ISearchOptions
+) {
+ const { body = undefined, index = undefined, ...params } = request.id ? {} : request.params;
+
+ // If we have an ID, then just poll for that ID, otherwise send the entire request body
+ const method = request.id ? 'GET' : 'POST';
+ const path = request.id ? `_async_search/${request.id}` : `${index}/_async_search`;
+
+ // Wait up to 1s for the response to return
+ const query = toSnakeCase({ waitForCompletion: '1s', ...params });
+
+ return caller('transport.request', { method, path, body, query }, options);
+}
+
+async function rollupSearch(
caller: APICaller,
request: IEnhancedEsSearchRequest,
options?: ISearchOptions
) {
+ const { body, index, ...params } = request.params;
const method = 'POST';
- const path = `${request.params.index}/_rollup_search`;
- const { body, ...params } = request.params;
+ const path = `${index}/_rollup_search`;
const query = toSnakeCase(params);
return caller('transport.request', { method, path, body, query }, options);
}
diff --git a/x-pack/plugins/endpoint/common/generate_data.test.ts b/x-pack/plugins/endpoint/common/generate_data.test.ts
index a687d7af1c590..dfb906c7af606 100644
--- a/x-pack/plugins/endpoint/common/generate_data.test.ts
+++ b/x-pack/plugins/endpoint/common/generate_data.test.ts
@@ -21,8 +21,8 @@ describe('data generator', () => {
const generator1 = new EndpointDocGenerator('seed');
const generator2 = new EndpointDocGenerator('seed');
const timestamp = new Date().getTime();
- const metadata1 = generator1.generateEndpointMetadata(timestamp);
- const metadata2 = generator2.generateEndpointMetadata(timestamp);
+ const metadata1 = generator1.generateHostMetadata(timestamp);
+ const metadata2 = generator2.generateHostMetadata(timestamp);
expect(metadata1).toEqual(metadata2);
});
@@ -30,14 +30,14 @@ describe('data generator', () => {
const generator1 = new EndpointDocGenerator('seed');
const generator2 = new EndpointDocGenerator('different seed');
const timestamp = new Date().getTime();
- const metadata1 = generator1.generateEndpointMetadata(timestamp);
- const metadata2 = generator2.generateEndpointMetadata(timestamp);
+ const metadata1 = generator1.generateHostMetadata(timestamp);
+ const metadata2 = generator2.generateHostMetadata(timestamp);
expect(metadata1).not.toEqual(metadata2);
});
- it('creates endpoint metadata documents', () => {
+ it('creates host metadata documents', () => {
const timestamp = new Date().getTime();
- const metadata = generator.generateEndpointMetadata(timestamp);
+ const metadata = generator.generateHostMetadata(timestamp);
expect(metadata['@timestamp']).toEqual(timestamp);
expect(metadata.event.created).toEqual(timestamp);
expect(metadata.endpoint).not.toBeNull();
diff --git a/x-pack/plugins/endpoint/common/generate_data.ts b/x-pack/plugins/endpoint/common/generate_data.ts
index 36896e5af6810..2e1d6074d0c2f 100644
--- a/x-pack/plugins/endpoint/common/generate_data.ts
+++ b/x-pack/plugins/endpoint/common/generate_data.ts
@@ -6,7 +6,7 @@
import uuid from 'uuid';
import seedrandom from 'seedrandom';
-import { AlertEvent, EndpointEvent, EndpointMetadata, OSFields, HostFields } from './types';
+import { AlertEvent, EndpointEvent, HostMetadata, OSFields, HostFields } from './types';
export type Event = AlertEvent | EndpointEvent;
@@ -104,8 +104,8 @@ export class EndpointDocGenerator {
this.commonInfo = this.createHostData();
}
- // This function will create new values for all the host fields, so documents from a different endpoint can be created
- // This provides a convenient way to make documents from multiple endpoints that are all tied to a single seed value
+ // This function will create new values for all the host fields, so documents from a different host can be created
+ // This provides a convenient way to make documents from multiple hosts that are all tied to a single seed value
public randomizeHostData() {
this.commonInfo = this.createHostData();
}
@@ -129,7 +129,7 @@ export class EndpointDocGenerator {
};
}
- public generateEndpointMetadata(ts = new Date().getTime()): EndpointMetadata {
+ public generateHostMetadata(ts = new Date().getTime()): HostMetadata {
return {
'@timestamp': ts,
event: {
diff --git a/x-pack/plugins/endpoint/common/models/event.ts b/x-pack/plugins/endpoint/common/models/event.ts
new file mode 100644
index 0000000000000..650486f3c3858
--- /dev/null
+++ b/x-pack/plugins/endpoint/common/models/event.ts
@@ -0,0 +1,31 @@
+/*
+ * 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 { EndpointEvent, LegacyEndpointEvent } from '../types';
+
+export function isLegacyEvent(
+ event: EndpointEvent | LegacyEndpointEvent
+): event is LegacyEndpointEvent {
+ return (event as LegacyEndpointEvent).endgame !== undefined;
+}
+
+export function eventTimestamp(
+ event: EndpointEvent | LegacyEndpointEvent
+): string | undefined | number {
+ if (isLegacyEvent(event)) {
+ return event.endgame.timestamp_utc;
+ } else {
+ return event['@timestamp'];
+ }
+}
+
+export function eventName(event: EndpointEvent | LegacyEndpointEvent): string {
+ if (isLegacyEvent(event)) {
+ return event.endgame.process_name ? event.endgame.process_name : '';
+ } else {
+ return event.process.name;
+ }
+}
diff --git a/x-pack/plugins/endpoint/common/types.ts b/x-pack/plugins/endpoint/common/types.ts
index aa326c663965d..7e4cf3d700ec8 100644
--- a/x-pack/plugins/endpoint/common/types.ts
+++ b/x-pack/plugins/endpoint/common/types.ts
@@ -83,10 +83,10 @@ export interface AlertResultList {
prev: string | null;
}
-export interface EndpointResultList {
- /* the endpoints restricted by the page size */
- endpoints: EndpointMetadata[];
- /* the total number of unique endpoints in the index */
+export interface HostResultList {
+ /* the hosts restricted by the page size */
+ hosts: HostMetadata[];
+ /* the total number of unique hosts in the index */
total: number;
/* the page size requested */
request_page_size: number;
@@ -243,7 +243,7 @@ interface AlertMetadata {
*/
export type AlertData = AlertEvent & AlertMetadata;
-export interface EndpointMetadata {
+export type HostMetadata = Immutable<{
'@timestamp': number;
event: {
created: number;
@@ -258,7 +258,7 @@ export interface EndpointMetadata {
version: string;
};
host: HostFields;
-}
+}>;
/**
* Represents `total` response from Elasticsearch after ES 7.0.
@@ -311,8 +311,8 @@ export interface EndpointEvent {
version: string;
};
event: {
- category: string;
- type: string;
+ category: string | string[];
+ type: string | string[];
id: string;
kind: string;
};
@@ -328,6 +328,7 @@ export interface EndpointEvent {
name: string;
parent?: {
entity_id: string;
+ name?: string;
};
};
}
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/components/header_nav.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/components/header_nav.tsx
index f7d6551f9093b..1bafcbec93f5f 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/components/header_nav.tsx
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/components/header_nav.tsx
@@ -24,11 +24,11 @@ export const navTabs: NavTabs[] = [
href: '/',
},
{
- id: 'management',
- name: i18n.translate('xpack.endpoint.headerNav.management', {
- defaultMessage: 'Management',
+ id: 'hosts',
+ name: i18n.translate('xpack.endpoint.headerNav.hosts', {
+ defaultMessage: 'Hosts',
}),
- href: '/management',
+ href: '/hosts',
},
{
id: 'alerts',
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx
index cec51f570f95d..997113754f95d 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx
@@ -11,15 +11,17 @@ import { I18nProvider, FormattedMessage } from '@kbn/i18n/react';
import { Route, Switch, BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { Store } from 'redux';
+import { useObservable } from 'react-use';
import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public';
import { RouteCapture } from './view/route_capture';
import { EndpointPluginStartDependencies } from '../../plugin';
import { appStoreFactory } from './store';
import { AlertIndex } from './view/alerts';
-import { ManagementList } from './view/managing';
+import { HostList } from './view/hosts';
import { PolicyList } from './view/policy';
import { PolicyDetails } from './view/policy';
import { HeaderNavigation } from './components/header_nav';
+import { EuiThemeProvider } from '../../../../../legacy/common/eui_styled_components';
/**
* This module will be loaded asynchronously to reduce the bundle size of your plugin's main bundle.
@@ -48,43 +50,49 @@ interface RouterProps {
}
const AppRoot: React.FunctionComponent = React.memo(
- ({ basename, store, coreStart: { http, notifications }, depsStart: { data } }) => (
-
-
-
-
-
-
-
- (
-
-
-
- )}
- />
-
-
-
-
- (
- {
+ const isDarkMode = useObservable(uiSettings.get$('theme:darkMode'));
+
+ return (
+
+
+
+
+
+
+
+
+ (
+
+
+
+ )}
+ />
+
+
+
+
+ (
+
+ )}
/>
- )}
- />
-
-
-
-
-
-
- )
+
+
+
+
+
+
+
+ );
+ }
);
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/action.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/action.ts
index 85215238dbefc..2dce8ead38584 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/store/action.ts
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/action.ts
@@ -4,14 +4,14 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { ManagementAction } from './managing';
+import { HostAction } from './hosts';
import { AlertAction } from './alerts';
import { RoutingAction } from './routing';
import { PolicyListAction } from './policy_list';
import { PolicyDetailsAction } from './policy_details';
export type AppAction =
- | ManagementAction
+ | HostAction
| AlertAction
| RoutingAction
| PolicyListAction
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts
index 68731bb3f307f..5e9b08c09c2c7 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/alerts/selectors.ts
@@ -165,15 +165,3 @@ export const hasSelectedAlert: (state: AlertListState) => boolean = createSelect
uiQueryParams,
({ selected_alert: selectedAlert }) => selectedAlert !== undefined
);
-
-/**
- * Determine if the alert event is most likely compatible with LegacyEndpointEvent.
- */
-export const selectedAlertIsLegacyEndpointEvent: (
- state: AlertListState
-) => boolean = createSelector(selectedAlertDetailsData, function(event) {
- if (event === undefined) {
- return false;
- }
- return 'endgame' in event;
-});
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/action.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/action.ts
new file mode 100644
index 0000000000000..dee35aa3b895a
--- /dev/null
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/action.ts
@@ -0,0 +1,40 @@
+/*
+ * 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 { HostListPagination, ServerApiError } from '../../types';
+import { HostResultList, HostMetadata } from '../../../../../common/types';
+
+interface ServerReturnedHostList {
+ type: 'serverReturnedHostList';
+ payload: HostResultList;
+}
+
+interface ServerReturnedHostDetails {
+ type: 'serverReturnedHostDetails';
+ payload: HostMetadata;
+}
+
+interface ServerFailedToReturnHostDetails {
+ type: 'serverFailedToReturnHostDetails';
+ payload: ServerApiError;
+}
+
+interface UserPaginatedHostList {
+ type: 'userPaginatedHostList';
+ payload: HostListPagination;
+}
+
+// Why is FakeActionWithNoPayload here, see: https://github.com/elastic/endpoint-app-team/issues/273
+interface FakeActionWithNoPayload {
+ type: 'fakeActionWithNoPayLoad';
+}
+
+export type HostAction =
+ | ServerReturnedHostList
+ | ServerReturnedHostDetails
+ | ServerFailedToReturnHostDetails
+ | UserPaginatedHostList
+ | FakeActionWithNoPayload;
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/index.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/index.test.ts
new file mode 100644
index 0000000000000..9aff66cdfb75e
--- /dev/null
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/index.test.ts
@@ -0,0 +1,73 @@
+/*
+ * 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 { createStore, Dispatch, Store } from 'redux';
+import { HostAction, hostListReducer } from './index';
+import { HostListState } from '../../types';
+import { listData } from './selectors';
+import { mockHostResultList } from './mock_host_result_list';
+
+describe('HostList store concerns', () => {
+ let store: Store;
+ let dispatch: Dispatch;
+ const createTestStore = () => {
+ store = createStore(hostListReducer);
+ dispatch = store.dispatch;
+ };
+
+ const loadDataToStore = () => {
+ dispatch({
+ type: 'serverReturnedHostList',
+ payload: mockHostResultList({ request_page_size: 1, request_page_index: 1, total: 10 }),
+ });
+ };
+
+ describe('# Reducers', () => {
+ beforeEach(() => {
+ createTestStore();
+ });
+
+ test('it creates default state', () => {
+ expect(store.getState()).toEqual({
+ hosts: [],
+ pageSize: 10,
+ pageIndex: 0,
+ total: 0,
+ loading: false,
+ });
+ });
+
+ test('it handles `serverReturnedHostList', () => {
+ const payload = mockHostResultList({
+ request_page_size: 1,
+ request_page_index: 1,
+ total: 10,
+ });
+ dispatch({
+ type: 'serverReturnedHostList',
+ payload,
+ });
+
+ const currentState = store.getState();
+ expect(currentState.hosts).toEqual(payload.hosts);
+ expect(currentState.pageSize).toEqual(payload.request_page_size);
+ expect(currentState.pageIndex).toEqual(payload.request_page_index);
+ expect(currentState.total).toEqual(payload.total);
+ });
+ });
+
+ describe('# Selectors', () => {
+ beforeEach(() => {
+ createTestStore();
+ loadDataToStore();
+ });
+
+ test('it selects `hostListData`', () => {
+ const currentState = store.getState();
+ expect(listData(currentState)).toEqual(currentState.hosts);
+ });
+ });
+});
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/index.ts
similarity index 60%
rename from x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.ts
rename to x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/index.ts
index f0bfe27c9e30f..e80d7a82dc8cb 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.ts
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/index.ts
@@ -4,6 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
-export { managementListReducer } from './reducer';
-export { ManagementAction } from './action';
-export { managementMiddlewareFactory } from './middleware';
+export { hostListReducer } from './reducer';
+export { HostAction } from './action';
+export { hostMiddlewareFactory } from './middleware';
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.test.ts
similarity index 63%
rename from x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts
rename to x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.test.ts
index 459a1789a58da..a1973a38b6534 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.test.ts
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.test.ts
@@ -7,51 +7,40 @@ import { CoreStart, HttpSetup } from 'kibana/public';
import { applyMiddleware, createStore, Dispatch, Store } from 'redux';
import { coreMock } from '../../../../../../../../src/core/public/mocks';
import { History, createBrowserHistory } from 'history';
-import { managementListReducer, managementMiddlewareFactory } from './index';
-import { EndpointMetadata, EndpointResultList } from '../../../../../common/types';
-import { EndpointDocGenerator } from '../../../../../common/generate_data';
-import { ManagementListState } from '../../types';
+import { hostListReducer, hostMiddlewareFactory } from './index';
+import { HostResultList } from '../../../../../common/types';
+import { HostListState } from '../../types';
import { AppAction } from '../action';
import { listData } from './selectors';
import { DepsStartMock, depsStartMock } from '../../mocks';
+import { mockHostResultList } from './mock_host_result_list';
-describe('endpoint list saga', () => {
+describe('host list middleware', () => {
const sleep = (ms = 100) => new Promise(wakeup => setTimeout(wakeup, ms));
let fakeCoreStart: jest.Mocked;
let depsStart: DepsStartMock;
let fakeHttpServices: jest.Mocked;
- let store: Store;
+ let store: Store;
let getState: typeof store['getState'];
let dispatch: Dispatch;
- const generator = new EndpointDocGenerator();
- // https://github.com/elastic/endpoint-app-team/issues/131
- const generateEndpoint = (): EndpointMetadata => {
- return generator.generateEndpointMetadata();
- };
-
let history: History;
- const getEndpointListApiResponse = (): EndpointResultList => {
- return {
- endpoints: [generateEndpoint()],
- request_page_size: 1,
- request_page_index: 1,
- total: 10,
- };
+ const getEndpointListApiResponse = (): HostResultList => {
+ return mockHostResultList({ request_page_size: 1, request_page_index: 1, total: 10 });
};
beforeEach(() => {
fakeCoreStart = coreMock.createStart({ basePath: '/mock' });
depsStart = depsStartMock();
fakeHttpServices = fakeCoreStart.http as jest.Mocked;
store = createStore(
- managementListReducer,
- applyMiddleware(managementMiddlewareFactory(fakeCoreStart, depsStart))
+ hostListReducer,
+ applyMiddleware(hostMiddlewareFactory(fakeCoreStart, depsStart))
);
getState = store.getState;
dispatch = store.dispatch;
history = createBrowserHistory();
});
- test('it handles `userChangedUrl`', async () => {
+ test('handles `userChangedUrl`', async () => {
const apiResponse = getEndpointListApiResponse();
fakeHttpServices.post.mockResolvedValue(apiResponse);
expect(fakeHttpServices.post).not.toHaveBeenCalled();
@@ -60,7 +49,7 @@ describe('endpoint list saga', () => {
type: 'userChangedUrl',
payload: {
...history.location,
- pathname: '/management',
+ pathname: '/hosts',
},
});
await sleep();
@@ -69,6 +58,6 @@ describe('endpoint list saga', () => {
paging_properties: [{ page_index: 0 }, { page_size: 10 }],
}),
});
- expect(listData(getState())).toEqual(apiResponse.endpoints);
+ expect(listData(getState())).toEqual(apiResponse.hosts);
});
});
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.ts
similarity index 59%
rename from x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.ts
rename to x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.ts
index 1131e8d769fcf..9481b6633f12e 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/middleware.ts
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.ts
@@ -5,39 +5,30 @@
*/
import { MiddlewareFactory } from '../../types';
-import {
- pageIndex,
- pageSize,
- isOnManagementPage,
- hasSelectedHost,
- uiQueryParams,
-} from './selectors';
-import { ManagementListState } from '../../types';
+import { pageIndex, pageSize, isOnHostPage, hasSelectedHost, uiQueryParams } from './selectors';
+import { HostListState } from '../../types';
import { AppAction } from '../action';
-export const managementMiddlewareFactory: MiddlewareFactory = coreStart => {
+export const hostMiddlewareFactory: MiddlewareFactory = coreStart => {
return ({ getState, dispatch }) => next => async (action: AppAction) => {
next(action);
const state = getState();
if (
(action.type === 'userChangedUrl' &&
- isOnManagementPage(state) &&
+ isOnHostPage(state) &&
hasSelectedHost(state) !== true) ||
- action.type === 'userPaginatedManagementList'
+ action.type === 'userPaginatedHostList'
) {
- const managementPageIndex = pageIndex(state);
- const managementPageSize = pageSize(state);
+ const hostPageIndex = pageIndex(state);
+ const hostPageSize = pageSize(state);
const response = await coreStart.http.post('/api/endpoint/metadata', {
body: JSON.stringify({
- paging_properties: [
- { page_index: managementPageIndex },
- { page_size: managementPageSize },
- ],
+ paging_properties: [{ page_index: hostPageIndex }, { page_size: hostPageSize }],
}),
});
- response.request_page_index = managementPageIndex;
+ response.request_page_index = hostPageIndex;
dispatch({
- type: 'serverReturnedManagementList',
+ type: 'serverReturnedHostList',
payload: response,
});
}
@@ -46,12 +37,12 @@ export const managementMiddlewareFactory: MiddlewareFactory
try {
const response = await coreStart.http.get(`/api/endpoint/metadata/${selectedHost}`);
dispatch({
- type: 'serverReturnedManagementDetails',
+ type: 'serverReturnedHostDetails',
payload: response,
});
} catch (error) {
dispatch({
- type: 'serverFailedToReturnManagementDetails',
+ type: 'serverFailedToReturnHostDetails',
payload: error,
});
}
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/mock_host_result_list.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/mock_host_result_list.ts
similarity index 82%
rename from x-pack/plugins/endpoint/public/applications/endpoint/store/managing/mock_host_result_list.ts
rename to x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/mock_host_result_list.ts
index 61833d1dfb957..db39ecf448312 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/mock_host_result_list.ts
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/mock_host_result_list.ts
@@ -4,14 +4,14 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { EndpointResultList } from '../../../../../common/types';
+import { HostResultList } from '../../../../../common/types';
import { EndpointDocGenerator } from '../../../../../common/generate_data';
export const mockHostResultList: (options?: {
total?: number;
request_page_size?: number;
request_page_index?: number;
-}) => EndpointResultList = (options = {}) => {
+}) => HostResultList = (options = {}) => {
const {
total = 1,
request_page_size: requestPageSize = 10,
@@ -24,13 +24,13 @@ export const mockHostResultList: (options?: {
// 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 endpoints = [];
+ const hosts = [];
for (let index = 0; index < actualCountToReturn; index++) {
const generator = new EndpointDocGenerator('seed');
- endpoints.push(generator.generateEndpointMetadata());
+ hosts.push(generator.generateHostMetadata());
}
- const mock: EndpointResultList = {
- endpoints,
+ const mock: HostResultList = {
+ hosts,
total,
request_page_size: requestPageSize,
request_page_index: requestPageIndex,
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/reducer.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/reducer.ts
similarity index 66%
rename from x-pack/plugins/endpoint/public/applications/endpoint/store/managing/reducer.ts
rename to x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/reducer.ts
index 582aa6b7138c9..fd70317a9f37e 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/reducer.ts
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/reducer.ts
@@ -5,12 +5,12 @@
*/
import { Reducer } from 'redux';
-import { ManagementListState } from '../../types';
+import { HostListState } from '../../types';
import { AppAction } from '../action';
-const initialState = (): ManagementListState => {
+const initialState = (): HostListState => {
return {
- endpoints: [],
+ hosts: [],
pageSize: 10,
pageIndex: 0,
total: 0,
@@ -21,38 +21,36 @@ const initialState = (): ManagementListState => {
};
};
-export const managementListReducer: Reducer = (
+export const hostListReducer: Reducer = (
state = initialState(),
action
) => {
- if (action.type === 'serverReturnedManagementList') {
+ if (action.type === 'serverReturnedHostList') {
const {
- endpoints,
+ hosts,
total,
request_page_size: pageSize,
request_page_index: pageIndex,
} = action.payload;
return {
...state,
- endpoints,
+ hosts,
total,
pageSize,
pageIndex,
loading: false,
};
- } else if (action.type === 'serverReturnedManagementDetails') {
+ } else if (action.type === 'serverReturnedHostDetails') {
return {
...state,
details: action.payload,
};
- } else if (action.type === 'serverFailedToReturnManagementDetails') {
+ } else if (action.type === 'serverFailedToReturnHostDetails') {
return {
...state,
detailsError: action.payload,
};
- } else if (action.type === 'userExitedManagementList') {
- return initialState();
- } else if (action.type === 'userPaginatedManagementList') {
+ } else if (action.type === 'userPaginatedHostList') {
return {
...state,
...action.payload,
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/selectors.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/selectors.ts
new file mode 100644
index 0000000000000..ebe310cb51190
--- /dev/null
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/selectors.ts
@@ -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 querystring from 'querystring';
+import { createSelector } from 'reselect';
+import { Immutable } from '../../../../../common/types';
+import { HostListState, HostIndexUIQueryParams } from '../../types';
+
+export const listData = (state: HostListState) => state.hosts;
+
+export const pageIndex = (state: HostListState) => state.pageIndex;
+
+export const pageSize = (state: HostListState) => state.pageSize;
+
+export const totalHits = (state: HostListState) => state.total;
+
+export const isLoading = (state: HostListState) => state.loading;
+
+export const detailsError = (state: HostListState) => state.detailsError;
+
+export const detailsData = (state: HostListState) => {
+ return state.details;
+};
+
+export const isOnHostPage = (state: HostListState) =>
+ state.location ? state.location.pathname === '/hosts' : false;
+
+export const uiQueryParams: (
+ state: HostListState
+) => Immutable = createSelector(
+ (state: HostListState) => state.location,
+ (location: HostListState['location']) => {
+ const data: HostIndexUIQueryParams = {};
+ if (location) {
+ // Removes the `?` from the beginning of query string if it exists
+ const query = querystring.parse(location.search.slice(1));
+
+ const keys: Array = ['selected_host'];
+
+ for (const key of keys) {
+ const value = query[key];
+ if (typeof value === 'string') {
+ data[key] = value;
+ } else if (Array.isArray(value)) {
+ data[key] = value[value.length - 1];
+ }
+ }
+ }
+ return data;
+ }
+);
+
+export const hasSelectedHost: (state: HostListState) => boolean = createSelector(
+ uiQueryParams,
+ ({ selected_host: selectedHost }) => {
+ return selectedHost !== undefined;
+ }
+);
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/index.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/index.ts
index c051be2bb83cb..efa79b163d3b6 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/store/index.ts
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/index.ts
@@ -16,7 +16,7 @@ import {
import { CoreStart } from 'kibana/public';
import { appReducer } from './reducer';
import { alertMiddlewareFactory } from './alerts/middleware';
-import { managementMiddlewareFactory } from './managing';
+import { hostMiddlewareFactory } from './hosts';
import { policyListMiddlewareFactory } from './policy_list';
import { policyDetailsMiddlewareFactory } from './policy_details';
import { GlobalState } from '../types';
@@ -69,8 +69,8 @@ export const appStoreFactory: (middlewareDeps?: {
middleware = composeWithReduxDevTools(
applyMiddleware(
substateMiddlewareFactory(
- globalState => globalState.managementList,
- managementMiddlewareFactory(coreStart, depsStart)
+ globalState => globalState.hostList,
+ hostMiddlewareFactory(coreStart, depsStart)
),
substateMiddlewareFactory(
globalState => globalState.policyList,
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/action.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/action.ts
deleted file mode 100644
index a42e23e57d107..0000000000000
--- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/action.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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 { ManagementListPagination, ServerApiError } from '../../types';
-import { EndpointResultList, EndpointMetadata } from '../../../../../common/types';
-
-interface ServerReturnedManagementList {
- type: 'serverReturnedManagementList';
- payload: EndpointResultList;
-}
-
-interface ServerReturnedManagementDetails {
- type: 'serverReturnedManagementDetails';
- payload: EndpointMetadata;
-}
-
-interface ServerFailedToReturnManagementDetails {
- type: 'serverFailedToReturnManagementDetails';
- payload: ServerApiError;
-}
-
-interface UserExitedManagementList {
- type: 'userExitedManagementList';
-}
-
-interface UserPaginatedManagementList {
- type: 'userPaginatedManagementList';
- payload: ManagementListPagination;
-}
-
-export type ManagementAction =
- | ServerReturnedManagementList
- | ServerReturnedManagementDetails
- | ServerFailedToReturnManagementDetails
- | UserExitedManagementList
- | UserPaginatedManagementList;
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.test.ts
deleted file mode 100644
index e435fded13f4c..0000000000000
--- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/index.test.ts
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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 { createStore, Dispatch, Store } from 'redux';
-import { ManagementAction, managementListReducer } from './index';
-import { EndpointMetadata } from '../../../../../common/types';
-import { EndpointDocGenerator } from '../../../../../common/generate_data';
-import { ManagementListState } from '../../types';
-import { listData } from './selectors';
-
-describe('endpoint_list store concerns', () => {
- let store: Store;
- let dispatch: Dispatch;
- const generator = new EndpointDocGenerator();
- const createTestStore = () => {
- store = createStore(managementListReducer);
- dispatch = store.dispatch;
- };
- const generateEndpoint = (): EndpointMetadata => {
- return generator.generateEndpointMetadata();
- };
- const loadDataToStore = () => {
- dispatch({
- type: 'serverReturnedManagementList',
- payload: {
- endpoints: [generateEndpoint()],
- request_page_size: 1,
- request_page_index: 1,
- total: 10,
- },
- });
- };
-
- describe('# Reducers', () => {
- beforeEach(() => {
- createTestStore();
- });
-
- test('it creates default state', () => {
- expect(store.getState()).toEqual({
- endpoints: [],
- pageSize: 10,
- pageIndex: 0,
- total: 0,
- loading: false,
- });
- });
-
- test('it handles `serverReturnedManagementList', () => {
- const payload = {
- endpoints: [generateEndpoint()],
- request_page_size: 1,
- request_page_index: 1,
- total: 10,
- };
- dispatch({
- type: 'serverReturnedManagementList',
- payload,
- });
-
- const currentState = store.getState();
- expect(currentState.endpoints).toEqual(payload.endpoints);
- expect(currentState.pageSize).toEqual(payload.request_page_size);
- expect(currentState.pageIndex).toEqual(payload.request_page_index);
- expect(currentState.total).toEqual(payload.total);
- });
-
- test('it handles `userExitedManagementListPage`', () => {
- loadDataToStore();
-
- expect(store.getState().total).toEqual(10);
-
- dispatch({ type: 'userExitedManagementList' });
- expect(store.getState().endpoints.length).toEqual(0);
- expect(store.getState().pageIndex).toEqual(0);
- });
- });
-
- describe('# Selectors', () => {
- beforeEach(() => {
- createTestStore();
- loadDataToStore();
- });
-
- test('it selects `managementListData`', () => {
- const currentState = store.getState();
- expect(listData(currentState)).toEqual(currentState.endpoints);
- });
- });
-});
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/selectors.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/selectors.ts
deleted file mode 100644
index a7776f09fe2b8..0000000000000
--- a/x-pack/plugins/endpoint/public/applications/endpoint/store/managing/selectors.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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 querystring from 'querystring';
-import { createSelector } from 'reselect';
-import { Immutable } from '../../../../../common/types';
-import { ManagementListState, ManagingIndexUIQueryParams } from '../../types';
-
-export const listData = (state: ManagementListState) => state.endpoints;
-
-export const pageIndex = (state: ManagementListState) => state.pageIndex;
-
-export const pageSize = (state: ManagementListState) => state.pageSize;
-
-export const totalHits = (state: ManagementListState) => state.total;
-
-export const isLoading = (state: ManagementListState) => state.loading;
-
-export const detailsError = (state: ManagementListState) => state.detailsError;
-
-export const detailsData = (state: ManagementListState) => {
- return state.details;
-};
-
-export const isOnManagementPage = (state: ManagementListState) =>
- state.location ? state.location.pathname === '/management' : false;
-
-export const uiQueryParams: (
- state: ManagementListState
-) => Immutable = createSelector(
- (state: ManagementListState) => state.location,
- (location: ManagementListState['location']) => {
- const data: ManagingIndexUIQueryParams = {};
- if (location) {
- // Removes the `?` from the beginning of query string if it exists
- const query = querystring.parse(location.search.slice(1));
-
- const keys: Array = ['selected_host'];
-
- for (const key of keys) {
- const value = query[key];
- if (typeof value === 'string') {
- data[key] = value;
- } else if (Array.isArray(value)) {
- data[key] = value[value.length - 1];
- }
- }
- }
- return data;
- }
-);
-
-export const hasSelectedHost: (state: ManagementListState) => boolean = createSelector(
- uiQueryParams,
- ({ selected_host: selectedHost }) => {
- return selectedHost !== undefined;
- }
-);
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/reducer.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/reducer.ts
index e655a8d5e46db..c8b2d08676724 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/store/reducer.ts
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/reducer.ts
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { combineReducers, Reducer } from 'redux';
-import { managementListReducer } from './managing';
+import { hostListReducer } from './hosts';
import { AppAction } from './action';
import { alertListReducer } from './alerts';
import { GlobalState } from '../types';
@@ -12,7 +12,7 @@ import { policyListReducer } from './policy_list';
import { policyDetailsReducer } from './policy_details';
export const appReducer: Reducer = combineReducers({
- managementList: managementListReducer,
+ hostList: hostListReducer,
alertList: alertListReducer,
policyList: policyListReducer,
policyDetails: policyDetailsReducer,
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/types.ts b/x-pack/plugins/endpoint/public/applications/endpoint/types.ts
index 91be6e4936dbe..3045f42a93fe2 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/types.ts
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/types.ts
@@ -7,7 +7,7 @@
import { Dispatch, MiddlewareAPI } from 'redux';
import { IIndexPattern } from 'src/plugins/data/public';
import {
- EndpointMetadata,
+ HostMetadata,
AlertData,
AlertResultList,
Immutable,
@@ -25,22 +25,22 @@ export type MiddlewareFactory = (
api: MiddlewareAPI, S>
) => (next: Dispatch) => (action: AppAction) => unknown;
-export interface ManagementListState {
- endpoints: EndpointMetadata[];
- total: number;
+export interface HostListState {
+ hosts: HostMetadata[];
pageSize: number;
pageIndex: number;
+ total: number;
loading: boolean;
detailsError?: ServerApiError;
- details?: Immutable;
+ details?: Immutable;
location?: Immutable;
}
-export interface ManagementListPagination {
+export interface HostListPagination {
pageIndex: number;
pageSize: number;
}
-export interface ManagingIndexUIQueryParams {
+export interface HostIndexUIQueryParams {
selected_host?: string;
}
@@ -92,7 +92,7 @@ export interface PolicyDetailsState {
}
export interface GlobalState {
- readonly managementList: ManagementListState;
+ readonly hostList: HostListState;
readonly alertList: AlertListState;
readonly policyList: PolicyListState;
readonly policyDetails: PolicyDetailsState;
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx
index 82a4bc00a4396..0ec5a855c8615 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/details/overview/index.tsx
@@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React, { memo, useMemo } from 'react';
+import styled from 'styled-components';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import {
@@ -19,87 +20,104 @@ import * as selectors from '../../../../store/alerts/selectors';
import { MetadataPanel } from './metadata_panel';
import { FormattedDate } from '../../formatted_date';
import { AlertDetailResolver } from '../../resolver';
+import { ResolverEvent } from '../../../../../../../common/types';
import { TakeActionDropdown } from './take_action_dropdown';
-export const AlertDetailsOverview = memo(() => {
- const alertDetailsData = useAlertListSelector(selectors.selectedAlertDetailsData);
- if (alertDetailsData === undefined) {
- return null;
- }
- const selectedAlertIsLegacyEndpointEvent = useAlertListSelector(
- selectors.selectedAlertIsLegacyEndpointEvent
- );
+export const AlertDetailsOverview = styled(
+ memo(() => {
+ const alertDetailsData = useAlertListSelector(selectors.selectedAlertDetailsData);
+ if (alertDetailsData === undefined) {
+ return null;
+ }
- const tabs: EuiTabbedContentTab[] = useMemo(() => {
- return [
- {
- id: 'overviewMetadata',
- 'data-test-subj': 'overviewMetadata',
- name: i18n.translate(
- 'xpack.endpoint.application.endpoint.alertDetails.overview.tabs.overview',
- {
- defaultMessage: 'Overview',
- }
- ),
- content: (
- <>
-
-
- >
- ),
- },
- {
- id: 'overviewResolver',
- name: i18n.translate(
- 'xpack.endpoint.application.endpoint.alertDetails.overview.tabs.resolver',
- {
- defaultMessage: 'Resolver',
- }
- ),
- content: (
- <>
-
- {selectedAlertIsLegacyEndpointEvent && }
- >
- ),
- },
- ];
- }, [selectedAlertIsLegacyEndpointEvent]);
+ const tabs: EuiTabbedContentTab[] = useMemo(() => {
+ return [
+ {
+ id: 'overviewMetadata',
+ 'data-test-subj': 'overviewMetadata',
+ name: i18n.translate(
+ 'xpack.endpoint.application.endpoint.alertDetails.overview.tabs.overview',
+ {
+ defaultMessage: 'Overview',
+ }
+ ),
+ content: (
+ <>
+
+
+ >
+ ),
+ },
+ {
+ id: 'overviewResolver',
+ 'data-test-subj': 'overviewResolverTab',
+ name: i18n.translate(
+ 'xpack.endpoint.application.endpoint.alertDetails.overview.tabs.resolver',
+ {
+ defaultMessage: 'Resolver',
+ }
+ ),
+ content: (
+ <>
+
+
+ >
+ ),
+ },
+ ];
+ }, [alertDetailsData]);
- return (
- <>
-
-
-
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+ ,
+ }}
+ />
+
+
+
+
+ Endpoint Status:{' '}
+
+ {' '}
+
+
+
+
+ {' '}
-
-
-
-
-
- ,
- }}
- />
-
-
-
-
- Endpoint Status: Online
-
- Alert Status: Open
-
-
-
-
-
- >
- );
-});
+
+
+
+
+
+
+ >
+ );
+ })
+)`
+ height: 100%;
+ width: 100%;
+`;
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/resolver.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/resolver.tsx
index 52ef480bbb930..d18bc59a35f52 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/resolver.tsx
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/alerts/resolver.tsx
@@ -10,12 +10,12 @@ import { Provider } from 'react-redux';
import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public';
import { Resolver } from '../../../../embeddables/resolver/view';
import { EndpointPluginServices } from '../../../../plugin';
-import { LegacyEndpointEvent } from '../../../../../common/types';
+import { ResolverEvent } from '../../../../../common/types';
import { storeFactory } from '../../../../embeddables/resolver/store';
export const AlertDetailResolver = styled(
React.memo(
- ({ className, selectedEvent }: { className?: string; selectedEvent?: LegacyEndpointEvent }) => {
+ ({ className, selectedEvent }: { className?: string; selectedEvent?: ResolverEvent }) => {
const context = useKibana();
const { store } = storeFactory(context);
@@ -33,4 +33,5 @@ export const AlertDetailResolver = styled(
width: 100%;
display: flex;
flex-grow: 1;
+ min-height: 500px;
`;
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/formatted_date_time.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/formatted_date_time.tsx
new file mode 100644
index 0000000000000..dcf97b4b2b226
--- /dev/null
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/formatted_date_time.tsx
@@ -0,0 +1,24 @@
+/*
+ * 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 React from 'react';
+import { FormattedDate, FormattedTime, FormattedRelative } from '@kbn/i18n/react';
+
+export const FormattedDateAndTime: React.FC<{ date: Date }> = ({ date }) => {
+ // If date is greater than or equal to 1h (ago), then show it as a date
+ // else, show it as relative to "now"
+ return Date.now() - date.getTime() >= 3.6e6 ? (
+ <>
+
+ {' @'}
+
+ >
+ ) : (
+ <>
+
+ >
+ );
+};
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/details.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details.tsx
similarity index 62%
rename from x-pack/plugins/endpoint/public/applications/endpoint/view/managing/details.tsx
rename to x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details.tsx
index 9f2a732042719..37080e8568350 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/details.tsx
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/details.tsx
@@ -13,38 +13,46 @@ import {
EuiDescriptionList,
EuiLoadingContent,
EuiHorizontalRule,
+ EuiHealth,
EuiSpacer,
+ EuiListGroup,
+ EuiListGroupItem,
} from '@elastic/eui';
import { useHistory } from 'react-router-dom';
+import styled from 'styled-components';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public';
-import { useManagementListSelector } from './hooks';
+import { HostMetadata } from '../../../../../common/types';
+import { useHostListSelector } from './hooks';
import { urlFromQueryParams } from './url_from_query_params';
-import { uiQueryParams, detailsData, detailsError } from './../../store/managing/selectors';
+import { FormattedDateAndTime } from '../formatted_date_time';
+import { uiQueryParams, detailsData, detailsError } from './../../store/hosts/selectors';
-const HostDetails = memo(() => {
- const details = useManagementListSelector(detailsData);
- if (details === undefined) {
- return null;
+const HostIds = styled(EuiListGroupItem)`
+ margin-top: 0;
+ .euiListGroupItem__text {
+ padding: 0;
}
+`;
+const HostDetails = memo(({ details }: { details: HostMetadata }) => {
const detailsResultsUpper = useMemo(() => {
return [
{
- title: i18n.translate('xpack.endpoint.management.details.os', {
+ title: i18n.translate('xpack.endpoint.host.details.os', {
defaultMessage: 'OS',
}),
description: details.host.os.full,
},
{
- title: i18n.translate('xpack.endpoint.management.details.lastSeen', {
+ title: i18n.translate('xpack.endpoint.host.details.lastSeen', {
defaultMessage: 'Last Seen',
}),
- description: details['@timestamp'],
+ description: ,
},
{
- title: i18n.translate('xpack.endpoint.management.details.alerts', {
+ title: i18n.translate('xpack.endpoint.host.details.alerts', {
defaultMessage: 'Alerts',
}),
description: '0',
@@ -55,62 +63,67 @@ const HostDetails = memo(() => {
const detailsResultsLower = useMemo(() => {
return [
{
- title: i18n.translate('xpack.endpoint.management.details.policy', {
+ title: i18n.translate('xpack.endpoint.host.details.policy', {
defaultMessage: 'Policy',
}),
description: details.endpoint.policy.id,
},
{
- title: i18n.translate('xpack.endpoint.management.details.policyStatus', {
+ title: i18n.translate('xpack.endpoint.host.details.policyStatus', {
defaultMessage: 'Policy Status',
}),
- description: 'active',
+ description: active,
},
{
- title: i18n.translate('xpack.endpoint.management.details.ipAddress', {
+ title: i18n.translate('xpack.endpoint.host.details.ipAddress', {
defaultMessage: 'IP Address',
}),
- description: details.host.ip,
+ description: (
+
+ {details.host.ip.map((ip: string, index: number) => (
+
+ ))}
+
+ ),
},
{
- title: i18n.translate('xpack.endpoint.management.details.hostname', {
+ title: i18n.translate('xpack.endpoint.host.details.hostname', {
defaultMessage: 'Hostname',
}),
description: details.host.hostname,
},
{
- title: i18n.translate('xpack.endpoint.management.details.sensorVersion', {
+ title: i18n.translate('xpack.endpoint.host.details.sensorVersion', {
defaultMessage: 'Sensor Version',
}),
description: details.agent.version,
},
];
}, [details.agent.version, details.endpoint.policy.id, details.host.hostname, details.host.ip]);
-
return (
<>
>
);
});
-export const ManagementDetails = () => {
+export const HostDetailsFlyout = () => {
const history = useHistory();
const { notifications } = useKibana();
- const queryParams = useManagementListSelector(uiQueryParams);
+ const queryParams = useHostListSelector(uiQueryParams);
const { selected_host: selectedHost, ...queryParamsWithoutSelectedHost } = queryParams;
- const details = useManagementListSelector(detailsData);
- const error = useManagementListSelector(detailsError);
+ const details = useHostListSelector(detailsData);
+ const error = useHostListSelector(detailsError);
const handleFlyoutClose = useCallback(() => {
history.push(urlFromQueryParams(queryParamsWithoutSelectedHost));
@@ -121,13 +134,13 @@ export const ManagementDetails = () => {
notifications.toasts.danger({
title: (
),
body: (
),
@@ -137,10 +150,10 @@ export const ManagementDetails = () => {
}, [error, notifications.toasts]);
return (
-
+
-
+
{details === undefined ? : details.host.hostname}
@@ -151,7 +164,7 @@ export const ManagementDetails = () => {
>
) : (
-
+
)}
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/hooks.ts b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/hooks.ts
similarity index 61%
rename from x-pack/plugins/endpoint/public/applications/endpoint/view/managing/hooks.ts
rename to x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/hooks.ts
index a0720fbd8aeeb..99a0073f46c74 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/hooks.ts
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/hooks.ts
@@ -5,12 +5,10 @@
*/
import { useSelector } from 'react-redux';
-import { GlobalState, ManagementListState } from '../../types';
+import { GlobalState, HostListState } from '../../types';
-export function useManagementListSelector(
- selector: (state: ManagementListState) => TSelected
-) {
+export function useHostListSelector(selector: (state: HostListState) => TSelected) {
return useSelector(function(state: GlobalState) {
- return selector(state.managementList);
+ return selector(state.hostList);
});
}
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/index.test.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/index.test.tsx
similarity index 75%
rename from x-pack/plugins/endpoint/public/applications/endpoint/view/managing/index.test.tsx
rename to x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/index.test.tsx
index ced27ae8945b5..f6dfae99c1b11 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/index.test.tsx
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/index.test.tsx
@@ -8,15 +8,16 @@ import React from 'react';
import * as reactTestingLibrary from '@testing-library/react';
import { Provider } from 'react-redux';
import { I18nProvider } from '@kbn/i18n/react';
+import { EuiThemeProvider } from '../../../../../../../legacy/common/eui_styled_components';
import { appStoreFactory } from '../../store';
import { RouteCapture } from '../route_capture';
import { createMemoryHistory, MemoryHistory } from 'history';
import { Router } from 'react-router-dom';
import { AppAction } from '../../types';
-import { ManagementList } from './index';
-import { mockHostResultList } from '../../store/managing/mock_host_result_list';
+import { HostList } from './index';
+import { mockHostResultList } from '../../store/hosts/mock_host_result_list';
-describe('when on the managing page', () => {
+describe('when on the hosts page', () => {
let render: () => reactTestingLibrary.RenderResult;
let history: MemoryHistory;
let store: ReturnType;
@@ -28,11 +29,13 @@ describe('when on the managing page', () => {
return reactTestingLibrary.render(
-
-
-
-
-
+
+
+
+
+
+
+
);
@@ -41,7 +44,7 @@ describe('when on the managing page', () => {
it('should show a table', async () => {
const renderResult = render();
- const table = await renderResult.findByTestId('managementListTable');
+ const table = await renderResult.findByTestId('hostListTable');
expect(table).not.toBeNull();
});
@@ -49,7 +52,7 @@ describe('when on the managing page', () => {
it('should not show the flyout', () => {
const renderResult = render();
expect.assertions(1);
- return renderResult.findByTestId('managementDetailsFlyout').catch(e => {
+ return renderResult.findByTestId('hostDetailsFlyout').catch(e => {
expect(e).not.toBeNull();
});
});
@@ -57,14 +60,14 @@ describe('when on the managing page', () => {
beforeEach(() => {
reactTestingLibrary.act(() => {
const action: AppAction = {
- type: 'serverReturnedManagementList',
+ type: 'serverReturnedHostList',
payload: mockHostResultList(),
};
store.dispatch(action);
});
});
- it('should render the management summary row in the table', async () => {
+ it('should render the host summary row in the table', async () => {
const renderResult = render();
const rows = await renderResult.findAllByRole('row');
expect(rows).toHaveLength(2);
@@ -81,7 +84,7 @@ describe('when on the managing page', () => {
});
it('should show the flyout', () => {
- return renderResult.findByTestId('managementDetailsFlyout').then(flyout => {
+ return renderResult.findByTestId('hostDetailsFlyout').then(flyout => {
expect(flyout).not.toBeNull();
});
});
@@ -100,7 +103,7 @@ describe('when on the managing page', () => {
});
it('should show the flyout', () => {
const renderResult = render();
- return renderResult.findByTestId('managementDetailsFlyout').then(flyout => {
+ return renderResult.findByTestId('hostDetailsFlyout').then(flyout => {
expect(flyout).not.toBeNull();
});
});
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/index.tsx
similarity index 55%
rename from x-pack/plugins/endpoint/public/applications/endpoint/view/managing/index.tsx
rename to x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/index.tsx
index ba9a931a233b2..94625b8c66191 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/index.tsx
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/index.tsx
@@ -10,28 +10,29 @@ import { useHistory } from 'react-router-dom';
import {
EuiPage,
EuiPageBody,
+ EuiPageHeader,
EuiPageContent,
- EuiPageContentBody,
- EuiPageContentHeader,
- EuiPageContentHeaderSection,
+ EuiHorizontalRule,
EuiTitle,
EuiBasicTable,
- EuiTextColor,
+ EuiText,
EuiLink,
+ EuiHealth,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
+import styled from 'styled-components';
import { FormattedMessage } from '@kbn/i18n/react';
import { createStructuredSelector } from 'reselect';
-import { ManagementDetails } from './details';
-import * as selectors from '../../store/managing/selectors';
-import { ManagementAction } from '../../store/managing/action';
-import { useManagementListSelector } from './hooks';
+import { HostDetailsFlyout } from './details';
+import * as selectors from '../../store/hosts/selectors';
+import { HostAction } from '../../store/hosts/action';
+import { useHostListSelector } from './hooks';
import { CreateStructuredSelector } from '../../types';
import { urlFromQueryParams } from './url_from_query_params';
const selector = (createStructuredSelector as CreateStructuredSelector)(selectors);
-export const ManagementList = () => {
- const dispatch = useDispatch<(a: ManagementAction) => void>();
+export const HostList = () => {
+ const dispatch = useDispatch<(a: HostAction) => void>();
const history = useHistory();
const {
listData,
@@ -41,7 +42,7 @@ export const ManagementList = () => {
isLoading,
uiQueryParams: queryParams,
hasSelectedHost,
- } = useManagementListSelector(selector);
+ } = useHostListSelector(selector);
const paginationSetup = useMemo(() => {
return {
@@ -57,7 +58,7 @@ export const ManagementList = () => {
({ page }: { page: { index: number; size: number } }) => {
const { index, size } = page;
dispatch({
- type: 'userPaginatedManagementList',
+ type: 'userPaginatedHostList',
payload: { pageIndex: index, pageSize: size },
});
},
@@ -68,7 +69,7 @@ export const ManagementList = () => {
return [
{
field: '',
- name: i18n.translate('xpack.endpoint.management.list.host', {
+ name: i18n.translate('xpack.endpoint.host.list.hostname', {
defaultMessage: 'Hostname',
}),
render: ({ host: { hostname, id } }: { host: { hostname: string; id: string } }) => {
@@ -89,7 +90,7 @@ export const ManagementList = () => {
},
{
field: '',
- name: i18n.translate('xpack.endpoint.management.list.policy', {
+ name: i18n.translate('xpack.endpoint.host.list.policy', {
defaultMessage: 'Policy',
}),
render: () => {
@@ -98,37 +99,38 @@ export const ManagementList = () => {
},
{
field: '',
- name: i18n.translate('xpack.endpoint.management.list.policyStatus', {
+ name: i18n.translate('xpack.endpoint.host.list.policyStatus', {
defaultMessage: 'Policy Status',
}),
render: () => {
- return 'Policy Status';
+ return Policy Status;
},
},
{
field: '',
- name: i18n.translate('xpack.endpoint.management.list.alerts', {
+ name: i18n.translate('xpack.endpoint.host.list.alerts', {
defaultMessage: 'Alerts',
}),
+ dataType: 'number',
render: () => {
return '0';
},
},
{
field: 'host.os.name',
- name: i18n.translate('xpack.endpoint.management.list.os', {
+ name: i18n.translate('xpack.endpoint.host.list.os', {
defaultMessage: 'Operating System',
}),
},
{
field: 'host.ip',
- name: i18n.translate('xpack.endpoint.management.list.ip', {
+ name: i18n.translate('xpack.endpoint.host.list.ip', {
defaultMessage: 'IP Address',
}),
},
{
field: '',
- name: i18n.translate('xpack.endpoint.management.list.sensorVersion', {
+ name: i18n.translate('xpack.endpoint.host.list.sensorVersion', {
defaultMessage: 'Sensor Version',
}),
render: () => {
@@ -137,9 +139,10 @@ export const ManagementList = () => {
},
{
field: '',
- name: i18n.translate('xpack.endpoint.management.list.lastActive', {
+ name: i18n.translate('xpack.endpoint.host.list.lastActive', {
defaultMessage: 'Last Active',
}),
+ dataType: 'date',
render: () => {
return 'xxxx';
},
@@ -148,45 +151,59 @@ export const ManagementList = () => {
}, [queryParams, history]);
return (
- <>
- {hasSelectedHost && }
-
+
+ {hasSelectedHost && }
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
+
+
+
- >
+
);
};
+
+const HostPage = styled.div`
+ .hostPage {
+ padding: 0;
+ }
+ .hostHeader {
+ background-color: ${props => props.theme.eui.euiColorLightestShade};
+ border-bottom: ${props => props.theme.eui.euiBorderThin};
+ padding: ${props =>
+ props.theme.eui.euiSizeXL +
+ ' ' +
+ 0 +
+ props.theme.eui.euiSizeXL +
+ ' ' +
+ props.theme.eui.euiSizeL};
+ margin-bottom: 0;
+ }
+ .hostPageContent {
+ border: none;
+ }
+`;
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/url_from_query_params.ts b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/url_from_query_params.ts
similarity index 78%
rename from x-pack/plugins/endpoint/public/applications/endpoint/view/managing/url_from_query_params.ts
rename to x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/url_from_query_params.ts
index ea6a4c6f684ad..225aad8cab020 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/view/managing/url_from_query_params.ts
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/hosts/url_from_query_params.ts
@@ -5,10 +5,10 @@
*/
import querystring from 'querystring';
-import { EndpointAppLocation, ManagingIndexUIQueryParams } from '../../types';
+import { EndpointAppLocation, HostIndexUIQueryParams } from '../../types';
export function urlFromQueryParams(
- queryParams: ManagingIndexUIQueryParams
+ queryParams: HostIndexUIQueryParams
): Partial {
const search = querystring.stringify(queryParams);
return {
diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_list.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_list.tsx
index cf573da3703cc..e7ce53679bbe7 100644
--- a/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_list.tsx
+++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_list.tsx
@@ -20,17 +20,12 @@ import {
EuiLink,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
-import {
- FormattedMessage,
- FormattedDate,
- FormattedTime,
- FormattedNumber,
- FormattedRelative,
-} from '@kbn/i18n/react';
+import { FormattedMessage, FormattedNumber } from '@kbn/i18n/react';
import { useDispatch } from 'react-redux';
import styled from 'styled-components';
import { useHistory } from 'react-router-dom';
import { usePageId } from '../use_page_id';
+import { FormattedDateAndTime } from '../formatted_date_time';
import {
selectIsLoading,
selectPageIndex,
@@ -56,22 +51,6 @@ const TruncateTooltipText = styled(TruncateText)`
}
`;
-const FormattedDateAndTime: React.FC<{ date: Date }> = ({ date }) => {
- // If date is greater than or equal to 24h (ago), then show it as a date
- // else, show it as relative to "now"
- return Date.now() - date.getTime() >= 8.64e7 ? (
- <>
-
- {' @'}
-
- >
- ) : (
- <>
-
- >
- );
-};
-
const PolicyLink: React.FC<{ name: string; route: string }> = ({ name, route }) => {
const history = useHistory();
diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/models/indexed_process_tree.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/models/indexed_process_tree.ts
index 6892bf11ecff2..c9a03f0a47968 100644
--- a/x-pack/plugins/endpoint/public/embeddables/resolver/models/indexed_process_tree.ts
+++ b/x-pack/plugins/endpoint/public/embeddables/resolver/models/indexed_process_tree.ts
@@ -6,15 +6,15 @@
import { uniquePidForProcess, uniqueParentPidForProcess } from './process_event';
import { IndexedProcessTree } from '../types';
-import { LegacyEndpointEvent } from '../../../../common/types';
+import { ResolverEvent } from '../../../../common/types';
import { levelOrder as baseLevelOrder } from '../lib/tree_sequencers';
/**
* Create a new IndexedProcessTree from an array of ProcessEvents
*/
-export function factory(processes: LegacyEndpointEvent[]): IndexedProcessTree {
- const idToChildren = new Map();
- const idToValue = new Map();
+export function factory(processes: ResolverEvent[]): IndexedProcessTree {
+ const idToChildren = new Map();
+ const idToValue = new Map();
for (const process of processes) {
idToValue.set(uniquePidForProcess(process), process);
@@ -36,10 +36,7 @@ export function factory(processes: LegacyEndpointEvent[]): IndexedProcessTree {
/**
* Returns an array with any children `ProcessEvent`s of the passed in `process`
*/
-export function children(
- tree: IndexedProcessTree,
- process: LegacyEndpointEvent
-): LegacyEndpointEvent[] {
+export function children(tree: IndexedProcessTree, process: ResolverEvent): ResolverEvent[] {
const id = uniquePidForProcess(process);
const processChildren = tree.idToChildren.get(id);
return processChildren === undefined ? [] : processChildren;
@@ -50,8 +47,8 @@ export function children(
*/
export function parent(
tree: IndexedProcessTree,
- childProcess: LegacyEndpointEvent
-): LegacyEndpointEvent | undefined {
+ childProcess: ResolverEvent
+): ResolverEvent | undefined {
const uniqueParentPid = uniqueParentPidForProcess(childProcess);
if (uniqueParentPid === undefined) {
return undefined;
@@ -74,7 +71,7 @@ export function root(tree: IndexedProcessTree) {
if (size(tree) === 0) {
return null;
}
- let current: LegacyEndpointEvent = tree.idToProcess.values().next().value;
+ let current: ResolverEvent = tree.idToProcess.values().next().value;
while (parent(tree, current) !== undefined) {
current = parent(tree, current)!;
}
diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/models/process_event.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/models/process_event.ts
index 876168d2ed96a..a709d6caf46cb 100644
--- a/x-pack/plugins/endpoint/public/embeddables/resolver/models/process_event.ts
+++ b/x-pack/plugins/endpoint/public/embeddables/resolver/models/process_event.ts
@@ -4,36 +4,65 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { LegacyEndpointEvent } from '../../../../common/types';
+import { ResolverEvent } from '../../../../common/types';
+import * as event from '../../../../common/models/event';
+import { ResolverProcessType } from '../types';
/**
* Returns true if the process's eventType is either 'processCreated' or 'processRan'.
* Resolver will only render 'graphable' process events.
*/
-export function isGraphableProcess(passedEvent: LegacyEndpointEvent) {
+export function isGraphableProcess(passedEvent: ResolverEvent) {
return eventType(passedEvent) === 'processCreated' || eventType(passedEvent) === 'processRan';
}
+function isValue(field: string | string[], value: string) {
+ if (field instanceof Array) {
+ return field.length === 1 && field[0] === value;
+ } else {
+ return field === value;
+ }
+}
+
/**
* Returns a custom event type for a process event based on the event's metadata.
*/
-export function eventType(passedEvent: LegacyEndpointEvent) {
- const {
- endgame: { event_type_full: type, event_subtype_full: subType },
- } = passedEvent;
+export function eventType(passedEvent: ResolverEvent): ResolverProcessType {
+ if (event.isLegacyEvent(passedEvent)) {
+ const {
+ endgame: { event_type_full: type, event_subtype_full: subType },
+ } = passedEvent;
- if (type === 'process_event') {
- if (subType === 'creation_event' || subType === 'fork_event' || subType === 'exec_event') {
- return 'processCreated';
- } else if (subType === 'already_running') {
- return 'processRan';
- } else if (subType === 'termination_event') {
- return 'processTerminated';
- } else {
- return 'unknownProcessEvent';
+ if (type === 'process_event') {
+ if (subType === 'creation_event' || subType === 'fork_event' || subType === 'exec_event') {
+ return 'processCreated';
+ } else if (subType === 'already_running') {
+ return 'processRan';
+ } else if (subType === 'termination_event') {
+ return 'processTerminated';
+ } else {
+ return 'unknownProcessEvent';
+ }
+ } else if (type === 'alert_event') {
+ return 'processCausedAlert';
+ }
+ } else {
+ const {
+ event: { type, category, kind },
+ } = passedEvent;
+ if (isValue(category, 'process')) {
+ if (isValue(type, 'start') || isValue(type, 'change') || isValue(type, 'creation')) {
+ return 'processCreated';
+ } else if (isValue(type, 'info')) {
+ return 'processRan';
+ } else if (isValue(type, 'end')) {
+ return 'processTerminated';
+ } else {
+ return 'unknownProcessEvent';
+ }
+ } else if (kind === 'alert') {
+ return 'processCausedAlert';
}
- } else if (type === 'alert_event') {
- return 'processCausedAlert';
}
return 'unknownEvent';
}
@@ -41,13 +70,21 @@ export function eventType(passedEvent: LegacyEndpointEvent) {
/**
* Returns the process event's pid
*/
-export function uniquePidForProcess(event: LegacyEndpointEvent) {
- return event.endgame.unique_pid;
+export function uniquePidForProcess(passedEvent: ResolverEvent): string {
+ if (event.isLegacyEvent(passedEvent)) {
+ return String(passedEvent.endgame.unique_pid);
+ } else {
+ return passedEvent.process.entity_id;
+ }
}
/**
* Returns the process event's parent pid
*/
-export function uniqueParentPidForProcess(event: LegacyEndpointEvent) {
- return event.endgame.unique_ppid;
+export function uniqueParentPidForProcess(passedEvent: ResolverEvent): string | undefined {
+ if (event.isLegacyEvent(passedEvent)) {
+ return String(passedEvent.endgame.unique_ppid);
+ } else {
+ return passedEvent.process.parent?.entity_id;
+ }
}
diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/actions.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/actions.ts
index ecba0ec404d44..fec2078cc60c9 100644
--- a/x-pack/plugins/endpoint/public/embeddables/resolver/store/actions.ts
+++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/actions.ts
@@ -5,7 +5,7 @@
*/
import { CameraAction } from './camera';
import { DataAction } from './data';
-import { LegacyEndpointEvent } from '../../../../common/types';
+import { ResolverEvent } from '../../../../common/types';
/**
* When the user wants to bring a process node front-and-center on the map.
@@ -16,7 +16,7 @@ interface UserBroughtProcessIntoView {
/**
* Used to identify the process node that should be brought into view.
*/
- readonly process: LegacyEndpointEvent;
+ readonly process: ResolverEvent;
/**
* The time (since epoch in milliseconds) when the action was dispatched.
*/
@@ -33,7 +33,7 @@ interface UserChangedSelectedEvent {
/**
* Optional because they could have unselected the event.
*/
- selectedEvent?: LegacyEndpointEvent;
+ readonly selectedEvent?: ResolverEvent;
};
}
diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/action.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/action.ts
index f34d7c08ce08c..373afa89921dc 100644
--- a/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/action.ts
+++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/action.ts
@@ -4,14 +4,14 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { LegacyEndpointEvent } from '../../../../../common/types';
+import { ResolverEvent } from '../../../../../common/types';
interface ServerReturnedResolverData {
readonly type: 'serverReturnedResolverData';
readonly payload: {
readonly data: {
readonly result: {
- readonly search_results: readonly LegacyEndpointEvent[];
+ readonly search_results: readonly ResolverEvent[];
};
};
};
diff --git a/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/selectors.ts b/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/selectors.ts
index 304abbb06880b..e8007f82e30c2 100644
--- a/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/selectors.ts
+++ b/x-pack/plugins/endpoint/public/embeddables/resolver/store/data/selectors.ts
@@ -14,7 +14,7 @@ import {
ProcessWithWidthMetadata,
Matrix3,
} from '../../types';
-import { LegacyEndpointEvent } from '../../../../../common/types';
+import { ResolverEvent } from '../../../../../common/types';
import { Vector2 } from '../../types';
import { add as vector2Add, applyMatrix3 } from '../../lib/vector2';
import { isGraphableProcess } from '../../models/process_event';
@@ -112,7 +112,7 @@ export const graphableProcesses = createSelector(
*
*/
function widthsOfProcessSubtrees(indexedProcessTree: IndexedProcessTree): ProcessWidths {
- const widths = new Map();
+ const widths = new Map();
if (size(indexedProcessTree) === 0) {
return widths;
@@ -313,13 +313,13 @@ function processPositions(
indexedProcessTree: IndexedProcessTree,
widths: ProcessWidths
): ProcessPositions {
- const positions = new Map();
+ const positions = new Map();
/**
* This algorithm iterates the tree in level order. It keeps counters that are reset for each parent.
* By keeping track of the last parent node, we can know when we are dealing with a new set of siblings and
* reset the counters.
*/
- let lastProcessedParentNode: LegacyEndpointEvent | undefined;
+ let lastProcessedParentNode: ResolverEvent | undefined;
/**
* Nodes are positioned relative to their siblings. We walk this in level order, so we handle
* children left -> right.
@@ -424,7 +424,7 @@ export const processNodePositionsAndEdgeLineSegments = createSelector(
* Transform the positions of nodes and edges so they seem like they are on an isometric grid.
*/
const transformedEdgeLineSegments: EdgeLineSegment[] = [];
- const transformedPositions = new Map();
+ const transformedPositions = new Map