Skip to content

Commit

Permalink
[Synthetics UI] Add pagination and date filtering to test runs table (#…
Browse files Browse the repository at this point in the history
…144029)

Co-authored-by: Shahzad <shahzad.muhammad@elastic.co>
  • Loading branch information
Alejandro Fernández Gómez and shahzad31 authored Oct 31, 2022
1 parent 7f8e78f commit fdce066
Show file tree
Hide file tree
Showing 11 changed files with 320 additions and 186 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ export const GetPingsParamsType = t.intersection([
excludedLocations: t.string,
index: t.number,
size: t.number,
pageIndex: t.number,
locations: t.string,
monitorId: t.string,
sort: t.string,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* 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 { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';

import { useSelectedMonitor } from './use_selected_monitor';
import { useSelectedLocation } from './use_selected_location';
import { getMonitorRecentPingsAction, selectMonitorPingsMetadata } from '../../../state';

interface UseMonitorPingsProps {
pageSize?: number;
pageIndex?: number;
from?: string;
to?: string;
}

export const useMonitorPings = (props?: UseMonitorPingsProps) => {
const dispatch = useDispatch();

const { monitor } = useSelectedMonitor();
const location = useSelectedLocation();

const monitorId = monitor?.id;
const locationLabel = location?.label;

useEffect(() => {
if (monitorId && locationLabel) {
dispatch(
getMonitorRecentPingsAction.get({
monitorId,
locationId: locationLabel,
size: props?.pageSize,
pageIndex: props?.pageIndex,
from: props?.from,
to: props?.to,
})
);
}
}, [
dispatch,
monitorId,
locationLabel,
props?.pageSize,
props?.pageIndex,
props?.from,
props?.to,
]);

const { total, data: pings, loading } = useSelector(selectMonitorPingsMetadata);

return {
loading,
total,
pings,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { DurationPanel } from './duration_panel';
import { MonitorDetailsPanel } from './monitor_details_panel';
import { AvailabilitySparklines } from './availability_sparklines';
import { LastTestRun } from './last_test_run';
import { LastTenTestRuns } from './last_ten_test_runs';
import { TestRunsTable } from './test_runs_table';
import { MonitorErrorsCount } from './monitor_errors_count';

export const MonitorSummary = () => {
Expand Down Expand Up @@ -107,7 +107,7 @@ export const MonitorSummary = () => {
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="m" />
<LastTenTestRuns />
<TestRunsTable paginable={false} from={from} to={to} />
</>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,27 +31,44 @@ import {
import { useSyntheticsSettingsContext } from '../../../contexts/synthetics_settings_context';

import { sortPings } from '../../../utils/monitor_test_result/sort_pings';
import { selectPingsLoading, selectMonitorRecentPings, selectPingsError } from '../../../state';
import { selectPingsError } from '../../../state';
import { parseBadgeStatus, StatusBadge } from '../../common/monitor_test_result/status_badge';
import { isStepEnd } from '../../common/monitor_test_result/browser_steps_list';
import { JourneyStepScreenshotContainer } from '../../common/monitor_test_result/journey_step_screenshot_container';

import { useKibanaDateFormat } from '../../../../../hooks/use_kibana_date_format';
import { useSelectedMonitor } from '../hooks/use_selected_monitor';
import { useMonitorPings } from '../hooks/use_monitor_pings';
import { useJourneySteps } from '../hooks/use_journey_steps';

type SortableField = 'timestamp' | 'monitor.status' | 'monitor.duration.us';

export const LastTenTestRuns = () => {
interface TestRunsTableProps {
from: string;
to: string;
paginable?: boolean;
}

export const TestRunsTable = ({ paginable = true, from, to }: TestRunsTableProps) => {
const { basePath } = useSyntheticsSettingsContext();
const [page, setPage] = useState({ index: 0, size: 10 });

const [sortField, setSortField] = useState<SortableField>('timestamp');
const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('desc');
const pings = useSelector(selectMonitorRecentPings);
const {
pings,
total,
loading: pingsLoading,
} = useMonitorPings({
from,
to,
pageSize: page.size,
pageIndex: page.index,
});
const sortedPings = useMemo(() => {
return sortPings(pings, sortField, sortDirection);
}, [pings, sortField, sortDirection]);
const pingsLoading = useSelector(selectPingsLoading);

const pingsError = useSelector(selectPingsError);
const { monitor } = useSelectedMonitor();

Expand All @@ -64,7 +81,10 @@ export const LastTenTestRuns = () => {
},
};

const handleTableChange = ({ page, sort }: Criteria<Ping>) => {
const handleTableChange = ({ page: newPage, sort }: Criteria<Ping>) => {
if (newPage !== undefined) {
setPage(newPage);
}
if (sort !== undefined) {
setSortField(sort.field as SortableField);
setSortDirection(sort.direction);
Expand Down Expand Up @@ -125,7 +145,7 @@ export const LastTenTestRuns = () => {
<EuiFlexGroup alignItems="center" gutterSize="s">
<EuiFlexItem grow={false}>
<EuiTitle size="xs">
<h3>{pings?.length >= 10 ? LAST_10_TEST_RUNS : TEST_RUNS}</h3>
<h3>{paginable || pings?.length < 10 ? TEST_RUNS : LAST_10_TEST_RUNS}</h3>
</EuiTitle>
</EuiFlexItem>
<EuiFlexItem grow={true} />
Expand Down Expand Up @@ -162,6 +182,16 @@ export const LastTenTestRuns = () => {
tableLayout={'auto'}
sorting={sorting}
onChange={handleTableChange}
pagination={
paginable
? {
pageIndex: page.index,
pageSize: page.size,
totalItemCount: total,
pageSizeOptions: [10, 20, 50], // TODO Confirm with Henry,
}
: undefined
}
/>
</EuiPanel>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ export const getMonitorAction = createAsyncAction<
>('[MONITOR DETAILS] GET MONITOR');

export const getMonitorRecentPingsAction = createAsyncAction<
{ monitorId: string; locationId: string },
{
monitorId: string;
locationId: string;
size?: number;
pageIndex?: number;
from?: string;
to?: string;
},
PingsResponse
>('[MONITOR DETAILS] GET RECENT PINGS');
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,32 @@ export interface QueryParams {
export const fetchMonitorRecentPings = async ({
monitorId,
locationId,
from,
to,
size = 10,
pageIndex = 0,
}: {
monitorId: string;
locationId: string;
from?: string;
to?: string;
size?: number;
pageIndex?: number;
}): Promise<PingsResponse> => {
const from = new Date(0).toISOString();
const to = new Date().toISOString();
const locations = JSON.stringify([locationId]);
const sort = 'desc';
const size = 10;

return await apiService.get(
SYNTHETICS_API_URLS.PINGS,
{ monitorId, from, to, locations, sort, size },
{
monitorId,
from: from ?? new Date(0).toISOString(),
to: to ?? new Date().toISOString(),
locations,
sort,
size,
pageIndex,
},
PingsResponseType
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,19 @@ import {
} from './actions';

export interface MonitorDetailsState {
pings: Ping[];
loading: boolean;
pings: {
total: number;
data: Ping[];
loading: boolean;
};
syntheticsMonitorLoading: boolean;
syntheticsMonitor: EncryptedSyntheticsSavedMonitor | null;
error: IHttpSerializedFetchError | null;
selectedLocationId: string | null;
}

const initialState: MonitorDetailsState = {
pings: [],
loading: false,
pings: { total: 0, data: [], loading: false },
syntheticsMonitor: null,
syntheticsMonitorLoading: false,
error: null,
Expand All @@ -42,16 +44,19 @@ export const monitorDetailsReducer = createReducer(initialState, (builder) => {
})

.addCase(getMonitorRecentPingsAction.get, (state, action) => {
state.loading = true;
state.pings = state.pings.filter((ping) => !checkIsStalePing(action.payload.monitorId, ping));
state.pings.loading = true;
state.pings.data = state.pings.data.filter(
(ping) => !checkIsStalePing(action.payload.monitorId, ping)
);
})
.addCase(getMonitorRecentPingsAction.success, (state, action) => {
state.pings = action.payload.pings;
state.loading = false;
state.pings.total = action.payload.total;
state.pings.data = action.payload.pings;
state.pings.loading = false;
})
.addCase(getMonitorRecentPingsAction.fail, (state, action) => {
state.error = action.payload;
state.loading = false;
state.pings.loading = false;
})

.addCase(getMonitorAction.get, (state) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ export const selectSelectedLocationId = createSelector(
(state) => state.selectedLocationId
);

export const selectLatestPing = createSelector(getState, (state) => state.pings?.[0] ?? null);
export const selectLatestPing = createSelector(getState, (state) => state.pings.data[0] ?? null);

export const selectPingsLoading = createSelector(getState, (state) => state.loading);
export const selectPingsLoading = createSelector(getState, (state) => state.pings.loading);

export const selectMonitorRecentPings = createSelector(getState, (state) => state.pings);
export const selectMonitorPingsMetadata = createSelector(getState, (state) => state.pings);

export const selectPingsError = createSelector(getState, (state) => state.error);
Loading

0 comments on commit fdce066

Please sign in to comment.