Skip to content

Commit

Permalink
fix middleware types
Browse files Browse the repository at this point in the history
  • Loading branch information
Brent Kimmel committed May 8, 2020
1 parent 2d1b3f2 commit 4ad185d
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ interface AppRequestedResolverData {
*/
interface AppRequestedRelatedEventData {
readonly type: 'appRequestedRelatedEventData';
readonly payload: [ResolverEvent, string];
readonly payload: ResolverEvent;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,15 @@ interface ServerReturnedRelatedEventData {
readonly payload: Map<ResolverEvent, RelatedEventDataEntry>;
}

/**
* Will occur when a request for related event data is unsuccessful.
*/
interface ServerFailedToReturnRelatedEventData {
readonly type: 'serverFailedToReturnRelatedEventData';
}

export type DataAction =
| ServerReturnedResolverData
| ServerFailedToReturnResolverData
| ServerReturnedRelatedEventData;
| ServerReturnedRelatedEventData
| ServerFailedToReturnRelatedEventData;
Original file line number Diff line number Diff line change
Expand Up @@ -32,32 +32,39 @@ export const dataReducer: Reducer<DataState, ResolverAction> = (state = initialS
isLoading: false,
hasError: false,
};
}
else if (action.type === 'appRequestedRelatedEventData') {
} else if (action.type === 'appRequestedRelatedEventData') {
const evt = action.payload;
const statsMap = state[resultsEnrichedWithRelatedEventInfo];
if(statsMap){
if (statsMap) {
const currentStatsMap = new Map(statsMap);
currentStatsMap.set(evt, waitingForRelatedEventData);
return { ...state, [resultsEnrichedWithRelatedEventInfo]: currentStatsMap };
}
return state;
}
else if (action.type === 'serverReturnedRelatedEventData') {
} else if (action.type === 'serverReturnedRelatedEventData') {
/**
* REMOVE: pending resolution of https://github.com/elastic/endpoint-app-team/issues/379
* When this data is inlined with results, there won't be a need for this.
*/
const statsMap = state[resultsEnrichedWithRelatedEventInfo];

if (statsMap && typeof statsMap?.set === 'function') {
const currentStatsMap: RelatedEventData = new Map(statsMap);
for (const updatedEvent of action.payload.keys()) {
const newStatsEntry = action.payload.get(updatedEvent);

if (newStatsEntry) {
// do stats
const statsForEntry = newStatsEntry?.related_events.reduce((a: Partial<Record<RelatedEventType, number>>,v: {related_event_type: RelatedEventType})=>{ a[v.related_event_type] = (a[v.related_event_type]||0)+1; return a; },{})
const statsForEntry = newStatsEntry?.relatedEvents.reduce(
(
a: Partial<Record<RelatedEventType, number>>,
v: { relatedEventType: RelatedEventType }
) => {
a[v.relatedEventType] = (a[v.relatedEventType] || 0) + 1;
return a;
},
{}
);
const newRelatedEventStats: RelatedEventDataEntryWithStats = Object.assign(
newStatsEntry,
{ stats: statsForEntry }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
*/

import { Dispatch, MiddlewareAPI } from 'redux';
import { HttpHandler } from 'kibana/public';
import { KibanaReactContextValue } from '../../../../../../../src/plugins/kibana_react/public';
import { EndpointPluginServices } from '../../../plugin';
import { ResolverState, ResolverAction, RelatedEventDataEntry, RelatedEventType } from '../types';
import { ResolverEvent, ResolverNode } from '../../../../common/types';
import * as event from '../../../../common/models/event';
import { HttpHandler } from 'kibana/public';

type MiddlewareFactory<S = ResolverState> = (
context?: KibanaReactContextValue<EndpointPluginServices>
Expand All @@ -31,15 +31,23 @@ function flattenEvents(children: ResolverNode[], events: ResolverEvent[] = []):
}, events);
}

async function* getEachRelatedEventsResult(eventsToFetch: ResolverEvent[], httpGetter: HttpHandler) {
for (const eventToQueryForRelateds of eventsToFetch){
type RelatedEventAPIResponse = Error | { events: ResolverEvent[] };
async function* getEachRelatedEventsResult(
eventsToFetch: ResolverEvent[],
httpGetter: HttpHandler
): AsyncGenerator<[ResolverEvent, RelatedEventAPIResponse], any, any> {
for (const eventToQueryForRelateds of eventsToFetch) {
const id = event.entityId(eventToQueryForRelateds);
yield [eventToQueryForRelateds, await Promise.all([
httpGetter(`/api/endpoint/resolver/${id}/events`, {
query: {events: 100},
}),
])
]
const relatedEventError = new Error(`Error fetching related events for entity=${id}`);
let result: RelatedEventAPIResponse = relatedEventError;
try {
result = await httpGetter(`/api/endpoint/resolver/${id}/events`, {
query: { events: 100 },
});
} catch (e) {
result = relatedEventError;
}
yield [eventToQueryForRelateds, result];
}
}

Expand Down Expand Up @@ -96,28 +104,35 @@ export const resolverMiddlewareFactory: MiddlewareFactory = context => {
* When this data is inlined with results, there won't be a need for this.
*/
if (action.type === 'appRequestedRelatedEventData') {
if(typeof context !== 'undefined') {
for await (const results of getEachRelatedEventsResult([action.payload], context.services.http.get)){
const response: Map<ResolverEvent, RelatedEventDataEntry> = new Map();
const baseEvent = results[0] as unknown as ResolverEvent;
const fetchedResults = (results[1] as unknown as {events: ResolverEvent[]}[])
//pack up the results into response
for (const relatedEventResult of fetchedResults) {
//help figure out how to type the Async Generator above
const relatedEventsFromResult = relatedEventResult.events;
const relatedEventEntry = relatedEventsFromResult.map(related_event => {
return {
related_event,
related_event_type: event.eventCategoryDisplayName(related_event) as RelatedEventType
}
})
response.set(baseEvent, {related_events: relatedEventEntry});
}
if (typeof context !== 'undefined') {
for await (const results of getEachRelatedEventsResult(
[action.payload],
context.services.http.get
)) {
const apiResults = results[1];
if (apiResults instanceof Error) {
api.dispatch({
type: 'serverReturnedRelatedEventData',
payload: response,
type: 'serverFailedToReturnRelatedEventData',
});
}
const response: Map<ResolverEvent, RelatedEventDataEntry> = new Map();
const baseEvent = results[0];
const fetchedResults = (results[1] as { events: ResolverEvent[] }).events;
// pack up the results into response
const relatedEventEntry = fetchedResults.map(relatedEvent => {
return {
relatedEvent,
relatedEventType: event.eventCategoryDisplayName(relatedEvent) as RelatedEventType,
};
});

response.set(baseEvent, { relatedEvents: relatedEventEntry });

api.dispatch({
type: 'serverReturnedRelatedEventData',
payload: response,
});
}
}
}
};
Expand Down
14 changes: 5 additions & 9 deletions x-pack/plugins/endpoint/public/embeddables/resolver/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,18 +146,14 @@ export type RelatedEventType =
| 'Image Load'
| 'User';

export type EventCategory = RelatedEventType |
( | 'Alert'
| 'Process'
| 'Security'
)
export type EventCategory = RelatedEventType | ('Alert' | 'Process' | 'Security');

/**
* This symbol is used to tag results with Related event info
* REMOVE: pending resolution of https://github.com/elastic/endpoint-app-team/issues/379
* When this data is inlined with results, there won't be a need for this.
*/
export const resultsEnrichedWithRelatedEventInfo = `resultsEnrichedWithRelatedEventInfo`
export const resultsEnrichedWithRelatedEventInfo = `resultsEnrichedWithRelatedEventInfo`;
/**
* This symbol indicates that the app is waiting for related event data for the subject
* of any particular request.
Expand All @@ -170,9 +166,9 @@ export const waitingForRelatedEventData = Symbol(
* about a particular subject's related events
*/
export interface RelatedEventDataEntry {
related_events: Array<{
related_event: ResolverEvent;
related_event_type: RelatedEventType;
relatedEvents: Array<{
relatedEvent: ResolverEvent;
relatedEventType: RelatedEventType;
}>;
}
/**
Expand Down

0 comments on commit 4ad185d

Please sign in to comment.