Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[Endpoint] Basic Functionality Alert List #55800

Merged
merged 9 commits into from
Jan 29, 2020
52 changes: 52 additions & 0 deletions x-pack/plugins/endpoint/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,24 @@
* you may not use this file except in compliance with the Elastic License.
*/

/**
* A deep readonly type that will make all children of a given object readonly recursively
*/
export type Immutable<T> = T extends undefined | null | boolean | string | number
? T
: T extends Array<infer U>
? ImmutableArray<U>
: T extends Map<infer K, infer V>
? ImmutableMap<K, V>
: T extends Set<infer M>
? ImmutableSet<M>
: ImmutableObject<T>;

export type ImmutableArray<T> = ReadonlyArray<Immutable<T>>;
Copy link
Contributor

Choose a reason for hiding this comment

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

please add a comment to all exported identifiers

export type ImmutableMap<K, V> = ReadonlyMap<Immutable<K>, Immutable<V>>;
export type ImmutableSet<T> = ReadonlySet<Immutable<T>>;
export type ImmutableObject<T> = { readonly [K in keyof T]: Immutable<T[K]> };

export class EndpointAppConstants {
static ENDPOINT_INDEX_NAME = 'endpoint-agent*';
}
Expand Down Expand Up @@ -44,3 +62,37 @@ export interface EndpointMetadata {
};
};
}

export type AlertData = Immutable<{
Copy link
Contributor

Choose a reason for hiding this comment

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

Why should this be Immutable?

Copy link
Contributor

Choose a reason for hiding this comment

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

There should be one section of the alert data that is mutable... otherwise, it should all be immutable.

Copy link
Contributor

Choose a reason for hiding this comment

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

done

Copy link
Contributor

Choose a reason for hiding this comment

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

Please add a comment (in jsdoc style) to each field.

Copy link
Contributor

Choose a reason for hiding this comment

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

This data will change very soon to conform to ECS. We will document more once the format is closer to the final format. Right now we are using outdated fake data.

value: {
source: {
endgame: {
data: {
file_operation: string;
malware_classification: {
score: number;
};
};
metadata: {
key: string;
};
timestamp_utc: Date;
};
labels: {
endpoint_id: string;
};
host: {
hostname: string;
ip: string;
os: {
name: string;
};
};
};
};
}>;

/**
* The PageId type is used for the payload when firing userNavigatedToPage actions
*/
export type PageId = 'alertsPage' | 'endpointListPage';
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { Route, BrowserRouter, Switch } from 'react-router-dom';
import { Provider } from 'react-redux';
import { Store } from 'redux';
import { appStoreFactory } from './store';
import { AlertIndex } from './view/alerts';

/**
* This module will be loaded asynchronously to reduce the bundle size of your plugin's main bundle.
Expand Down Expand Up @@ -64,6 +65,7 @@ const AppRoot: React.FunctionComponent<RouterProps> = React.memo(({ basename, st
);
}}
/>
<Route path="/alerts" component={AlertIndex} />
<Route
render={() => (
<FormattedMessage id="xpack.endpoint.notFound" defaultMessage="Page Not Found" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* 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 { EndpointListAction } from './endpoint_list';
import { AlertAction } from './alerts';
import { RoutingAction } from './routing';

export type AppAction = EndpointListAction | AlertAction | RoutingAction;
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* 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 { AlertData } from '../../../../../common/types';
Copy link
Contributor

Choose a reason for hiding this comment

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

Thoughts about setting a baseUrl to remove these relative imports? https://www.typescriptlang.org/docs/handbook/module-resolution.html#base-url

I'm not sure if we are going to be able to maintain our own tsconfig.json or if we are sharing one with the rest of Kibana.

Copy link
Contributor

Choose a reason for hiding this comment

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

@alexk307 We are currently not using our own tsconfig.json. We could look into this, but i think it's fine for now to use relative imports. Most IDE's that provide typescript support like VSCode or Vim make relative imports pretty easy.

Copy link
Contributor

Choose a reason for hiding this comment

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

FYI - I seem remember a discussion from the Kibana platform team that suggests this is something they want to look at and possibly introduce aliases that will simplify the import paths. Found discussion on Kibana issues (look at Issue 40446).


interface ServerReturnedAlertsData {
Copy link
Contributor

Choose a reason for hiding this comment

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

this would be a better place for Immutable since the code relies on the fact that action payloads aren't mutated. as it is, the payload array is mutable.

// maybe
interface ServerReturnedAlertsData {  
  readonly type: 'serverReturnedAlertsData';
  readonly payload: readonly AlertData[];
}

// or
type ServerReturnedAlertsData = Immutable<{  
  type: 'serverReturnedAlertsData';
  payload: AlertData[];
}>;

Copy link
Contributor

Choose a reason for hiding this comment

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

done

readonly type: 'serverReturnedAlertsData';

readonly payload: AlertData[];
}

export type AlertAction = ServerReturnedAlertsData;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* 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.
*/

