From 6ca3826db9ba189042ea6c05b66689e77dada0f2 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Thu, 13 Feb 2020 13:08:37 +1300 Subject: [PATCH 1/3] added pagination on alert instances page --- .../components/alert_instances.tsx | 40 +++++--- .../apps/triggers_actions_ui/details.ts | 91 ++++++++++++++++++- .../page_objects/alert_details.ts | 4 + 3 files changed, 123 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx index 1f0e4f015f229d..d53d8fd87d5211 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx @@ -4,16 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment } from 'react'; +import React, { Fragment, useState } from 'react'; import moment, { Duration } from 'moment'; import { i18n } from '@kbn/i18n'; import { EuiBasicTable, EuiButtonToggle, EuiBadge, EuiHealth } from '@elastic/eui'; // @ts-ignore import { RIGHT_ALIGNMENT, CENTER_ALIGNMENT } from '@elastic/eui/lib/services'; -import { padLeft, difference } from 'lodash'; +import { padLeft, difference, chunk } from 'lodash'; import { FormattedMessage } from '@kbn/i18n/react'; import { RawAlertInstance } from '../../../../../../../legacy/plugins/alerting/common'; -import { Alert, AlertTaskState } from '../../../../types'; +import { Alert, AlertTaskState, Pagination } from '../../../../types'; import { ComponentOpts as AlertApis, withBulkAlertOperations, @@ -135,22 +135,36 @@ export function AlertInstances({ unmuteAlertInstance, requestRefresh, }: AlertInstancesProps) { + const [pagination, setPagination] = useState({ index: 0, size: 10 }); + + const mergedAlertInstances = [ + ...Object.entries(alertInstances).map(([instanceId, instance]) => + alertInstanceToListItem(alert, instanceId, instance) + ), + ...difference(alert.mutedInstanceIds, Object.keys(alertInstances)).map(instanceId => + alertInstanceToListItem(alert, instanceId) + ), + ]; + const pageOfAlertInstances = getPage(mergedAlertInstances, pagination); + const onMuteAction = async (instance: AlertInstanceListItem) => { await (instance.isMuted ? unmuteAlertInstance(alert, instance.instance) : muteAlertInstance(alert, instance.instance)); requestRefresh(); }; + return ( - alertInstanceToListItem(alert, instanceId, instance) - ), - ...difference(alert.mutedInstanceIds, Object.keys(alertInstances)).map(instanceId => - alertInstanceToListItem(alert, instanceId) - ), - ]} + items={pageOfAlertInstances} + pagination={{ + pageIndex: pagination.index, + pageSize: pagination.size, + totalItemCount: mergedAlertInstances.length, + }} + onChange={({ page: changedPage }: { page: Pagination }) => { + setPagination(changedPage); + }} rowProps={() => ({ 'data-test-subj': 'alert-instance-row', })} @@ -164,6 +178,10 @@ export function AlertInstances({ } export const AlertInstancesWithApi = withBulkAlertOperations(AlertInstances); +function getPage(items: any[], pagination: Pagination) { + return chunk(items, pagination.size)[pagination.index]; +} + interface AlertInstanceListItemStatus { label: string; healthColor: string; diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts index ce9160abdb086d..a282af5c95ad0e 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts @@ -6,7 +6,7 @@ import expect from '@kbn/expect'; import uuid from 'uuid'; -import { omit } from 'lodash'; +import { omit, range, flatten } from 'lodash'; import moment from 'moment'; import { FtrProviderContext } from '../../ftr_provider_context'; @@ -330,5 +330,94 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.alertDetailsUI.ensureAlertInstanceExistance('eu-east', false); }); }); + + describe('Alert Instance Pagination', function() { + const testRunUuid = uuid.v4(); + let alert: any; + + before(async () => { + await pageObjects.common.navigateToApp('triggersActions'); + + const actions = await Promise.all([ + alerting.actions.createAction({ + name: `server-log-${testRunUuid}-${0}`, + actionTypeId: '.server-log', + config: {}, + secrets: {}, + }), + alerting.actions.createAction({ + name: `server-log-${testRunUuid}-${1}`, + actionTypeId: '.server-log', + config: {}, + secrets: {}, + }), + ]); + + const instances = flatten( + range(10).map(index => [ + { id: `us-central-${index}` }, + { id: `us-east-${index}` }, + { id: `us-west-${index}` }, + ]) + ); + alert = await alerting.alerts.createAlwaysFiringWithActions( + `test-alert-${testRunUuid}`, + actions.map(action => ({ + id: action.id, + group: 'default', + params: { + message: 'from alert 1s', + level: 'warn', + }, + })), + { + instances, + } + ); + + // refresh to see alert + await browser.refresh(); + + await pageObjects.header.waitUntilLoadingHasFinished(); + + // Verify content + await testSubjects.existOrFail('alertsList'); + + // click on first alert + await pageObjects.triggersActionsUI.clickOnAlertInAlertsList(alert.name); + + // await first run to complete so we have an initial state + await retry.try(async () => { + const { alertInstances } = await alerting.alerts.getAlertState(alert.id); + expect(Object.keys(alertInstances).length).to.eql(instances.length); + }); + }); + + it('renders the first page', async () => { + // Verify content + await testSubjects.existOrFail('alertInstancesList'); + + const { alertInstances } = await alerting.alerts.getAlertState(alert.id); + + const [firstItem] = await pageObjects.alertDetailsUI.getAlertInstancesList(); + expect(firstItem.instance).to.eql(Object.keys(alertInstances)[0]); + }); + + it('navigates to the next page', async () => { + // Verify content + await testSubjects.existOrFail('alertInstancesList'); + + const { alertInstances } = await alerting.alerts.getAlertState(alert.id); + + await pageObjects.alertDetailsUI.clickPaginationNextPage(); + + const PAGE_SIZE = 10; + + await retry.try(async () => { + const [firstItem] = await pageObjects.alertDetailsUI.getAlertInstancesList(); + expect(firstItem.instance).to.eql(Object.keys(alertInstances)[PAGE_SIZE]); + }); + }); + }); }); }; diff --git a/x-pack/test/functional_with_es_ssl/page_objects/alert_details.ts b/x-pack/test/functional_with_es_ssl/page_objects/alert_details.ts index fd936b37386770..900fe3237ffac0 100644 --- a/x-pack/test/functional_with_es_ssl/page_objects/alert_details.ts +++ b/x-pack/test/functional_with_es_ssl/page_objects/alert_details.ts @@ -92,5 +92,9 @@ export function AlertDetailsPageProvider({ getService }: FtrProviderContext) { ).to.eql(shouldExist ? 1 : 0); }); }, + async clickPaginationNextPage() { + const nextButton = await testSubjects.find(`pagination-button-next`); + nextButton.click(); + }, }; } From 7930a7c7c2ac634649e46ab3a41a8ea3deb23fca Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Fri, 14 Feb 2020 11:38:27 +1300 Subject: [PATCH 2/3] extracted default page size to a constant for alerting UI as a whole --- .../public/application/constants/index.ts | 2 ++ .../sections/alert_details/components/alert_instances.tsx | 6 +++++- .../sections/alerts_list/components/alerts_list.tsx | 4 ++-- .../apps/triggers_actions_ui/details.ts | 8 +++++--- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts b/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts index 11b094dea0e624..d469651b48b04a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/constants/index.ts @@ -20,3 +20,5 @@ export enum SORT_ORDERS { ASCENDING = 'asc', DESCENDING = 'desc', } + +export const DEFAULT_SEARCH_PAGE_SIZE: number = 10; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx index d53d8fd87d5211..926a3de323b08c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx @@ -18,6 +18,7 @@ import { ComponentOpts as AlertApis, withBulkAlertOperations, } from '../../common/components/with_bulk_alert_api_operations'; +import { DEFAULT_SEARCH_PAGE_SIZE } from '../../../constants'; type AlertInstancesProps = { alert: Alert; @@ -135,7 +136,10 @@ export function AlertInstances({ unmuteAlertInstance, requestRefresh, }: AlertInstancesProps) { - const [pagination, setPagination] = useState({ index: 0, size: 10 }); + const [pagination, setPagination] = useState({ + index: 0, + size: DEFAULT_SEARCH_PAGE_SIZE, + }); const mergedAlertInstances = [ ...Object.entries(alertInstances).map(([instanceId, instance]) => diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx index a89215e6c29648..d9ccb84452e47e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx @@ -32,7 +32,7 @@ import { ActionTypeFilter } from './action_type_filter'; import { loadAlerts, loadAlertTypes } from '../../../lib/alert_api'; import { loadActionTypes } from '../../../lib/action_connector_api'; import { hasDeleteAlertsCapability, hasSaveAlertsCapability } from '../../../lib/capabilities'; -import { routeToAlertDetails } from '../../../constants'; +import { routeToAlertDetails, DEFAULT_SEARCH_PAGE_SIZE } from '../../../constants'; const ENTER_KEY = 13; @@ -67,7 +67,7 @@ export const AlertsList: React.FunctionComponent = () => { const [actionTypes, setActionTypes] = useState([]); const [selectedIds, setSelectedIds] = useState([]); const [isPerformingAction, setIsPerformingAction] = useState(false); - const [page, setPage] = useState({ index: 0, size: 10 }); + const [page, setPage] = useState({ index: 0, size: DEFAULT_SEARCH_PAGE_SIZE }); const [searchText, setSearchText] = useState(); const [inputText, setInputText] = useState(); const [typesFilter, setTypesFilter] = useState([]); diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts index 357a82b0d2ec1d..3db4731f0adfb6 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts @@ -394,13 +394,17 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); }); + const PAGE_SIZE = 10; it('renders the first page', async () => { // Verify content await testSubjects.existOrFail('alertInstancesList'); const { alertInstances } = await alerting.alerts.getAlertState(alert.id); - const [firstItem] = await pageObjects.alertDetailsUI.getAlertInstancesList(); + const items = await pageObjects.alertDetailsUI.getAlertInstancesList(); + expect(items.length).to.eql(PAGE_SIZE); + + const [firstItem] = items; expect(firstItem.instance).to.eql(Object.keys(alertInstances)[0]); }); @@ -412,8 +416,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.alertDetailsUI.clickPaginationNextPage(); - const PAGE_SIZE = 10; - await retry.try(async () => { const [firstItem] = await pageObjects.alertDetailsUI.getAlertInstancesList(); expect(firstItem.instance).to.eql(Object.keys(alertInstances)[PAGE_SIZE]); From bfce131d00bd2523fb444b6e28f32c91a56bc957 Mon Sep 17 00:00:00 2001 From: Mike Cote Date: Tue, 18 Feb 2020 10:26:45 -0500 Subject: [PATCH 3/3] Fix test failure --- .../sections/alert_details/components/alert_instances.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx index 926a3de323b08c..cae55443c25e85 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.tsx @@ -183,7 +183,7 @@ export function AlertInstances({ export const AlertInstancesWithApi = withBulkAlertOperations(AlertInstances); function getPage(items: any[], pagination: Pagination) { - return chunk(items, pagination.size)[pagination.index]; + return chunk(items, pagination.size)[pagination.index] || []; } interface AlertInstanceListItemStatus {