mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
# Backport This will backport the following commits from `main` to `8.18`: - [[Synthetics] Fix overview error popover !! (#211431)](https://github.com/elastic/kibana/pull/211431) <!--- Backport version: 9.6.6 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport) <!--BACKPORT [{"author":{"name":"Shahzad","email":"shahzad31comp@gmail.com"},"sourceCommit":{"committedDate":"2025-02-21T16:38:54Z","message":"[Synthetics] Fix overview error popover !! (#211431)\n\n## Summary\n\nFix overview error popover !!\n\nPings aren't being returned as part of overview data anymore, so had to\nadd redux actions to fetch it separately via an existing API\n\nFixes https://github.com/elastic/kibana/issues/211745\n\n\n<img width=\"1728\" alt=\"image\"\nsrc=\"https://github.com/user-attachments/assets/2244948f-e42d-443d-b6e7-42e0a72b1bfa\"\n/>\n\n---------\n\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>\nCo-authored-by: Justin Kambic <jk@elastic.co>","sha":"aaf73ff5f67a9163773ff43868f329e3b5693242","branchLabelMapping":{"^v9.1.0$":"main","^v8.19.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","Team:obs-ux-management","backport:version","v8.18.0","v9.1.0"],"title":"[Synthetics] Fix overview error popover !!","number":211431,"url":"https://github.com/elastic/kibana/pull/211431","mergeCommit":{"message":"[Synthetics] Fix overview error popover !! (#211431)\n\n## Summary\n\nFix overview error popover !!\n\nPings aren't being returned as part of overview data anymore, so had to\nadd redux actions to fetch it separately via an existing API\n\nFixes https://github.com/elastic/kibana/issues/211745\n\n\n<img width=\"1728\" alt=\"image\"\nsrc=\"https://github.com/user-attachments/assets/2244948f-e42d-443d-b6e7-42e0a72b1bfa\"\n/>\n\n---------\n\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>\nCo-authored-by: Justin Kambic <jk@elastic.co>","sha":"aaf73ff5f67a9163773ff43868f329e3b5693242"}},"sourceBranch":"main","suggestedTargetBranches":["8.18"],"targetPullRequestStates":[{"branch":"9.0","label":"v9.0.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"url":"https://github.com/elastic/kibana/pull/212123","number":212123,"state":"MERGED","mergeCommit":{"sha":"83e37babbff8e702c51c2746c790930510f4ccb4","message":"[9.0] [Synthetics] Fix overview error popover !! (#211431) (#212123)\n\n# Backport\n\nThis will backport the following commits from `main` to `9.0`:\n- [[Synthetics] Fix overview error popover !!\n(#211431)](https://github.com/elastic/kibana/pull/211431)\n\n\n\n### Questions ?\nPlease refer to the [Backport tool\ndocumentation](https://github.com/sorenlouv/backport)\n\n\n\nCo-authored-by: Shahzad <shahzad31comp@gmail.com>"}},{"branch":"8.18","label":"v8.18.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/211431","number":211431,"mergeCommit":{"message":"[Synthetics] Fix overview error popover !! (#211431)\n\n## Summary\n\nFix overview error popover !!\n\nPings aren't being returned as part of overview data anymore, so had to\nadd redux actions to fetch it separately via an existing API\n\nFixes https://github.com/elastic/kibana/issues/211745\n\n\n<img width=\"1728\" alt=\"image\"\nsrc=\"https://github.com/user-attachments/assets/2244948f-e42d-443d-b6e7-42e0a72b1bfa\"\n/>\n\n---------\n\nCo-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>\nCo-authored-by: Justin Kambic <jk@elastic.co>","sha":"aaf73ff5f67a9163773ff43868f329e3b5693242"}}]}] BACKPORT--> --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
a00807b7a9
commit
8dd76bedd8
17 changed files with 234 additions and 99 deletions
|
@ -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/,
|
||||
|
|
|
@ -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,
|
||||
}),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<NodeJS.Timeout | null>(null);
|
||||
const euiShadow = useEuiShadow('s');
|
||||
|
||||
const theme = useEuiTheme().euiTheme;
|
||||
const { darkMode } = useSyntheticsSettingsContext();
|
||||
|
||||
return (
|
||||
<div
|
||||
css={css`
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background: ${darkMode ? theme.colors.backgroundBaseSubdued : theme.colors.lightestShade};
|
||||
border: 1px solid ${darkMode ? theme.colors.darkShade : theme.colors.lightShade};
|
||||
box-shadow: ${euiShadow};
|
||||
border-radius: 16px;
|
||||
flex: none;
|
||||
order: 0;
|
||||
flex-grow: 0;
|
||||
`}
|
||||
onMouseEnter={() => {
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<EuiButtonIcon
|
||||
data-test-subj="syntheticsMetricItemIconButton"
|
||||
iconType="warning"
|
||||
color="danger"
|
||||
size="m"
|
||||
aria-label={ERROR_DETAILS}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
const ERROR_DETAILS = i18n.translate('xpack.synthetics.errorDetails.label', {
|
||||
defaultMessage: 'Error details',
|
||||
});
|
|
@ -15,24 +15,24 @@ import { useTheme } from '@kbn/observability-shared-plugin/public';
|
|||
import moment from 'moment';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
|
||||
import { FlyoutParamProps } from './types';
|
||||
import { MetricItemBody } from './metric_item/metric_item_body';
|
||||
import { FlyoutParamProps } from '../types';
|
||||
import { MetricItemBody } from './metric_item_body';
|
||||
import {
|
||||
selectErrorPopoverState,
|
||||
selectOverviewTrends,
|
||||
toggleErrorPopoverOpen,
|
||||
} from '../../../../state';
|
||||
import { useLocationName, useStatusByLocationOverview } from '../../../../hooks';
|
||||
import { formatDuration } from '../../../../utils/formatting';
|
||||
import { OverviewStatusMetaData } from '../../../../../../../common/runtime_types';
|
||||
import { ActionsPopover } from './actions_popover';
|
||||
} from '../../../../../state';
|
||||
import { useLocationName, useStatusByLocationOverview } from '../../../../../hooks';
|
||||
import { formatDuration } from '../../../../../utils/formatting';
|
||||
import { OverviewStatusMetaData } from '../../../../../../../../common/runtime_types';
|
||||
import { ActionsPopover } from '../actions_popover';
|
||||
import {
|
||||
hideTestNowFlyoutAction,
|
||||
manualTestRunInProgressSelector,
|
||||
toggleTestNowFlyoutAction,
|
||||
} from '../../../../state/manual_test_runs';
|
||||
} from '../../../../../state/manual_test_runs';
|
||||
import { MetricItemIcon } from './metric_item_icon';
|
||||
import { MetricItemExtra } from './metric_item/metric_item_extra';
|
||||
import { MetricItemExtra } from './metric_item_extra';
|
||||
|
||||
const METRIC_ITEM_HEIGHT = 160;
|
||||
|
||||
|
@ -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 = ({
|
|||
<MetricItemIcon
|
||||
monitor={monitor}
|
||||
status={status}
|
||||
ping={ping}
|
||||
timestamp={timestamp}
|
||||
configIdByLocation={configIdByLocation}
|
||||
/>
|
|
@ -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<NodeJS.Timeout | null>(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 (
|
||||
<Container>
|
||||
<EuiPopover
|
||||
button={
|
||||
<StyledIcon
|
||||
onMouseEnter={() => {
|
||||
// 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));
|
||||
}
|
||||
}}
|
||||
>
|
||||
<EuiButtonIcon
|
||||
data-test-subj="syntheticsMetricItemIconButton"
|
||||
iconType="warning"
|
||||
color="danger"
|
||||
size="m"
|
||||
aria-label={ERROR_DETAILS}
|
||||
/>
|
||||
</StyledIcon>
|
||||
}
|
||||
button={<MetricErrorIcon configIdByLocation={configIdByLocation} />}
|
||||
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',
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPopoverTitle>
|
||||
<div style={{ width: '300px' }}>
|
||||
{ping?.url?.full && (
|
||||
<div style={{ width: '300px', overflowWrap: 'break-word' }}>
|
||||
{latestPing?.url?.full && (
|
||||
<>
|
||||
{i18n.translate('xpack.synthetics.metricItemIcon.div.urlLabel', {
|
||||
defaultMessage: 'URL: ',
|
||||
})}
|
||||
<EuiLink
|
||||
data-test-subj="syntheticsMetricItemIconLink"
|
||||
href={ping.url.full}
|
||||
href={latestPing.url.full}
|
||||
target="_blank"
|
||||
>
|
||||
{ping.url.full}
|
||||
{latestPing.url.full}
|
||||
</EuiLink>
|
||||
<EuiSpacer size="s" />
|
||||
</>
|
||||
)}
|
||||
<EuiCallOut title={ping?.error?.message} color="danger" iconType="warning" />
|
||||
<EuiCallOut
|
||||
title={
|
||||
latestPing?.error?.message ? (
|
||||
latestPing?.error?.message
|
||||
) : (
|
||||
<EuiSkeletonText lines={2} />
|
||||
)
|
||||
}
|
||||
color="danger"
|
||||
iconType="warning"
|
||||
/>
|
||||
</div>
|
||||
<EuiPopoverFooter>
|
||||
<EuiButton
|
||||
|
@ -181,14 +159,14 @@ export const MetricItemIcon = ({
|
|||
</Container>
|
||||
);
|
||||
} else {
|
||||
if (ping?.url) {
|
||||
if (latestPing?.url) {
|
||||
return (
|
||||
<Container>
|
||||
<EuiButtonIcon
|
||||
title={ping.url.full}
|
||||
title={latestPing.url.full}
|
||||
color="text"
|
||||
data-test-subj="syntheticsMetricItemIconButton"
|
||||
href={ping.url.full}
|
||||
href={latestPing.url.full}
|
||||
iconType="link"
|
||||
target="_blank"
|
||||
aria-label={i18n.translate('xpack.synthetics.metricItemIcon.euiButtonIcon.monitorUrl', {
|
||||
|
@ -209,22 +187,3 @@ const ERROR_DETAILS = i18n.translate('xpack.synthetics.errorDetails.label', {
|
|||
const TEST_IN_PROGRESS = i18n.translate('xpack.synthetics.inProgress.label', {
|
||||
defaultMessage: 'Manual test run is in progress.',
|
||||
});
|
||||
|
||||
const StyledIcon = euiStyled.div<{ boxShadow: string }>`
|
||||
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;
|
||||
`;
|
|
@ -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 };
|
||||
};
|
|
@ -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;
|
||||
|
|
|
@ -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<PropsWithChildren<Synth
|
|||
commonlyUsedRanges,
|
||||
isDev,
|
||||
isServerless,
|
||||
darkMode,
|
||||
} = props;
|
||||
|
||||
const { dateRangeStart, dateRangeEnd } = useGetUrlParams();
|
||||
|
@ -103,6 +106,7 @@ export const SyntheticsSettingsContextProvider: React.FC<PropsWithChildren<Synth
|
|||
|
||||
const value = useMemo(() => {
|
||||
return {
|
||||
darkMode,
|
||||
canSave,
|
||||
isDev,
|
||||
basePath,
|
||||
|
@ -116,6 +120,7 @@ export const SyntheticsSettingsContextProvider: React.FC<PropsWithChildren<Synth
|
|||
canManagePrivateLocations,
|
||||
};
|
||||
}, [
|
||||
darkMode,
|
||||
canSave,
|
||||
isDev,
|
||||
basePath,
|
||||
|
|
|
@ -55,6 +55,7 @@ describe('useBreadcrumbs', () => {
|
|||
<Route path={OVERVIEW_ROUTE}>
|
||||
<SyntheticsSettingsContext.Provider
|
||||
value={{
|
||||
darkMode: false,
|
||||
basePath: '/app/synthetics',
|
||||
canSave: true,
|
||||
dateRangeStart: '',
|
||||
|
|
|
@ -36,6 +36,5 @@ export const getConfigStatusByLocation = (
|
|||
configIdByLocation,
|
||||
status: config?.status || 'unknown',
|
||||
timestamp: config?.timestamp,
|
||||
ping: config?.ping,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -24,6 +24,11 @@ export const getMonitorLastRunAction = createAsyncAction<
|
|||
{ ping?: Ping }
|
||||
>('[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 }>(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
})
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -197,6 +197,9 @@ function getBrowserJourneyMockSlice() {
|
|||
|
||||
function getMonitorDetailsMockSlice() {
|
||||
return {
|
||||
lastErrorRun: {
|
||||
loading: false,
|
||||
},
|
||||
lastRun: {
|
||||
loading: false,
|
||||
loaded: true,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue