Skip to content

Commit

Permalink
[Security Solution] [Endpoint] Allow filtering activity log with date…
Browse files Browse the repository at this point in the history
… range (elastic#104085)

* use date range in search query

fixes elastic/security-team/issues/1137

* make any date selection fetch matching log

fixes elastic/security-team/issues/1137

* use a single action for updating paging info and fetching data

fixes elastic/security-team/issues/1137

* use consistent types

for some reason TS was complaining earlier with `undefined`

* reset date picker on tab load

fixes elastic/security-team/issues/1137

* refactor date pickers into a component

refs elastic/security-team/issues/1137

* clear dates on change of endpoint

fixes elastic/security-team/issues/1137

* do not show empty state if date filtering results return empty data

fixes elastic/security-team/issues/1137

* add tests

fixes elastic/security-team/issues/1137

* review changes

* update comment

refs f551b67

* store invalidDateRange on redux store and decouple logic from the component

review changes

* fix test

* fix lint

* review changes

* expand date picker to use the full width of the flyout

review changes

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
ashokaditya and kibanamachine committed Jul 9, 2021
1 parent 90a1fb4 commit 30fb7a2
Show file tree
Hide file tree
Showing 18 changed files with 416 additions and 67 deletions.
Expand Up @@ -23,6 +23,8 @@ export const EndpointActionLogRequestSchema = {
query: schema.object({
page: schema.number({ defaultValue: 1, min: 1 }),
page_size: schema.number({ defaultValue: 10, min: 1, max: 100 }),
start_date: schema.maybe(schema.string()),
end_date: schema.maybe(schema.string()),
}),
params: schema.object({
agent_id: schema.string(),
Expand Down
Expand Up @@ -60,6 +60,8 @@ export type ActivityLogEntry = ActivityLogAction | ActivityLogActionResponse;
export interface ActivityLog {
page: number;
pageSize: number;
startDate?: string;
endDate?: string;
data: ActivityLogEntry[];
}

Expand Down
Expand Up @@ -146,13 +146,6 @@ export type EndpointIsolationRequestStateChange = Action<'endpointIsolationReque
payload: EndpointState['isolationRequestState'];
};

export interface AppRequestedEndpointActivityLog {
type: 'appRequestedEndpointActivityLog';
payload: {
page: number;
pageSize: number;
};
}
export type EndpointDetailsActivityLogChanged = Action<'endpointDetailsActivityLogChanged'> & {
payload: EndpointState['endpointDetails']['activityLog']['logData'];
};
Expand All @@ -165,9 +158,18 @@ export interface EndpointDetailsActivityLogUpdatePaging {
type: 'endpointDetailsActivityLogUpdatePaging';
payload: {
// disable paging when no more data after paging
disabled: boolean;
disabled?: boolean;
page: number;
pageSize: number;
startDate?: string;
endDate?: string;
};
}

export interface EndpointDetailsActivityLogUpdateIsInvalidDateRange {
type: 'endpointDetailsActivityLogUpdateIsInvalidDateRange';
payload: {
isInvalidDateRange?: boolean;
};
}

Expand All @@ -181,8 +183,8 @@ export type EndpointAction =
| ServerFailedToReturnEndpointList
| ServerReturnedEndpointDetails
| ServerFailedToReturnEndpointDetails
| AppRequestedEndpointActivityLog
| EndpointDetailsActivityLogUpdatePaging
| EndpointDetailsActivityLogUpdateIsInvalidDateRange
| EndpointDetailsFlyoutTabChanged
| EndpointDetailsActivityLogChanged
| ServerReturnedEndpointPolicyResponse
Expand Down
Expand Up @@ -25,6 +25,9 @@ export const initialEndpointPageState = (): Immutable<EndpointState> => {
disabled: false,
page: 1,
pageSize: 50,
startDate: undefined,
endDate: undefined,
isInvalidDateRange: false,
},
logData: createUninitialisedResourceState(),
},
Expand Down
Expand Up @@ -48,6 +48,7 @@ describe('EndpointList store concerns', () => {
disabled: false,
page: 1,
pageSize: 50,
isInvalidDateRange: false,
},
logData: { type: 'UninitialisedResourceState' },
},
Expand Down
Expand Up @@ -65,6 +65,7 @@ import { resolvePathVariables } from '../../../../common/utils/resolve_path_vari
import { EndpointPackageInfoStateChanged } from './action';
import { fetchPendingActionsByAgentId } from '../../../../common/lib/endpoint_pending_actions';
import { EndpointDetailsTabsTypes } from '../view/details/components/endpoint_details_tabs';
import { getIsInvalidDateRange } from '../utils';

type EndpointPageStore = ImmutableMiddlewareAPI<EndpointState, AppAction>;

Expand Down Expand Up @@ -400,21 +401,50 @@ export const endpointMiddlewareFactory: ImmutableMiddlewareFactory<EndpointState
}

// page activity log API
if (action.type === 'appRequestedEndpointActivityLog' && hasSelectedEndpoint(getState())) {
dispatch({
type: 'endpointDetailsActivityLogChanged',
// ts error to be fixed when AsyncResourceState is refactored (#830)
// @ts-expect-error
payload: createLoadingResourceState<ActivityLog>(getActivityLogData(getState())),
});

if (
action.type === 'endpointDetailsActivityLogUpdatePaging' &&
hasSelectedEndpoint(getState())
) {
try {
const { page, pageSize } = getActivityLogDataPaging(getState());
const { disabled, page, pageSize, startDate, endDate } = getActivityLogDataPaging(
getState()
);
// don't page when paging is disabled or when date ranges are invalid
if (disabled) {
return;
}
if (getIsInvalidDateRange({ startDate, endDate })) {
dispatch({
type: 'endpointDetailsActivityLogUpdateIsInvalidDateRange',
payload: {
isInvalidDateRange: true,
},
});
return;
}

dispatch({
type: 'endpointDetailsActivityLogUpdateIsInvalidDateRange',
payload: {
isInvalidDateRange: false,
},
});
dispatch({
type: 'endpointDetailsActivityLogChanged',
// ts error to be fixed when AsyncResourceState is refactored (#830)
// @ts-expect-error
payload: createLoadingResourceState<ActivityLog>(getActivityLogData(getState())),
});
const route = resolvePathVariables(ENDPOINT_ACTION_LOG_ROUTE, {
agent_id: selectedAgent(getState()),
});
const activityLog = await coreStart.http.get<ActivityLog>(route, {
query: { page, page_size: pageSize },
query: {
page,
page_size: pageSize,
start_date: startDate,
end_date: endDate,
},
});

const lastLoadedLogData = getLastLoadedActivityLogData(getState());
Expand All @@ -428,6 +458,8 @@ export const endpointMiddlewareFactory: ImmutableMiddlewareFactory<EndpointState
const updatedLogData = {
page: activityLog.page,
pageSize: activityLog.pageSize,
startDate: activityLog.startDate,
endDate: activityLog.endDate,
data: activityLog.page === 1 ? activityLog.data : updatedLogDataItems,
};
dispatch({
Expand All @@ -439,8 +471,10 @@ export const endpointMiddlewareFactory: ImmutableMiddlewareFactory<EndpointState
type: 'endpointDetailsActivityLogUpdatePaging',
payload: {
disabled: true,
page: activityLog.page - 1,
page: activityLog.page > 1 ? activityLog.page - 1 : 1,
pageSize: activityLog.pageSize,
startDate: activityLog.startDate,
endDate: activityLog.endDate,
},
});
}
Expand Down
Expand Up @@ -41,6 +41,8 @@ const handleEndpointDetailsActivityLogChanged: CaseReducer<EndpointDetailsActivi
...state.endpointDetails.activityLog.paging,
page: action.payload.data.page,
pageSize: action.payload.data.pageSize,
startDate: action.payload.data.startDate,
endDate: action.payload.data.endDate,
},
}
: { ...state.endpointDetails.activityLog };
Expand Down Expand Up @@ -162,33 +164,31 @@ export const endpointListReducer: StateReducer = (state = initialEndpointPageSta
},
},
};
} else if (action.type === 'appRequestedEndpointActivityLog') {
const paging = {
disabled: state.endpointDetails.activityLog.paging.disabled,
page: action.payload.page,
pageSize: action.payload.pageSize,
};
} else if (action.type === 'endpointDetailsActivityLogUpdatePaging') {
return {
...state,
endpointDetails: {
...state.endpointDetails!,
activityLog: {
...state.endpointDetails.activityLog,
paging,
paging: {
...state.endpointDetails.activityLog.paging,
...action.payload,
},
},
},
};
} else if (action.type === 'endpointDetailsActivityLogUpdatePaging') {
const paging = {
...action.payload,
};
} else if (action.type === 'endpointDetailsActivityLogUpdateIsInvalidDateRange') {
return {
...state,
endpointDetails: {
...state.endpointDetails!,
activityLog: {
...state.endpointDetails.activityLog,
paging,
paging: {
...state.endpointDetails.activityLog.paging,
...action.payload,
},
},
},
};
Expand Down Expand Up @@ -304,6 +304,7 @@ export const endpointListReducer: StateReducer = (state = initialEndpointPageSta
disabled: false,
page: 1,
pageSize: 50,
isInvalidDateRange: false,
},
};

