From 10cdc3c07061f18e6e61e8fe444d572b56972b95 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) 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 (cherry picked from commit aaf73ff5f67a9163773ff43868f329e3b5693242) --- .../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 | 3 +- .../{ => 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, 225 insertions(+), 90 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 (98%) 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 42564e7010df7..c9393397c6c12 100644 --- a/packages/kbn-babel-preset/styled_components_files.js +++ b/packages/kbn-babel-preset/styled_components_files.js @@ -152,7 +152,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 98% 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 c8c266db63736..2329d3f6e80e1 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 @@ -69,7 +69,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, }); @@ -184,7 +184,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 f0612498f8664..0c077faf6b9d3 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 dc71f90ea9e18..10d3145bd560f 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 @@ -197,6 +197,9 @@ function getBrowserJourneyMockSlice() { function getMonitorDetailsMockSlice() { return { + lastErrorRun: { + loading: false, + }, lastRun: { loading: false, loaded: true,