Skip to content

Commit b646db8

Browse files
authored
[7.x] [Security Solution][Resolver] Request data from new event api (#78782) (#78862)
1 parent c114687 commit b646db8

File tree

9 files changed

+208
-28
lines changed

9 files changed

+208
-28
lines changed

x-pack/plugins/security_solution/public/resolver/store/data/action.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
ResolverRelatedEvents,
99
ResolverTree,
1010
SafeEndpointEvent,
11+
SafeResolverEvent,
1112
} from '../../../../common/endpoint/types';
1213
import { TreeFetcherParameters } from '../../types';
1314

@@ -78,10 +79,25 @@ interface ServerReturnedNodeEventsInCategory {
7879
eventCategory: string;
7980
};
8081
}
82+
interface AppRequestedCurrentRelatedEventData {
83+
type: 'appRequestedCurrentRelatedEventData';
84+
}
85+
86+
interface ServerFailedToReturnCurrentRelatedEventData {
87+
type: 'serverFailedToReturnCurrentRelatedEventData';
88+
}
89+
90+
interface ServerReturnedCurrentRelatedEventData {
91+
readonly type: 'serverReturnedCurrentRelatedEventData';
92+
readonly payload: SafeResolverEvent;
93+
}
8194

8295
export type DataAction =
8396
| ServerReturnedResolverData
8497
| ServerFailedToReturnResolverData
98+
| AppRequestedCurrentRelatedEventData
99+
| ServerReturnedCurrentRelatedEventData
100+
| ServerFailedToReturnCurrentRelatedEventData
85101
| ServerReturnedRelatedEventData
86102
| ServerReturnedNodeEventsInCategory
87103
| AppRequestedResolverData

x-pack/plugins/security_solution/public/resolver/store/data/reducer.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ import * as selectors from './selectors';
1212
import * as nodeEventsInCategoryModel from './node_events_in_category_model';
1313

1414
const initialState: DataState = {
15+
currentRelatedEvent: {
16+
loading: false,
17+
data: null,
18+
},
1519
relatedEvents: new Map(),
1620
resolverComponentInstanceID: undefined,
1721
};
@@ -153,6 +157,33 @@ export const dataReducer: Reducer<DataState, ResolverAction> = (state = initialS
153157
// the action is stale, ignore it
154158
return state;
155159
}
160+
} else if (action.type === 'appRequestedCurrentRelatedEventData') {
161+
const nextState: DataState = {
162+
...state,
163+
currentRelatedEvent: {
164+
loading: true,
165+
data: null,
166+
},
167+
};
168+
return nextState;
169+
} else if (action.type === 'serverReturnedCurrentRelatedEventData') {
170+
const nextState: DataState = {
171+
...state,
172+
currentRelatedEvent: {
173+
loading: false,
174+
data: action.payload,
175+
},
176+
};
177+
return nextState;
178+
} else if (action.type === 'serverFailedToReturnCurrentRelatedEventData') {
179+
const nextState: DataState = {
180+
...state,
181+
currentRelatedEvent: {
182+
loading: false,
183+
data: null,
184+
},
185+
};
186+
return nextState;
156187
} else {
157188
return state;
158189
}

x-pack/plugins/security_solution/public/resolver/store/data/selectors.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,27 @@ export function relatedEventsByEntityId(data: DataState): Map<string, ResolverRe
183183
return data.relatedEvents;
184184
}
185185