export { alertListReducer } from './reducer';
export { AlertAction } from './action';
export * from './types';
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
Copy link
Contributor

Choose a reason for hiding this comment

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

We are using a simple middleware to handle side-effects here, as opposed to using oatSaga. The idea is that we shouldn't overcomplicate the app from the start. oatSaga, though simpler than redux-saga, still has a fair amount of complexity in its usage. There is no need to have this complexity now, though over time we may feel like adding some more functionality. The other supporting argument to using this simpler approach is that newcomers to the team will most likely be familiar with Redux and the basic concepts of middlewares, but they won't know about oatSaga. Thoughts @oatkiller @kevinlog @paul-tavares @parkiino.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm good with this approach as well - much simpler

Copy link
Contributor

@oatkiller oatkiller Jan 24, 2020

Choose a reason for hiding this comment

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

image

seems like oatsaga has more adoption
image

Copy link
Contributor

@kevinlog kevinlog Jan 24, 2020

Choose a reason for hiding this comment

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

@peluja1012 what are some of the main differences here? It looks like we're still using appSagaFactory below which, as far as I can tell uses the existing createSagaMiddleware. Is the plan to drop some of the other functionality from https://github.com/elastic/kibana/blob/master/x-pack/plugins/endpoint/public/applications/endpoint/lib/saga.ts ?

Let me know what I'm missing @oatkiller @paul-tavares @parkiino

I'm all for simplicity, just wanna better understand what we're proposing that we remove.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we're proposing to remove anything. Just that the alerting feature will (initially anyway) forgo lib/saga and instead just make its simple middleware:

The logic consists of:

  1. call the next middleware
  2. Check if the action is a certain action (page loaded)
  3. if so, make an http request and dispatch an action when thats done.

Redux middleware, by default, provides the ability to run code on each action. lib/saga's main addition to that is to provide the stream of actions as an async generator.

* 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 { Dispatch, MiddlewareAPI } from 'redux';
import { CoreStart } from 'kibana/public';
import { AlertData } from '../../../../../common/types';
import { GlobalState } from '../reducer';
import { AppAction } from '../action';

// TODO, move this somewhere
Copy link
Contributor

Choose a reason for hiding this comment

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

DO IT then

Copy link
Contributor

Choose a reason for hiding this comment

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

done

type MiddlewareFactory = (
coreStart: CoreStart
) => (
api: MiddlewareAPI<Dispatch<AppAction>, GlobalState>
) => (next: Dispatch<AppAction>) => (action: AppAction) => unknown;

export const alertMiddlewareFactory: MiddlewareFactory = coreStart => {
return store => next => async (action: AppAction) => {
next(action);
if (action.type === 'userNavigatedToPage' && action.payload === 'alertsPage') {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm assuming by your above TODO, that all of this is still moving code for now, but would we be to define these action types in a types.ts file, so they're easy to use anywhere?

Copy link
Contributor

Choose a reason for hiding this comment

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

@alexk307 We define actions in files like this one https://github.com/elastic/kibana/pull/55800/files#diff-c971622c4d5a45ad624ef764513a0131. The type checker will throw and error if you use an invalid action type in this if-statement. So if we were to do if (action.type === 'fooAction'), the type checker will throw an error because fooAction is not of type AppAction.

const response: AlertData[] = await coreStart.http.get('/api/endpoint/alerts');
Copy link
Contributor

Choose a reason for hiding this comment

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

should be read only ya?

Copy link
Contributor

Choose a reason for hiding this comment

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

done

store.dispatch({ type: 'serverReturnedAlertsData', payload: response });
}
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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 { Reducer } from 'redux';
import { AlertListState } from './types';
import { AppAction } from '../action';

const initialState = (): AlertListState => {
return {
alerts: [],
};
};

export const alertListReducer: Reducer<AlertListState, AppAction> = (
Copy link
Contributor

Choose a reason for hiding this comment

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

Are you using index vs page vs `list consistently here?

Some suggestions:

The feature (aka concern) is called 'alerting' and the directory takes this name.
The component that shows a list of alerts: 'alertList'
The route is called 'alertsIndex' assuming that it's written 'alerts/'
The component that renders the page is called 'alertingPage'.
@peluja1012 can explain that we might end up with a single route that shows an alert list page, an alert detail page, or both at the same time.

Copy link
Contributor

Choose a reason for hiding this comment

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

Will address this in the next PR where I add a stub alert detail

state = initialState(),
action
) => {
if (action.type === 'serverReturnedAlertsData') {
Copy link
Contributor

Choose a reason for hiding this comment

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

You may want to consider (in the future) "reseting" the state when a user leaves the Alerts list so that data stored here does not take up memory in the browser.

return {
...state,
alerts: action.payload,
};
}

return state;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* 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 { AlertListState } from './types';

export const alertListData = (state: AlertListState) => state.alerts;
Copy link
Contributor

Choose a reason for hiding this comment

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

consider naming this type AlertingState and this reducer alertingReducer and the naming the piece of global state alerting

Copy link
Contributor

Choose a reason for hiding this comment

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

Will address this in the next PR where I add a stub alert detail

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
Copy link
Contributor

Choose a reason for hiding this comment

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

The types in this file would move the main types file at the top level.

Copy link
Contributor

Choose a reason for hiding this comment

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

On the topic of "top level types" and given my prior comment re: PR #54772 - These types here (and in store/endpoint_list/ ) are likely not to be needed by BE. Should we instead create a under plugin/endpoint/public/types.ts and place all FE specific types there? (and use /plugin/endpoint/common/types.ts for those shared across BE/FE?

Copy link
Contributor

Choose a reason for hiding this comment

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

@paul-tavares Moved the types to common/types.ts

* 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 { Immutable, AlertData } from '../../../../../common/types';

export type AlertListState = Immutable<{
alerts: AlertData[];

This comment was marked as resolved.

}>;
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { Reducer } from 'redux';
import { EndpointListState } from './types';
import { EndpointListAction } from './action';
import { AppAction } from '../action';

const initialState = (): EndpointListState => {
return {
Expand All @@ -16,7 +17,10 @@ const initialState = (): EndpointListState => {
};
};

export const endpointListReducer = (state = initialState(), action: EndpointListAction) => {
export const endpointListReducer: Reducer<EndpointListState, AppAction> = (
Copy link
Contributor

Choose a reason for hiding this comment

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

Q. Why AppAction and not EndpointListAction?
is it so that in the future we could take advantage of other concern's Actions?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

its because combineReducers passes in all actions, not just EndpointListAction

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah Ok - but were you getting TS errors?

I think the benefit was that types shown here would be narrowed down to only the ones this concern cares about.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We weren't getting errors but I think to your previous point it might be good to take advantage of being able to use other concern's or generic/global app actions in the future and this would keep that ability. Kind of like how we have the page navigated actions now. I do see what you're saying though, i don't necessarily disagree with it

Copy link
Contributor

Choose a reason for hiding this comment

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

👍

Copy link
Contributor

Choose a reason for hiding this comment

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

even if we filter out non-concern specific actions in the future, they aren't yet filtered out, so this type is more correct

state = initialState(),
action
) => {
if (action.type === 'serverReturnedEndpointList') {
return {
...state,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { createStore, compose, applyMiddleware, Store } from 'redux';
import { CoreStart } from 'kibana/public';
import { appSagaFactory } from './saga';
import { appReducer } from './reducer';
import { alertMiddlewareFactory } from './alerts/middleware';

export { GlobalState } from './reducer';

Expand All @@ -19,7 +20,9 @@ export const appStoreFactory = (coreStart: CoreStart): [Store, () => void] => {
const sagaReduxMiddleware = appSagaFactory(coreStart);
const store = createStore(
appReducer,
composeWithReduxDevTools(applyMiddleware(sagaReduxMiddleware))
composeWithReduxDevTools(
applyMiddleware(alertMiddlewareFactory(coreStart), appSagaFactory(coreStart))
)
);

sagaReduxMiddleware.start();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@
*/
import { combineReducers, Reducer } from 'redux';
import { endpointListReducer, EndpointListState } from './endpoint_list';
import { AppAction } from './actions';
import { AppAction } from './action';
import { AlertListState, alertListReducer } from './alerts';

export interface GlobalState {
endpointList: EndpointListState;
readonly endpointList: EndpointListState;
readonly alertList: AlertListState;
}

export const appReducer: Reducer<GlobalState, AppAction> = combineReducers({
endpointList: endpointListReducer,
alertList: alertListReducer,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* 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 { PageId } from '../../../../../common/types';

interface UserNavigatedToPage {
readonly type: 'userNavigatedToPage';
readonly payload: PageId;
}

export type RoutingAction = UserNavigatedToPage;
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { EndpointListAction } from './endpoint_list';

export type AppAction = EndpointListAction;
export { RoutingAction } from './action';
Original file line number Diff line number Diff line change
@@ -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 { GlobalState } from './reducer';
import * as alertListSelectors from './alerts/selectors';

export const alertListData = composeSelectors(
Copy link
Contributor

Choose a reason for hiding this comment

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

FYI.
@parkiino will be pushing through a PR soon that takes a different approach for using a selector from a React component. Instead of having to re-define all your "context bound" selectors again here so that they receive the correct namespace from the GlobalStore, instead we're proposing having a hook like:

File: store/hooks.ts:

export function useEndpointListSelector<TSelected>(
  selector: (state: EndpointListState) => TSelected
) {
  return useSelector(function(state: GlobalState) {
    return selector(state.endpointList);
  });
}

The idea is that each of the concerns will have an associated hook. To use this in a component, you would:

import { endpointListData } from "../store/endpoint_list/selectors;
import { useEndpointListSelector } from "../store/hooks;

const EndpointsListPage = () => {
    const listData = useEndpointListSelector(endpointListData);

    return (<div>...</div>)
}

(Credit to @oatkiller for the idea during a recent discussion 😃 )

Copy link
Contributor

Choose a reason for hiding this comment

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

@paul-tavares If we use this approach, how would a middleware (or saga) use the selector if needed to grab some data from state?

Copy link
Contributor

Choose a reason for hiding this comment

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

@peluja1012 - From a saga or middleware we have access directly to the store and can get back GlobalState - so you would use the concern selector directly and feed it its namespace (ex. endpointListData(store.getState().endpointList)).

When we discussed this, the goal was to avoid the need to define all concerns selectors a second time in order for us to continue to use react-redux useSelector(). The hook does that nicely 😃

alertListStateSelector,
alertListSelectors.alertListData
);

/**
* Returns the alert list state from within Global State
*/
function alertListStateSelector(state: GlobalState) {
return state.alertList;
}

/**
* Calls the `secondSelector` with the result of the `selector`. Use this when re-exporting a
* concern-specific selector. `selector` should return the concern-specific state.
*/
function composeSelectors<OuterState, InnerState, ReturnValue>(
Copy link
Contributor

Choose a reason for hiding this comment

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

selector: (state: OuterState) => InnerState,
secondSelector: (state: InnerState) => ReturnValue
): (state: OuterState) => ReturnValue {
return state => secondSelector(selector(state));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* 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 { memo, useState, useMemo } from 'react';
import React from 'react';
import { EuiDataGrid } from '@elastic/eui';
import { useSelector } from 'react-redux';
import * as selectors from '../../store/selectors';
import { usePageId } from '../use_page_id';

export const AlertIndex = memo(() => {
usePageId('alertsPage');
Copy link
Contributor

Choose a reason for hiding this comment

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

This is where we call the usePageId hook, which will dispatch a userNavigatedToPage action.


const columns: Array<{ id: string }> = [
Copy link
Contributor

Choose a reason for hiding this comment

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

consider wrapping this in useMemo so that you don't pass a new array ref to EuiGrid each render

{ id: 'alert_type' },
{ id: 'event_type' },
{ id: 'os' },
{ id: 'ip_address' },
{ id: 'host_name' },
{ id: 'timestamp' },
{ id: 'archived' },
{ id: 'malware_score' },
];

const [visibleColumns, setVisibleColumns] = useState(() => columns.map(({ id }) => id));

const json = useSelector(selectors.alertListData);
Copy link
Contributor

Choose a reason for hiding this comment

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

This is pretty neat


const renderCellValue = useMemo(() => {
return ({ rowIndex, columnId }: { rowIndex: number; columnId: string }) => {
if (json.length === 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe instead

if (rowIndex >= json.length) {
  return null
} else {
  const row = json[rowIndex]
  // use `row` from here on instead of `json[rowIndex]`
}

Copy link
Contributor

Choose a reason for hiding this comment

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

done

return null;
}

if (columnId === 'alert_type') {
return json[rowIndex].value.source.endgame.metadata.key;
} else if (columnId === 'event_type') {
return json[rowIndex].value.source.endgame.data.file_operation;
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you confirm that all alerts are guaranteed to have this field?

Copy link
Contributor

Choose a reason for hiding this comment

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

We will do this properly in future PRs. We just want to get some basic code structure and patterns merged now.

} else if (columnId === 'os') {
return json[rowIndex].value.source.host.os.name;
} else if (columnId === 'ip_address') {
return json[rowIndex].value.source.host.ip;
} else if (columnId === 'host_name') {
return json[rowIndex].value.source.host.hostname;
} else if (columnId === 'timestamp') {
return json[rowIndex].value.source.endgame.timestamp_utc;
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks. We will add intl in future PRs. We just want to get some basic code structure and patterns merged now.

} else if (columnId === 'archived') {
return null; // TODO change this once its available in backend
Copy link
Contributor

Choose a reason for hiding this comment

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

Can ya move this to a github issue plz

Copy link
Contributor

Choose a reason for hiding this comment

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

removed comment. Github issue here https://github.com/elastic/endpoint-app-team/issues/87

} else if (columnId === 'malware_score') {
return json[rowIndex].value.source.endgame.data.malware_classification.score;
Copy link
Contributor

Choose a reason for hiding this comment

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

can you confirm that all alerts have this field?

Copy link
Contributor

Choose a reason for hiding this comment

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

We will do this properly in future PRs. We just want to get some basic code structure and patterns merged now.

}
return null;
};
}, [json]);

return (
<EuiDataGrid
aria-label="Alert List"
rowCount={json.length}
// Required. Sets up three columns, the last of which has a custom schema we later define down below.
// The second column B won't allow clicking in to see the content in a popup.
// The first column defines an starting width of 150px and prevents the user from resizing it
columns={columns}
// This allows you to initially hide columns. Users can still turn them on.
columnVisibility={{
visibleColumns,
setVisibleColumns,
}}
// Often used in combination with useEffect() to dynamically change the render.
renderCellValue={renderCellValue}
/>
);
});
Loading