Skip to content

Commit

Permalink
[Security Solution][Endpoint] Paginate actions log with infinite scro…
Browse files Browse the repository at this point in the history
…ll (#102261) (#103047)

* Show loading below the list when loading

fixes elastic/security-team/issues/1245

* use intersection observer to load data when callout is visible

fixes elastic/security-team/issues/1245

* remove unused `total` from API response

refs 4f7d18b

* toggle ability to paging based on API response and target intersection

fixes elastic/security-team/issues/1245

* use a invisible target

* display a message when end of log

fixes elastic/security-team/issues/1245

* remove search bar

fixes elastic/security-team/issues/1245

* refresh data

fixes elastic/security-team/issues/1245

* rename

refs 85e5add

* add refresh button to empty state

* add translations for copy

* remove refresh button

* load activity log for endpoint on activity log tab selection

fixes elastic/security-team/issues/1312

* reset paging correctly on activity log tab selection

* fix variable mixup

refs /pull/101032/commits/c4e933a9c5954ce249942ca66bab380c1dfa79e2#diff-41a74ad41665921620230a0729728f3bf6e27a6f9dc302fb37b0d2061637c212R81

* fix react warning

refs 697a3c3

* clean up

review changes

* use the complicated flyout version instead of styled version

refs https://elastic.github.io/eui/#/layout/flyout#more-complicated-flyout
refs https://github.com/elastic/kibana/pull/99795/files#r635810660
refs c26a7d4

* Page only when scrolled (so that info message is shown after paging once)

fixes elastic/security-team#1245 (comment)

* add tests

fixes elastic/security-team/issues/1312
fixes elastic/security-team/issues/1245

* increase the parent container's height to ensure that the scroll target is well hidden below the footer

refs 48e3291

* Update x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/store/reducer.ts

Co-authored-by: Paul Tavares <56442535+paul-tavares@users.noreply.github.com>

* Update x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx

Co-authored-by: Paul Tavares <56442535+paul-tavares@users.noreply.github.com>

* address review changes

* cleanup callback and effect

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Paul Tavares <56442535+paul-tavares@users.noreply.github.com>

Co-authored-by: Ashokaditya <am.struktr@gmail.com>
Co-authored-by: Paul Tavares <56442535+paul-tavares@users.noreply.github.com>
  • Loading branch information
3 people authored Jun 23, 2021
1 parent 04b6326 commit b725cf3
Show file tree
Hide file tree
Showing 18 changed files with 524 additions and 164 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ export interface ActivityLogActionResponse {
}
export type ActivityLogEntry = ActivityLogAction | ActivityLogActionResponse;
export interface ActivityLog {
total: number;
page: number;
pageSize: number;
data: ActivityLogEntry[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
import { ServerApiError } from '../../../../common/types';
import { GetPolicyListResponse } from '../../policy/types';
import { GetPackagesResponse } from '../../../../../../fleet/common';
import { EndpointState } from '../types';
import { EndpointIndexUIQueryParams, EndpointState } from '../types';
import { IIndexPattern } from '../../../../../../../../src/plugins/data/public';

export interface ServerReturnedEndpointList {
Expand Down Expand Up @@ -163,12 +163,29 @@ export type EndpointPendingActionsStateChanged = Action<'endpointPendingActionsS
payload: EndpointState['endpointPendingActions'];
};

export interface EndpointDetailsActivityLogUpdatePaging {
type: 'endpointDetailsActivityLogUpdatePaging';
payload: {
// disable paging when no more data after paging
disabled: boolean;
page: number;
pageSize: number;
};
}

export interface EndpointDetailsFlyoutTabChanged {
type: 'endpointDetailsFlyoutTabChanged';
payload: { flyoutView: EndpointIndexUIQueryParams['show'] };
}

export type EndpointAction =
| ServerReturnedEndpointList
| ServerFailedToReturnEndpointList
| ServerReturnedEndpointDetails
| ServerFailedToReturnEndpointDetails
| AppRequestedEndpointActivityLog
| EndpointDetailsActivityLogUpdatePaging
| EndpointDetailsFlyoutTabChanged
| EndpointDetailsActivityLogChanged
| ServerReturnedEndpointPolicyResponse
| ServerFailedToReturnEndpointPolicyResponse
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@ export const initialEndpointPageState = (): Immutable<EndpointState> => {
loading: false,
error: undefined,
endpointDetails: {
flyoutView: undefined,
activityLog: {
page: 1,
pageSize: 50,
paging: {
disabled: false,
page: 1,
pageSize: 50,
},
logData: createUninitialisedResourceState(),
},
hostDetails: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,13 @@ describe('EndpointList store concerns', () => {
loading: false,
error: undefined,
endpointDetails: {
flyoutView: undefined,
activityLog: {
page: 1,
pageSize: 50,
paging: {
disabled: false,
page: 1,
pageSize: 50,
},
logData: { type: 'UninitialisedResourceState' },
},
hostDetails: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import {
} from '../../../../common/lib/endpoint_isolation/mocks';
import { FleetActionGenerator } from '../../../../../common/endpoint/data_generators/fleet_action_generator';
import { endpointPageHttpMock } from '../mocks';
import { EndpointDetailsTabsTypes } from '../view/details/components/endpoint_details_tabs';

jest.mock('../../policy/store/services/ingest', () => ({
sendGetAgentConfigList: () => Promise.resolve({ items: [] }),
Expand Down Expand Up @@ -226,8 +227,16 @@ describe('endpoint list middleware', () => {
const dispatchUserChangedUrl = () => {
dispatchUserChangedUrlToEndpointList({ search: `?${search.split('?').pop()}` });
};
const dispatchFlyoutViewChange = () => {
dispatch({
type: 'endpointDetailsFlyoutTabChanged',
payload: {
flyoutView: EndpointDetailsTabsTypes.activityLog,
},
});
};

const fleetActionGenerator = new FleetActionGenerator(Math.random().toString());
const fleetActionGenerator = new FleetActionGenerator('seed');
const actionData = fleetActionGenerator.generate({
agents: [endpointList.hosts[0].metadata.agent.id],
});
Expand Down Expand Up @@ -265,6 +274,7 @@ describe('endpoint list middleware', () => {

it('should set ActivityLog state to loading', async () => {
dispatchUserChangedUrl();
dispatchFlyoutViewChange();

const loadingDispatched = waitForAction('endpointDetailsActivityLogChanged', {
validate(action) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
getActivityLogDataPaging,
getLastLoadedActivityLogData,
detailsData,
getEndpointDetailsFlyoutView,
} from './selectors';
import { AgentIdsPendingActions, EndpointState, PolicyIds } from '../types';
import {
Expand All @@ -48,6 +49,7 @@ import {
ENDPOINT_ACTION_LOG_ROUTE,
HOST_METADATA_GET_ROUTE,
HOST_METADATA_LIST_ROUTE,
BASE_POLICY_RESPONSE_ROUTE,
metadataCurrentIndexPattern,
} from '../../../../../common/endpoint/constants';
import { IIndexPattern, Query } from '../../../../../../../../src/plugins/data/public';
Expand All @@ -61,6 +63,7 @@ import { AppAction } from '../../../../common/store/actions';
import { resolvePathVariables } from '../../../../common/utils/resolve_path_variables';
import { ServerReturnedEndpointPackageInfo } from './action';
import { fetchPendingActionsByAgentId } from '../../../../common/lib/endpoint_pending_actions';
import { EndpointDetailsTabsTypes } from '../view/details/components/endpoint_details_tabs';

type EndpointPageStore = ImmutableMiddlewareAPI<EndpointState, AppAction>;

Expand Down Expand Up @@ -339,6 +342,28 @@ export const endpointMiddlewareFactory: ImmutableMiddlewareFactory<EndpointState

loadEndpointsPendingActions(store);

// call the policy response api
try {
const policyResponse = await coreStart.http.get(BASE_POLICY_RESPONSE_ROUTE, {
query: { agentId: selectedEndpoint },
});
dispatch({
type: 'serverReturnedEndpointPolicyResponse',
payload: policyResponse,
});
} catch (error) {
dispatch({
type: 'serverFailedToReturnEndpointPolicyResponse',
payload: error,
});
}
}

if (
action.type === 'userChangedUrl' &&
hasSelectedEndpoint(getState()) === true &&
getEndpointDetailsFlyoutView(getState()) === EndpointDetailsTabsTypes.activityLog
) {
// call the activity log api
dispatch({
type: 'endpointDetailsActivityLogChanged',
Expand All @@ -365,22 +390,6 @@ export const endpointMiddlewareFactory: ImmutableMiddlewareFactory<EndpointState
payload: createFailedResourceState<ActivityLog>(error.body ?? error),
});
}

// call the policy response api
try {
const policyResponse = await coreStart.http.get(`/api/endpoint/policy_response`, {
query: { agentId: selectedEndpoint },
});
dispatch({
type: 'serverReturnedEndpointPolicyResponse',
payload: policyResponse,
});
} catch (error) {
dispatch({
type: 'serverFailedToReturnEndpointPolicyResponse',
payload: error,
});
}
}

// page activity log API
Expand Down Expand Up @@ -408,17 +417,24 @@ export const endpointMiddlewareFactory: ImmutableMiddlewareFactory<EndpointState
] as ActivityLog['data'];

const updatedLogData = {
total: activityLog.total,
page: activityLog.page,
pageSize: activityLog.pageSize,
data: updatedLogDataItems,
data: activityLog.page === 1 ? activityLog.data : updatedLogDataItems,
};
dispatch({
type: 'endpointDetailsActivityLogChanged',
payload: createLoadedResourceState<ActivityLog>(updatedLogData),
});
// TODO dispatch 'noNewLogData' if !activityLog.length
// resets paging to previous state
if (!activityLog.data.length) {
dispatch({
type: 'endpointDetailsActivityLogUpdatePaging',
payload: {
disabled: true,
page: activityLog.page - 1,
pageSize: activityLog.pageSize,
},
});
}
} else {
dispatch({
type: 'endpointDetailsActivityLogChanged',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,23 @@ const handleEndpointDetailsActivityLogChanged: CaseReducer<EndpointDetailsActivi
state,
action
) => {
const pagingOptions =
action.payload.type === 'LoadedResourceState'
? {
...state.endpointDetails.activityLog,
paging: {
...state.endpointDetails.activityLog.paging,
page: action.payload.data.page,
pageSize: action.payload.data.pageSize,
},
}
: { ...state.endpointDetails.activityLog };
return {
...state!,
endpointDetails: {
...state.endpointDetails!,
activityLog: {
...state.endpointDetails.activityLog,
...pagingOptions,
logData: action.payload,
},
},
Expand Down Expand Up @@ -138,7 +149,8 @@ export const endpointListReducer: StateReducer = (state = initialEndpointPageSta
},
};
} else if (action.type === 'appRequestedEndpointActivityLog') {
const pageData = {
const paging = {
disabled: state.endpointDetails.activityLog.paging.disabled,
page: action.payload.page,
pageSize: action.payload.pageSize,
};
Expand All @@ -148,10 +160,32 @@ export const endpointListReducer: StateReducer = (state = initialEndpointPageSta
...state.endpointDetails!,
activityLog: {
...state.endpointDetails.activityLog,
...pageData,
paging,
},
},
};
} else if (action.type === 'endpointDetailsActivityLogUpdatePaging') {
const paging = {
...action.payload,
};
return {
...state,
endpointDetails: {
...state.endpointDetails!,
activityLog: {
...state.endpointDetails.activityLog,
paging,
},
},
};
} else if (action.type === 'endpointDetailsFlyoutTabChanged') {
return {
...state,
endpointDetails: {
...state.endpointDetails!,
flyoutView: action.payload.flyoutView,
},
};
} else if (action.type === 'endpointDetailsActivityLogChanged') {
return handleEndpointDetailsActivityLogChanged(state, action);
} else if (action.type === 'endpointPendingActionsStateChanged') {
Expand Down Expand Up @@ -255,8 +289,11 @@ export const endpointListReducer: StateReducer = (state = initialEndpointPageSta

const activityLog = {
logData: createUninitialisedResourceState(),
page: 1,
pageSize: 50,
paging: {
disabled: false,
page: 1,
pageSize: 50,
},
};

// Reset `isolationRequestState` if needed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -364,13 +364,14 @@ export const getIsolationRequestError: (
}
});

export const getEndpointDetailsFlyoutView = (
state: Immutable<EndpointState>
): EndpointIndexUIQueryParams['show'] => state.endpointDetails.flyoutView;

export const getActivityLogDataPaging = (
state: Immutable<EndpointState>
): Immutable<Omit<EndpointState['endpointDetails']['activityLog'], 'logData'>> => {
return {
page: state.endpointDetails.activityLog.page,
pageSize: state.endpointDetails.activityLog.pageSize,
};
): Immutable<EndpointState['endpointDetails']['activityLog']['paging']> => {
return state.endpointDetails.activityLog.paging;
};

export const getActivityLogData = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,13 @@ export interface EndpointState {
/** api error from retrieving host list */
error?: ServerApiError;
endpointDetails: {
flyoutView: EndpointIndexUIQueryParams['show'];
activityLog: {
page: number;
pageSize: number;
paging: {
disabled: boolean;
page: number;
pageSize: number;
};
logData: AsyncResourceState<ActivityLog>;
};
hostDetails: {
Expand Down
Loading

0 comments on commit b725cf3

Please sign in to comment.