186+
/**
187+
*
188+
*
189+
* @export
190+
* @param {DataState} state
191+
* @returns the loading state of the current related event data for the `event_detail` view
192+
*/
193+
export function isCurrentRelatedEventLoading(state: DataState) {
194+
return state.currentRelatedEvent.loading;
195+
}
196+
197+
/**
198+
*
199+
*
200+
* @export
201+
* @param {DataState} state
202+
* @returns {(SafeResolverEvent | null)} the current related event data for the `event_detail` view
203+
*/
204+
export function currentRelatedEventData(state: DataState): SafeResolverEvent | null {
205+
return state.currentRelatedEvent.data;
206+
}
186207
/**
187208
* Get an event (from memory) by its `event.id`.
188209
* @deprecated Use the API to find events by ID
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import { Dispatch, MiddlewareAPI } from 'redux';
8+
import { isEqual } from 'lodash';
9+
import { SafeResolverEvent } from '../../../../common/endpoint/types';
10+
11+
import { ResolverState, DataAccessLayer, PanelViewAndParameters } from '../../types';
12+
import * as selectors from '../selectors';
13+
import { ResolverAction } from '../actions';
14+
15+
/**
16+
*
17+
* @description - This api is called after every state change.
18+
* If the current view is the `eventDetail` view it will request the event details from the server.
19+
* @export
20+
* @param {DataAccessLayer} dataAccessLayer
21+
* @param {MiddlewareAPI<Dispatch<ResolverAction>, ResolverState>} api
22+
* @returns {() => void}
23+
*/
24+
export function CurrentRelatedEventFetcher(
25+
dataAccessLayer: DataAccessLayer,
26+
api: MiddlewareAPI<Dispatch<ResolverAction>, ResolverState>
27+
): () => void {
28+
let last: PanelViewAndParameters | undefined;
29+
30+
return async () => {
31+
const state = api.getState();
32+
33+
const newParams = selectors.panelViewAndParameters(state);
34+
35+
const oldParams = last;
36+
last = newParams;
37+
38+
// If the panel view params have changed and the current panel view is the `eventDetail`, then fetch the event details for that eventID.
39+
if (!isEqual(newParams, oldParams) && newParams.panelView === 'eventDetail') {
40+
const currentEventID = newParams.panelParameters.eventID;
41+
42+
api.dispatch({
43+
type: 'appRequestedCurrentRelatedEventData',
44+
});
45+
46+
let result: SafeResolverEvent | undefined | null;
47+
try {
48+
result = await dataAccessLayer.event(currentEventID);
49+
} catch (error) {
50+
api.dispatch({
51+
type: 'serverFailedToReturnCurrentRelatedEventData',
52+
});
53+
}
54+
55+
if (result) {
56+
api.dispatch({
57+
type: 'serverReturnedCurrentRelatedEventData',
58+
payload: result,
59+
});
60+
} else {
61+
api.dispatch({
62+
type: 'serverFailedToReturnCurrentRelatedEventData',
63+
});
64+
}
65+
}
66+
};
67+
}

x-pack/plugins/security_solution/public/resolver/store/middleware/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { ResolverTreeFetcher } from './resolver_tree_fetcher';
1010

1111
import { ResolverAction } from '../actions';
1212
import { RelatedEventsFetcher } from './related_events_fetcher';
13+
import { CurrentRelatedEventFetcher } from './current_related_event_fetcher';
1314

1415
type MiddlewareFactory<S = ResolverState> = (
1516
dataAccessLayer: DataAccessLayer
@@ -27,11 +28,13 @@ export const resolverMiddlewareFactory: MiddlewareFactory = (dataAccessLayer: Da
2728
return (api) => (next) => {
2829
const resolverTreeFetcher = ResolverTreeFetcher(dataAccessLayer, api);
2930
const relatedEventsFetcher = RelatedEventsFetcher(dataAccessLayer, api);
31+
const currentRelatedEventFetcher = CurrentRelatedEventFetcher(dataAccessLayer, api);
3032
return async (action: ResolverAction) => {
3133
next(action);
3234

3335
resolverTreeFetcher();
3436
relatedEventsFetcher();
37+
currentRelatedEventFetcher();
3538
};
3639
};
3740
};

x-pack/plugins/security_solution/public/resolver/store/selectors.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,22 @@ export const relatedEventCountByCategory: (
138138
dataSelectors.relatedEventCountByCategory
139139
);
140140

141+
/**
142+
* the loading state of the current related event data for the `event_detail` view
143+
*/
144+
export const isCurrentRelatedEventLoading = composeSelectors(
145+
dataStateSelector,
146+
dataSelectors.isCurrentRelatedEventLoading
147+
);
148+
149+
/**
150+
* the current related event data for the `event_detail` view
151+
*/
152+
export const currentRelatedEventData = composeSelectors(
153+
dataStateSelector,
154+
dataSelectors.currentRelatedEventData
155+
);
156+
141157
/**
142158
* Map of related events... by entity id
143159
* @deprecated

x-pack/plugins/security_solution/public/resolver/types.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,15 @@ export interface DataState {
245245
*/
246246
readonly nodeEventsInCategory?: NodeEventsInCategoryState;
247247

248+
/**
249+
* Used when the panelView is `eventDetail`.
250+
*
251+
*/
252+
readonly currentRelatedEvent: {
253+
loading: boolean;
254+
data: SafeResolverEvent | null;
255+
};
256+
248257
readonly tree?: {
249258
/**
250259
* The parameters passed from the resolver properties

x-pack/plugins/security_solution/public/resolver/view/panels/event_detail.tsx

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,21 @@ import { Breadcrumbs } from './breadcrumbs';
2525
import * as eventModel from '../../../../common/endpoint/models/event';
2626
import * as selectors from '../../store/selectors';
2727
import { PanelLoading } from './panel_loading';
28+
import { PanelContentError } from './panel_content_error';
2829
import { ResolverState } from '../../types';
2930
import { DescriptiveName } from './descriptive_name';
3031
import { useLinkProps } from '../use_link_props';
3132
import { SafeResolverEvent } from '../../../../common/endpoint/types';
3233
import { deepObjectEntries } from './deep_object_entries';
3334
import { useFormattedDate } from './use_formatted_date';
3435

36+
const eventDetailRequestError = i18n.translate(
37+
'xpack.securitySolution.resolver.panel.eventDetail.requestError',
38+
{
39+
defaultMessage: 'Event details were unable to be retrieved',
40+
}
41+
);
42+
3543
export const EventDetail = memo(function EventDetail({
3644
nodeID,
3745
eventID,
@@ -42,28 +50,31 @@ export const EventDetail = memo(function EventDetail({
4250
/** The event type to show in the breadcrumbs */
4351
eventCategory: string;
4452
}) {
45-
const event = useSelector((state: ResolverState) =>
46-
selectors.eventByID(state)({ nodeID, eventID })
47-
);
53+
const isEventLoading = useSelector(selectors.isCurrentRelatedEventLoading);
54+
const isProcessTreeLoading = useSelector(selectors.isTreeLoading);
55+
56+
const isLoading = isEventLoading || isProcessTreeLoading;
57+
58+
const event = useSelector(selectors.currentRelatedEventData);
4859
const processEvent = useSelector((state: ResolverState) =>
4960
selectors.processEventForID(state)(nodeID)
5061
);
51-
if (event && processEvent) {
52-
return (
53-
<EventDetailContents
54-
nodeID={nodeID}
55-
event={event}
56-
processEvent={processEvent}
57-
eventType={eventType}
58-
/>
59-
);
60-
} else {
61-
return (
62-
<StyledPanel>
63-
<PanelLoading />
64-
</StyledPanel>
65-
);
66-
}
62+
return isLoading ? (
63+
<StyledPanel>
64+
<PanelLoading />
65+
</StyledPanel>
66+
) : event ? (
67+
<EventDetailContents
68+
nodeID={nodeID}
69+
event={event}
70+
processEvent={processEvent}
71+
eventType={eventType}
72+
/>
73+
) : (
74+
<StyledPanel>
75+
<PanelContentError translatedErrorMessage={eventDetailRequestError} />
76+
</StyledPanel>
77+
);
6778
});
6879

