From aaf73ff5f67a9163773ff43868f329e3b5693242 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Fri, 21 Feb 2025 17:38:54 +0100 Subject: [PATCH] [Synthetics] Fix overview error popover !! (#211431) ## Summary Fix overview error popover !! Pings aren't being returned as part of overview data anymore, so had to add redux actions to fetch it separately via an existing API Fixes https://github.com/elastic/kibana/issues/211745 image --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Justin Kambic --- .../styled_components_files.js | 1 - .../synthetics_overview_status.ts | 1 - .../grid_by_group/grid_group_item.tsx | 2 +- .../metric_item/metric_error_icon.tsx | 93 +++++++++++++ .../{ => metric_item}/metric_item.tsx | 23 ++-- .../{ => metric_item}/metric_item_icon.tsx | 123 ++++++------------ .../overview/metric_item/use_latest_error.tsx | 41 ++++++ .../overview/overview/overview_grid.tsx | 2 +- .../contexts/synthetics_settings_context.tsx | 5 + .../synthetics/hooks/use_breadcrumbs.test.tsx | 1 + .../hooks/use_status_by_location_overview.ts | 1 - .../state/monitor_details/actions.ts | 5 + .../synthetics/state/monitor_details/api.ts | 2 +- .../state/monitor_details/effects.ts | 10 ++ .../synthetics/state/monitor_details/index.ts | 21 +++ .../state/monitor_details/selectors.ts | 1 + .../__mocks__/synthetics_store.mock.ts | 3 + 17 files changed, 235 insertions(+), 100 deletions(-) create mode 100644 x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/metric_item/metric_error_icon.tsx rename x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/{ => metric_item}/metric_item.tsx (91%) rename x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/{ => metric_item}/metric_item_icon.tsx (58%) create mode 100644 x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/metric_item/use_latest_error.tsx diff --git a/packages/kbn-babel-preset/styled_components_files.js b/packages/kbn-babel-preset/styled_components_files.js index f9fa1d2f0b43a..d101a8defc486 100644 --- a/packages/kbn-babel-preset/styled_components_files.js +++ b/packages/kbn-babel-preset/styled_components_files.js @@ -121,7 +121,6 @@ module.exports = { /x-pack[\/\\]solutions[\/\\]observability[\/\\]plugins[\/\\]synthetics[\/\\]public[\/\\]apps[\/\\]synthetics[\/\\]components[\/\\]monitor_add_edit[\/\\]fields[\/\\]key_value_field.tsx/, /x-pack[\/\\]solutions[\/\\]observability[\/\\]plugins[\/\\]synthetics[\/\\]public[\/\\]apps[\/\\]synthetics[\/\\]components[\/\\]monitors_page[\/\\]overview[\/\\]overview[\/\\]actions_popover.tsx/, /x-pack[\/\\]solutions[\/\\]observability[\/\\]plugins[\/\\]synthetics[\/\\]public[\/\\]apps[\/\\]synthetics[\/\\]components[\/\\]monitors_page[\/\\]overview[\/\\]overview[\/\\]grid_by_group[\/\\]grid_items_by_group.tsx/, - /x-pack[\/\\]solutions[\/\\]observability[\/\\]plugins[\/\\]synthetics[\/\\]public[\/\\]apps[\/\\]synthetics[\/\\]components[\/\\]monitors_page[\/\\]overview[\/\\]overview[\/\\]metric_item_icon.tsx/, /x-pack[\/\\]solutions[\/\\]observability[\/\\]plugins[\/\\]synthetics[\/\\]public[\/\\]apps[\/\\]synthetics[\/\\]components[\/\\]settings[\/\\]alerting_defaults[\/\\]connector_field.tsx/, /x-pack[\/\\]solutions[\/\\]observability[\/\\]plugins[\/\\]synthetics[\/\\]public[\/\\]apps[\/\\]synthetics[\/\\]components[\/\\]test_now_mode[\/\\]browser[\/\\]browser_test_results.tsx/, /x-pack[\/\\]solutions[\/\\]observability[\/\\]plugins[\/\\]synthetics[\/\\]public[\/\\]apps[\/\\]synthetics[\/\\]components[\/\\]test_now_mode[\/\\]simple[\/\\]ping_list[\/\\]columns[\/\\]ping_error.tsx/, diff --git a/x-pack/solutions/observability/plugins/synthetics/common/runtime_types/monitor_management/synthetics_overview_status.ts b/x-pack/solutions/observability/plugins/synthetics/common/runtime_types/monitor_management/synthetics_overview_status.ts index aec3d3ac3390f..87fe9cdadaddb 100644 --- a/x-pack/solutions/observability/plugins/synthetics/common/runtime_types/monitor_management/synthetics_overview_status.ts +++ b/x-pack/solutions/observability/plugins/synthetics/common/runtime_types/monitor_management/synthetics_overview_status.ts @@ -51,7 +51,6 @@ export const OverviewStatusMetaDataCodec = t.intersection([ t.partial({ projectId: t.string, updated_at: t.string, - ping: OverviewPingCodec, timestamp: t.string, spaceId: t.string, }), diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/grid_by_group/grid_group_item.tsx b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/grid_by_group/grid_group_item.tsx index 6fcf90f631fad..31e913cd8b68e 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/grid_by_group/grid_group_item.tsx +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/grid_by_group/grid_group_item.tsx @@ -25,7 +25,7 @@ import { OverviewLoader } from '../overview_loader'; import { useFilteredGroupMonitors } from './use_filtered_group_monitors'; import { OverviewStatusMetaData } from '../../types'; import { selectOverviewStatus } from '../../../../../state/overview_status'; -import { MetricItem } from '../metric_item'; +import { MetricItem } from '../metric_item/metric_item'; const PER_ROW = 4; const DEFAULT_ROW_SIZE = 2; diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/metric_item/metric_error_icon.tsx b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/metric_item/metric_error_icon.tsx new file mode 100644 index 0000000000000..10cea56014292 --- /dev/null +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/metric_item/metric_error_icon.tsx @@ -0,0 +1,93 @@ +/* + * 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 { EuiButtonIcon, useEuiShadow, useEuiTheme } from '@elastic/eui'; +import * as React from 'react'; +import { useRef } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { i18n } from '@kbn/i18n'; +import { css } from '@emotion/react'; +import { useSyntheticsSettingsContext } from '../../../../../contexts'; +import { selectErrorPopoverState, toggleErrorPopoverOpen } from '../../../../../state'; + +export const MetricErrorIcon = ({ configIdByLocation }: { configIdByLocation: string }) => { + const isPopoverOpen = useSelector(selectErrorPopoverState); + const dispatch = useDispatch(); + + const setIsPopoverOpen = () => { + dispatch(toggleErrorPopoverOpen(configIdByLocation)); + }; + const timer = useRef(null); + const euiShadow = useEuiShadow('s'); + + const theme = useEuiTheme().euiTheme; + const { darkMode } = useSyntheticsSettingsContext(); + + return ( +
{ + // show popover with delay + if (timer.current) { + clearTimeout(timer.current); + } + timer.current = setTimeout(() => { + setIsPopoverOpen(); + }, 300); + }} + onMouseLeave={() => { + if (isPopoverOpen) { + return; + } else if (timer.current) { + clearTimeout(timer.current); + } + }} + onClick={() => { + if (configIdByLocation === isPopoverOpen) { + dispatch(toggleErrorPopoverOpen(null)); + } else { + dispatch(toggleErrorPopoverOpen(configIdByLocation)); + } + }} + onKeyDown={(e) => { + if (e.key === 'Enter') { + if (configIdByLocation === isPopoverOpen) { + dispatch(toggleErrorPopoverOpen(null)); + } else { + dispatch(toggleErrorPopoverOpen(configIdByLocation)); + } + } + }} + > + +
+ ); +}; +const ERROR_DETAILS = i18n.translate('xpack.synthetics.errorDetails.label', { + defaultMessage: 'Error details', +}); diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/metric_item.tsx b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/metric_item/metric_item.tsx similarity index 91% rename from x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/metric_item.tsx rename to x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/metric_item/metric_item.tsx index 97af322dee989..3d2fbe8698088 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/metric_item.tsx +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/metric_item/metric_item.tsx @@ -13,25 +13,25 @@ import { useKibana } from '@kbn/kibana-react-plugin/public'; import moment from 'moment'; import React, { useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { OverviewStatusMetaData } from '../../../../../../../common/runtime_types'; -import { ClientPluginsStart } from '../../../../../../plugin'; -import { useLocationName, useStatusByLocationOverview } from '../../../../hooks'; +import { OverviewStatusMetaData } from '../../../../../../../../common/runtime_types'; +import { ClientPluginsStart } from '../../../../../../../plugin'; +import { useLocationName, useStatusByLocationOverview } from '../../../../../hooks'; import { selectErrorPopoverState, selectOverviewTrends, toggleErrorPopoverOpen, -} from '../../../../state'; +} from '../../../../../state'; import { hideTestNowFlyoutAction, manualTestRunInProgressSelector, toggleTestNowFlyoutAction, -} from '../../../../state/manual_test_runs'; -import { formatDuration } from '../../../../utils/formatting'; -import { ActionsPopover } from './actions_popover'; -import { MetricItemBody } from './metric_item/metric_item_body'; -import { MetricItemExtra } from './metric_item/metric_item_extra'; +} from '../../../../../state/manual_test_runs'; +import { formatDuration } from '../../../../../utils/formatting'; +import { ActionsPopover } from '../actions_popover'; +import { MetricItemBody } from './metric_item_body'; +import { MetricItemExtra } from './metric_item_extra'; import { MetricItemIcon } from './metric_item_icon'; -import { FlyoutParamProps } from './types'; +import { FlyoutParamProps } from '../types'; const METRIC_ITEM_HEIGHT = 160; @@ -75,7 +75,7 @@ export const MetricItem = ({ const [isPopoverOpen, setIsPopoverOpen] = useState(false); const isErrorPopoverOpen = useSelector(selectErrorPopoverState); const locationName = useLocationName(monitor); - const { status, timestamp, ping, configIdByLocation } = useStatusByLocationOverview({ + const { status, timestamp, configIdByLocation } = useStatusByLocationOverview({ configId: monitor.configId, locationId: monitor.locationId, }); @@ -190,7 +190,6 @@ export const MetricItem = ({ diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/metric_item_icon.tsx b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/metric_item/metric_item_icon.tsx similarity index 58% rename from x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/metric_item_icon.tsx rename to x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/metric_item/metric_item_icon.tsx index f932b74d07ba9..11fb4cdc91ce5 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/metric_item_icon.tsx +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/metric_item/metric_item_icon.tsx @@ -14,23 +14,24 @@ import { EuiPopoverTitle, EuiPopoverFooter, EuiButton, - useEuiShadow, EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiLink, EuiSpacer, + EuiSkeletonText, } from '@elastic/eui'; import { useDispatch, useSelector } from 'react-redux'; -import styled from 'styled-components'; +import styled from '@emotion/styled'; import { i18n } from '@kbn/i18n'; -import { euiStyled } from '@kbn/kibana-react-plugin/common'; -import { useRef } from 'react'; -import { selectErrorPopoverState, toggleErrorPopoverOpen } from '../../../../state'; -import { useErrorDetailsLink } from '../../../common/links/error_details_link'; -import { OverviewPing, OverviewStatusMetaData } from '../../../../../../../common/runtime_types'; -import { isTestRunning, manualTestRunSelector } from '../../../../state/manual_test_runs'; -import { useDateFormat } from '../../../../../../hooks/use_date_format'; + +import { MetricErrorIcon } from './metric_error_icon'; +import { OverviewStatusMetaData } from '../../../../../../../../common/runtime_types'; +import { isTestRunning, manualTestRunSelector } from '../../../../../state/manual_test_runs'; +import { selectErrorPopoverState, toggleErrorPopoverOpen } from '../../../../../state'; +import { useErrorDetailsLink } from '../../../../common/links/error_details_link'; +import { useDateFormat } from '../../../../../../../hooks/use_date_format'; +import { useLatestError } from './use_latest_error'; const Container = styled.div` display: inline-block; @@ -43,7 +44,6 @@ const Container = styled.div` export const MetricItemIcon = ({ monitor, status, - ping, timestamp, configIdByLocation, }: { @@ -51,27 +51,24 @@ export const MetricItemIcon = ({ status: string; configIdByLocation: string; timestamp?: string; - ping?: OverviewPing; }) => { const testNowRun = useSelector(manualTestRunSelector(monitor.configId)); const isPopoverOpen = useSelector(selectErrorPopoverState); + const { latestPing } = useLatestError({ + configIdByLocation, + monitorId: monitor.configId, + locationLabel: monitor.locationLabel, + }); const dispatch = useDispatch(); - const timer = useRef(null); - - const setIsPopoverOpen = () => { - dispatch(toggleErrorPopoverOpen(configIdByLocation)); - }; - const inProgress = isTestRunning(testNowRun); const errorLink = useErrorDetailsLink({ configId: monitor.configId, - stateId: ping?.state?.id!, + stateId: latestPing?.state?.id!, locationId: monitor.locationId, }); - const euiShadow = useEuiShadow('s'); const formatter = useDateFormat(); const testTime = formatter(timestamp); @@ -94,42 +91,7 @@ export const MetricItemIcon = ({ return ( { - // show popover with delay - if (timer.current) { - clearTimeout(timer.current); - } - timer.current = setTimeout(() => { - setIsPopoverOpen(); - }, 300); - }} - onMouseLeave={() => { - if (isPopoverOpen) { - return; - } else if (timer.current) { - clearTimeout(timer.current); - } - }} - boxShadow={euiShadow} - onClick={() => { - if (configIdByLocation === isPopoverOpen) { - dispatch(toggleErrorPopoverOpen(null)); - } else { - dispatch(toggleErrorPopoverOpen(configIdByLocation)); - } - }} - > - - - } + button={} isOpen={configIdByLocation === isPopoverOpen} closePopover={closePopover} anchorPosition="upCenter" @@ -145,27 +107,43 @@ export const MetricItemIcon = ({ data-test-subj="syntheticsMetricItemIconButton" iconType="cross" onClick={closePopover} + aria-label={i18n.translate( + 'xpack.synthetics.metricItemIcon.euiButtonIcon.closePopover', + { + defaultMessage: 'Close popover', + } + )} /> -
- {ping?.url?.full && ( +
+ {latestPing?.url?.full && ( <> {i18n.translate('xpack.synthetics.metricItemIcon.div.urlLabel', { defaultMessage: 'URL: ', })} - {ping.url.full} + {latestPing.url.full} )} - + + ) + } + color="danger" + iconType="warning" + />
); } else { - if (ping?.url) { + if (latestPing?.url) { return ( ` - box-sizing: border-box; - display: flex; - flex-direction: column; - align-items: center; - gap: 10px; - width: 32px; - height: 32px; - background: ${({ theme }) => - theme.darkMode ? theme.eui.euiColorDarkestShade : theme.eui.euiColorLightestShade}; - border: 1px solid ${({ theme }) => - theme.darkMode ? theme.eui.euiColorDarkShade : theme.eui.euiColorLightShade}; - ${({ boxShadow }) => boxShadow} - border-radius: 16px; - flex: none; - order: 0; - flex-grow: 0; -`; diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/metric_item/use_latest_error.tsx b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/metric_item/use_latest_error.tsx new file mode 100644 index 0000000000000..767385138f6ad --- /dev/null +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/metric_item/use_latest_error.tsx @@ -0,0 +1,41 @@ +/* + * 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 { useSyntheticsRefreshContext } from '../../../../../contexts'; +import { + getMonitorLastErrorRunAction, + selectErrorPopoverState, + selectLastErrorRunMetadata, +} from '../../../../../state'; + +interface UseMonitorLatestPingParams { + monitorId: string; + locationLabel: string; + configIdByLocation: string; +} + +export const useLatestError = ({ + monitorId, + locationLabel, + configIdByLocation, +}: UseMonitorLatestPingParams) => { + const dispatch = useDispatch(); + const { lastRefresh } = useSyntheticsRefreshContext(); + const isPopoverOpen = useSelector(selectErrorPopoverState); + + const { data: latestPing, loading } = useSelector(selectLastErrorRunMetadata); + + useEffect(() => { + if (monitorId && locationLabel && isPopoverOpen === configIdByLocation) { + dispatch(getMonitorLastErrorRunAction.get({ monitorId, locationLabel })); + } + }, [dispatch, monitorId, locationLabel, lastRefresh, isPopoverOpen, configIdByLocation]); + + return { loading, latestPing }; +}; diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_grid.tsx b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_grid.tsx index 507b971c6a40f..57dffe6477673 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_grid.tsx +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/components/monitors_page/overview/overview/overview_grid.tsx @@ -18,6 +18,7 @@ import { EuiAutoSizer, EuiAutoSize, } from '@elastic/eui'; +import { MetricItem } from './metric_item/metric_item'; import { ShowAllSpaces } from '../../common/show_all_spaces'; import { OverviewStatusMetaData } from '../../../../../../../common/runtime_types'; import { quietFetchOverviewStatusAction } from '../../../../state/overview_status'; @@ -40,7 +41,6 @@ import { SortFields } from './sort_fields'; import { NoMonitorsFound } from '../../common/no_monitors_found'; import { MonitorDetailFlyout } from './monitor_detail_flyout'; import { useSyntheticsRefreshContext } from '../../../../contexts'; -import { MetricItem } from './metric_item'; import { FlyoutParamProps } from './types'; const ITEM_HEIGHT = 172; diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/contexts/synthetics_settings_context.tsx b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/contexts/synthetics_settings_context.tsx index 518920ee0fd52..da9db1e163927 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/contexts/synthetics_settings_context.tsx +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/contexts/synthetics_settings_context.tsx @@ -56,6 +56,7 @@ export interface SyntheticsSettingsContextValues { isDev?: boolean; isServerless?: boolean; setBreadcrumbs?: (crumbs: ChromeBreadcrumb[]) => void; + darkMode: boolean; } const { BASE_PATH } = CONTEXT_DEFAULTS; @@ -76,6 +77,7 @@ const defaultContext: SyntheticsSettingsContextValues = { isDev: false, canSave: false, canManagePrivateLocations: false, + darkMode: false, }; export const SyntheticsSettingsContext = createContext(defaultContext); @@ -91,6 +93,7 @@ export const SyntheticsSettingsContextProvider: React.FC { return { + darkMode, canSave, isDev, basePath, @@ -116,6 +120,7 @@ export const SyntheticsSettingsContextProvider: React.FC { ('[MONITOR DETAILS] GET LAST RUN'); +export const getMonitorLastErrorRunAction = createAsyncAction< + { monitorId: string; locationLabel: string }, + { ping?: Ping } +>('[MONITOR DETAILS] GET LAST ERROR RUN'); + export const resetMonitorLastRunAction = createAction('[MONITOR DETAILS] LAST RUN RESET'); export const updateMonitorLastRunAction = createAction<{ data: Ping }>( diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_details/api.ts b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_details/api.ts index 31abd049a4dac..3e35295221ce8 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_details/api.ts +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_details/api.ts @@ -43,7 +43,7 @@ export const fetchMonitorRecentPings = async ({ { monitorId, from: from ?? moment().subtract(30, 'days').toISOString(), - to: to ?? new Date().toISOString(), + to: to ?? moment().toISOString(), locations, sort, size, diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_details/effects.ts b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_details/effects.ts index bd7a9e8386c20..ee97477db95b5 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_details/effects.ts +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_details/effects.ts @@ -15,6 +15,7 @@ import { getMonitorRecentPingsAction, getMonitorAction, updateMonitorLastRunAction, + getMonitorLastErrorRunAction, } from './actions'; import { fetchSyntheticsMonitor, fetchMonitorRecentPings, fetchLatestTestRun } from './api'; import { selectLastRunMetadata } from './selectors'; @@ -38,6 +39,15 @@ export function* fetchSyntheticsMonitorEffect() { ) ); + yield takeLeading( + getMonitorLastErrorRunAction.get, + fetchEffectFactory( + fetchLatestTestRun, + getMonitorLastErrorRunAction.success, + getMonitorLastErrorRunAction.fail + ) + ); + // Additional listener on `getMonitorRecentPingsAction.success` to possibly update the `lastRun` as well yield takeEvery( getMonitorRecentPingsAction.success, diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_details/index.ts b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_details/index.ts index d7130d7240926..2dd51a0c93694 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_details/index.ts +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_details/index.ts @@ -20,6 +20,7 @@ import { setMonitorDetailsLocationAction, getMonitorAction, setStatusFilter, + getMonitorLastErrorRunAction, } from './actions'; export interface MonitorDetailsState { @@ -33,6 +34,10 @@ export interface MonitorDetailsState { loading: boolean; loaded: boolean; }; + lastErrorRun: { + data?: Ping; + loading: boolean; + }; syntheticsMonitorLoading: boolean; syntheticsMonitor: SyntheticsMonitorWithId | null; syntheticsMonitorError?: IHttpSerializedFetchError | null; @@ -45,6 +50,7 @@ export interface MonitorDetailsState { const initialState: MonitorDetailsState = { pings: { total: 0, data: [], loading: false }, lastRun: { loading: false, loaded: false }, + lastErrorRun: { loading: false }, syntheticsMonitor: null, syntheticsMonitorLoading: false, syntheticsMonitorDispatchedAt: 0, @@ -73,6 +79,21 @@ export const monitorDetailsReducer = createReducer(initialState, (builder) => { state.lastRun.loading = false; state.error = action.payload; }) + .addCase(getMonitorLastErrorRunAction.get, (state, action) => { + state.lastErrorRun.loading = true; + const configId = state.lastErrorRun?.data?.config_id; + if (action.payload.monitorId !== configId) { + state.lastErrorRun.data = undefined; + } + }) + .addCase(getMonitorLastErrorRunAction.success, (state, action) => { + state.lastErrorRun.loading = false; + state.lastErrorRun.data = action.payload?.ping; + }) + .addCase(getMonitorLastErrorRunAction.fail, (state, action) => { + state.lastErrorRun.loading = false; + state.error = action.payload; + }) .addCase(updateMonitorLastRunAction, (state, action) => { state.lastRun.data = action.payload.data; }) diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_details/selectors.ts b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_details/selectors.ts index 47d116594eb3b..93528f9c0ae5e 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_details/selectors.ts +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/state/monitor_details/selectors.ts @@ -19,6 +19,7 @@ export const selectSelectedLocationId = createSelector( ); export const selectLastRunMetadata = createSelector(getState, (state) => state.lastRun); +export const selectLastErrorRunMetadata = createSelector(getState, (state) => state.lastErrorRun); export const selectPingsLoading = createSelector(getState, (state) => state.pings.loading); diff --git a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts index c18a1d9f6164c..02a0e9bb5b15b 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts +++ b/x-pack/solutions/observability/plugins/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts @@ -198,6 +198,9 @@ function getBrowserJourneyMockSlice() { function getMonitorDetailsMockSlice() { return { + lastErrorRun: { + loading: false, + }, lastRun: { loading: false, loaded: true,