Skip to content
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