Expand Down
Expand Up @@ -40,9 +40,12 @@ export interface EndpointState {
flyoutView: EndpointIndexUIQueryParams['show'];
activityLog: {
paging: {
disabled: boolean;
disabled?: boolean;
page: number;
pageSize: number;
startDate?: string;
endDate?: string;
isInvalidDateRange: boolean;
};
logData: AsyncResourceState<ActivityLog>;
};
Expand Down
@@ -0,0 +1,30 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import moment from 'moment';
import { getIsInvalidDateRange } from './utils';

describe('utils', () => {
describe('getIsInvalidDateRange', () => {
it('should return FALSE when either dates are undefined', () => {
expect(getIsInvalidDateRange({})).toBe(false);
expect(getIsInvalidDateRange({ startDate: moment().subtract(1, 'd').toISOString() })).toBe(
false
);
expect(getIsInvalidDateRange({ endDate: moment().toISOString() })).toBe(false);
});

it('should return TRUE when startDate is after endDate', () => {
expect(
getIsInvalidDateRange({
startDate: moment().toISOString(),
endDate: moment().subtract(1, 'd').toISOString(),
})
).toBe(true);
});
});
});
Expand Up @@ -5,6 +5,7 @@
* 2.0.
*/

import moment from 'moment';
import { HostInfo, HostMetadata } from '../../../../common/endpoint/types';

export const isPolicyOutOfDate = (
Expand All @@ -23,3 +24,18 @@ export const isPolicyOutOfDate = (
reported.endpoint_policy_version >= current.endpoint.revision
);
};

export const getIsInvalidDateRange = ({
startDate,
endDate,
}: {
startDate?: string;
endDate?: string;
}) => {
if (startDate && endDate) {
const start = moment(startDate);
const end = moment(endDate);
return start.isAfter(end);
}
return false;
};

0 comments on commit 30fb7a2

Please sign in to comment.