6980
/**
@@ -82,16 +93,17 @@ const EventDetailContents = memo(function ({
8293
* Event type to use in the breadcrumbs
8394
*/
8495
eventType: string;
85-
processEvent: SafeResolverEvent;
96+
processEvent: SafeResolverEvent | null;
8697
}) {
8798
const timestamp = eventModel.timestampSafeVersion(event);
8899
const formattedDate = useFormattedDate(timestamp) || noTimestampRetrievedText;
100+
const nodeName = processEvent ? eventModel.processNameSafeVersion(processEvent) : null;
89101

90102
return (
91103
<StyledPanel>
92104
<EventDetailBreadcrumbs
93105
nodeID={nodeID}
94-
nodeName={eventModel.processNameSafeVersion(processEvent)}
106+
nodeName={nodeName}
95107
event={event}
96108
breadcrumbEventCategory={eventType}
97109
/>
@@ -188,7 +200,7 @@ function EventDetailBreadcrumbs({
188200
breadcrumbEventCategory,
189201
}: {
190202
nodeID: string;
191-
nodeName?: string;
203+
nodeName: string | null | undefined;
192204
event: SafeResolverEvent;
193205
breadcrumbEventCategory: string;
194206
}) {
@@ -217,7 +229,7 @@ function EventDetailBreadcrumbs({
217229
panelParameters: { nodeID, eventCategory: breadcrumbEventCategory },
218230
});
219231
const breadcrumbs = useMemo(() => {
220-
return [
232+
const crumbs = [
221233
{
222234
text: i18n.translate(
223235
'xpack.securitySolution.endpoint.resolver.panel.relatedEventDetail.events',
@@ -227,10 +239,6 @@ function EventDetailBreadcrumbs({
227239
),
228240
...nodesLinkNavProps,
229241
},
230-
{
231-
text: nodeName,
232-
...nodeDetailLinkNavProps,
233-
},
234242
{
235243
text: (
236244
<FormattedMessage
@@ -255,6 +263,15 @@ function EventDetailBreadcrumbs({
255263
text: <DescriptiveName event={event} />,
256264
},
257265
];
266+
267+
if (nodeName) {
268+
crumbs.splice(1, 0, {
269+
text: nodeName,
270+
...nodeDetailLinkNavProps,
271+
});
272+
}
273+
274+
return crumbs;
258275
}, [
259276
breadcrumbEventCategory,
260277
countByCategory,

x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_error.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export const PanelContentError = memo(function ({
4848
<EuiSpacer size="l" />
4949
<EuiButtonEmpty {...nodesLinkNavProps}>
5050
{i18n.translate('xpack.securitySolution.endpoint.resolver.panel.error.goBack', {
51-
defaultMessage: 'Click this link to return to the list of all processes.',
51+
defaultMessage: 'Click here to return to the list of all processes.',
5252
})}
5353
</EuiButtonEmpty>
5454
</>

0 commit comments

Comments
 (0)