mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
# Backport This will backport the following commits from `main` to `8.6`: - [[Synthetics] Update monitor detail data on refresh (#145468)](https://github.com/elastic/kibana/pull/145468) <!--- Backport version: 8.9.7 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Justin Kambic","email":"jk@elastic.co"},"sourceCommit":{"committedDate":"2022-11-23T19:20:42Z","message":"[Synthetics] Update monitor detail data on refresh (#145468)\n\n## Summary\r\n\r\nResolves #143662.\r\n\r\nResolves #144367.\r\n\r\nThis patch will add refresh capabilities to the monitor detail page and\r\nits child views. Today, the page needs to be hard-refreshed in order for\r\nthis to work.\r\n\r\n### Arch changes\r\n\r\n#### App-wide refresh interval\r\n\r\nTo simplify how auto-refreshing works, I have\r\n[extracted](https://github.com/elastic/kibana/pull/145468/files#diff-09f3a6de5b54c7d3933df933d83e963f6ff6a5c18a3d83238bd658623595fe35L47)\r\nthe existing `interval` logic from the Overview page and put it into the\r\n[`RefreshContext`](https://github.com/elastic/kibana/pull/145468/files#diff-660f9a1ed6f641662eff6682448bac7d1b167b4ea518ddc43634192b2959be1fR36)\r\nitself. I am not overly fond of this solution, so if someone has a\r\nbetter suggestion for how to handle this, I am open to recommendations.\r\n\r\nThis refresh context also ties into the date picker components we use\r\nfor some of these pages, so everything should continue to work\r\nseamlessly, but there's going to be a conflicting auto-refresh between\r\ndate pickers and the internal context value itself. It might be\r\nworthwhile for us to discuss this in a tech sync and have a dedicated\r\nenhancement.\r\n\r\n#### New absolute date formatting hook\r\n\r\nI've introduced a [new hook for changing relative date values into\r\nabsolute ISO\r\nstrings](https://github.com/elastic/kibana/pull/145468/files#diff-48912058ac736ca8aece834bb50369cc5589b94b9c8b2b3e418e9d7690241525).\r\nThis is because we are making extensive use of Exploratory View\r\nEmbeddables, which do not have any kind of refresh mechanism exposed.\r\nThe simplest way to get these to refresh when our `lastRefresh` field\r\nupdates is to supply the embeddable with absolute values, which\r\ncorrespond directly to the existing `from` and `to` fields we're already\r\nusing.\r\n\r\n#### Existing hook updates\r\n\r\nI have tried to avoid manipulating the existing behavior of our fetch\r\nhooks, but there were a few cases where I added optional parameters to\r\nthem to support refresh functionality. It's worth extra attention to\r\nmake sure we're not adding something that we should avoid for these\r\nhooks.\r\n\r\n### Testing this PR\r\n\r\nThe best way to test this PR is to configure a monitor and open the\r\ndetail pages for `Overview`, `History`, and `Errors`. Let your monitor\r\nrun for some time and look at all the visualizations, while avoiding a\r\nhard refresh. If you run this patch from source, you can also modify the\r\n[interval](https://github.com/elastic/kibana/pull/145468/files#diff-660f9a1ed6f641662eff6682448bac7d1b167b4ea518ddc43634192b2959be1fR39)\r\nfrom `30s` to some smaller value if you want to make sure things are\r\nupdating where they should more easily.","sha":"06b7596810a285868db58c9040da868ff8ab6e4d","branchLabelMapping":{"^v8.7.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["bug","Team:uptime","release_note:skip","ci:cloud-deploy","v8.6.0","v8.7.0"],"number":145468,"url":"https://github.com/elastic/kibana/pull/145468","mergeCommit":{"message":"[Synthetics] Update monitor detail data on refresh (#145468)\n\n## Summary\r\n\r\nResolves #143662.\r\n\r\nResolves #144367.\r\n\r\nThis patch will add refresh capabilities to the monitor detail page and\r\nits child views. Today, the page needs to be hard-refreshed in order for\r\nthis to work.\r\n\r\n### Arch changes\r\n\r\n#### App-wide refresh interval\r\n\r\nTo simplify how auto-refreshing works, I have\r\n[extracted](https://github.com/elastic/kibana/pull/145468/files#diff-09f3a6de5b54c7d3933df933d83e963f6ff6a5c18a3d83238bd658623595fe35L47)\r\nthe existing `interval` logic from the Overview page and put it into the\r\n[`RefreshContext`](https://github.com/elastic/kibana/pull/145468/files#diff-660f9a1ed6f641662eff6682448bac7d1b167b4ea518ddc43634192b2959be1fR36)\r\nitself. I am not overly fond of this solution, so if someone has a\r\nbetter suggestion for how to handle this, I am open to recommendations.\r\n\r\nThis refresh context also ties into the date picker components we use\r\nfor some of these pages, so everything should continue to work\r\nseamlessly, but there's going to be a conflicting auto-refresh between\r\ndate pickers and the internal context value itself. It might be\r\nworthwhile for us to discuss this in a tech sync and have a dedicated\r\nenhancement.\r\n\r\n#### New absolute date formatting hook\r\n\r\nI've introduced a [new hook for changing relative date values into\r\nabsolute ISO\r\nstrings](https://github.com/elastic/kibana/pull/145468/files#diff-48912058ac736ca8aece834bb50369cc5589b94b9c8b2b3e418e9d7690241525).\r\nThis is because we are making extensive use of Exploratory View\r\nEmbeddables, which do not have any kind of refresh mechanism exposed.\r\nThe simplest way to get these to refresh when our `lastRefresh` field\r\nupdates is to supply the embeddable with absolute values, which\r\ncorrespond directly to the existing `from` and `to` fields we're already\r\nusing.\r\n\r\n#### Existing hook updates\r\n\r\nI have tried to avoid manipulating the existing behavior of our fetch\r\nhooks, but there were a few cases where I added optional parameters to\r\nthem to support refresh functionality. It's worth extra attention to\r\nmake sure we're not adding something that we should avoid for these\r\nhooks.\r\n\r\n### Testing this PR\r\n\r\nThe best way to test this PR is to configure a monitor and open the\r\ndetail pages for `Overview`, `History`, and `Errors`. Let your monitor\r\nrun for some time and look at all the visualizations, while avoiding a\r\nhard refresh. If you run this patch from source, you can also modify the\r\n[interval](https://github.com/elastic/kibana/pull/145468/files#diff-660f9a1ed6f641662eff6682448bac7d1b167b4ea518ddc43634192b2959be1fR39)\r\nfrom `30s` to some smaller value if you want to make sure things are\r\nupdating where they should more easily.","sha":"06b7596810a285868db58c9040da868ff8ab6e4d"}},"sourceBranch":"main","suggestedTargetBranches":["8.6"],"targetPullRequestStates":[{"branch":"8.6","label":"v8.6.0","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v8.7.0","labelRegex":"^v8.7.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/145468","number":145468,"mergeCommit":{"message":"[Synthetics] Update monitor detail data on refresh (#145468)\n\n## Summary\r\n\r\nResolves #143662.\r\n\r\nResolves #144367.\r\n\r\nThis patch will add refresh capabilities to the monitor detail page and\r\nits child views. Today, the page needs to be hard-refreshed in order for\r\nthis to work.\r\n\r\n### Arch changes\r\n\r\n#### App-wide refresh interval\r\n\r\nTo simplify how auto-refreshing works, I have\r\n[extracted](https://github.com/elastic/kibana/pull/145468/files#diff-09f3a6de5b54c7d3933df933d83e963f6ff6a5c18a3d83238bd658623595fe35L47)\r\nthe existing `interval` logic from the Overview page and put it into the\r\n[`RefreshContext`](https://github.com/elastic/kibana/pull/145468/files#diff-660f9a1ed6f641662eff6682448bac7d1b167b4ea518ddc43634192b2959be1fR36)\r\nitself. I am not overly fond of this solution, so if someone has a\r\nbetter suggestion for how to handle this, I am open to recommendations.\r\n\r\nThis refresh context also ties into the date picker components we use\r\nfor some of these pages, so everything should continue to work\r\nseamlessly, but there's going to be a conflicting auto-refresh between\r\ndate pickers and the internal context value itself. It might be\r\nworthwhile for us to discuss this in a tech sync and have a dedicated\r\nenhancement.\r\n\r\n#### New absolute date formatting hook\r\n\r\nI've introduced a [new hook for changing relative date values into\r\nabsolute ISO\r\nstrings](https://github.com/elastic/kibana/pull/145468/files#diff-48912058ac736ca8aece834bb50369cc5589b94b9c8b2b3e418e9d7690241525).\r\nThis is because we are making extensive use of Exploratory View\r\nEmbeddables, which do not have any kind of refresh mechanism exposed.\r\nThe simplest way to get these to refresh when our `lastRefresh` field\r\nupdates is to supply the embeddable with absolute values, which\r\ncorrespond directly to the existing `from` and `to` fields we're already\r\nusing.\r\n\r\n#### Existing hook updates\r\n\r\nI have tried to avoid manipulating the existing behavior of our fetch\r\nhooks, but there were a few cases where I added optional parameters to\r\nthem to support refresh functionality. It's worth extra attention to\r\nmake sure we're not adding something that we should avoid for these\r\nhooks.\r\n\r\n### Testing this PR\r\n\r\nThe best way to test this PR is to configure a monitor and open the\r\ndetail pages for `Overview`, `History`, and `Errors`. Let your monitor\r\nrun for some time and look at all the visualizations, while avoiding a\r\nhard refresh. If you run this patch from source, you can also modify the\r\n[interval](https://github.com/elastic/kibana/pull/145468/files#diff-660f9a1ed6f641662eff6682448bac7d1b167b4ea518ddc43634192b2959be1fR39)\r\nfrom `30s` to some smaller value if you want to make sure things are\r\nupdating where they should more easily.","sha":"06b7596810a285868db58c9040da868ff8ab6e4d"}}]}] BACKPORT--> Co-authored-by: Justin Kambic <jk@elastic.co>
This commit is contained in:
parent
1467f2491f
commit
fcc857c2f9
14 changed files with 132 additions and 41 deletions
|
@ -11,7 +11,7 @@ import { isStepEnd } from '../../common/monitor_test_result/browser_steps_list';
|
|||
import { JourneyStep, SyntheticsJourneyApiResponse } from '../../../../../../common/runtime_types';
|
||||
import { fetchJourneySteps } from '../../../state';
|
||||
|
||||
export const useJourneySteps = (checkGroup?: string) => {
|
||||
export const useJourneySteps = (checkGroup?: string, lastRefresh?: number) => {
|
||||
const { stepIndex } = useParams<{ stepIndex: string }>();
|
||||
const { checkGroupId: urlCheckGroup } = useParams<{ checkGroupId: string }>();
|
||||
|
||||
|
@ -23,7 +23,7 @@ export const useJourneySteps = (checkGroup?: string) => {
|
|||
}
|
||||
|
||||
return fetchJourneySteps({ checkGroup: checkGroupId });
|
||||
}, [checkGroupId]);
|
||||
}, [checkGroupId, lastRefresh]);
|
||||
|
||||
const isFailed =
|
||||
data?.steps.some(
|
||||
|
|
|
@ -13,6 +13,7 @@ import { useSelectedLocation } from './use_selected_location';
|
|||
import { getMonitorRecentPingsAction, selectMonitorPingsMetadata } from '../../../state';
|
||||
|
||||
interface UseMonitorPingsProps {
|
||||
lastRefresh?: number;
|
||||
pageSize?: number;
|
||||
pageIndex?: number;
|
||||
from?: string;
|
||||
|
@ -45,6 +46,7 @@ export const useMonitorPings = (props?: UseMonitorPingsProps) => {
|
|||
dispatch,
|
||||
monitorId,
|
||||
locationLabel,
|
||||
props?.lastRefresh,
|
||||
props?.pageSize,
|
||||
props?.pageIndex,
|
||||
props?.from,
|
||||
|
|
|
@ -9,6 +9,7 @@ import { useEffect, useMemo } from 'react';
|
|||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { ConfigKey } from '../../../../../../common/runtime_types';
|
||||
import { useSyntheticsRefreshContext } from '../../../contexts';
|
||||
import {
|
||||
getMonitorAction,
|
||||
selectEncryptedSyntheticsSavedMonitors,
|
||||
|
@ -24,6 +25,7 @@ export const useSelectedMonitor = () => {
|
|||
() => monitorsList.find((monitor) => monitor[ConfigKey.CONFIG_ID] === monitorId) ?? null,
|
||||
[monitorId, monitorsList]
|
||||
);
|
||||
const { lastRefresh } = useSyntheticsRefreshContext();
|
||||
const { syntheticsMonitor, syntheticsMonitorLoading } = useSelector(selectorMonitorDetailsState);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
|
@ -43,6 +45,10 @@ export const useSelectedMonitor = () => {
|
|||
}
|
||||
}, [dispatch, monitorId, availableMonitor, syntheticsMonitorLoading]);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getMonitorAction.get({ monitorId }));
|
||||
}, [dispatch, monitorId, lastRefresh]);
|
||||
|
||||
return {
|
||||
monitor: availableMonitor,
|
||||
loading: syntheticsMonitorLoading || monitorListLoading,
|
||||
|
|
|
@ -12,10 +12,10 @@ import {
|
|||
EuiTitle,
|
||||
useEuiTheme,
|
||||
} from '@elastic/eui';
|
||||
import React, { useMemo } from 'react';
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FailedTestsCount } from './failed_tests_count';
|
||||
import { useGetUrlParams } from '../../../hooks';
|
||||
import { useAbsoluteDate, useGetUrlParams } from '../../../hooks';
|
||||
import { SyntheticsDatePicker } from '../../common/date_picker/synthetics_date_picker';
|
||||
import { MonitorErrorsCount } from '../monitor_summary/monitor_errors_count';
|
||||
import { ErrorsList } from './errors_list';
|
||||
|
@ -26,10 +26,7 @@ export const MonitorErrors = () => {
|
|||
|
||||
const { dateRangeStart, dateRangeEnd } = useGetUrlParams();
|
||||
|
||||
const time = useMemo(
|
||||
() => ({ from: dateRangeStart, to: dateRangeEnd }),
|
||||
[dateRangeEnd, dateRangeStart]
|
||||
);
|
||||
const time = useAbsoluteDate({ from: dateRangeStart, to: dateRangeEnd });
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -43,10 +40,10 @@ export const MonitorErrors = () => {
|
|||
</EuiTitle>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<MonitorErrorsCount to={dateRangeEnd} from={dateRangeStart} />
|
||||
<MonitorErrorsCount to={time.to} from={time.from} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<FailedTestsCount to={dateRangeEnd} from={dateRangeStart} />
|
||||
<FailedTestsCount from={time.from} to={time.to} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPanel>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import { EuiFlexGrid, EuiFlexGroup, EuiFlexItem, EuiPanel, EuiTitle } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { useCallback } from 'react';
|
||||
import { useUrlParams } from '../../../hooks';
|
||||
import { useAbsoluteDate, useUrlParams } from '../../../hooks';
|
||||
import { useDimensions } from '../../../hooks';
|
||||
import { SyntheticsDatePicker } from '../../common/date_picker/synthetics_date_picker';
|
||||
import { AvailabilityPanel } from '../monitor_summary/availability_panel';
|
||||
|
@ -28,6 +28,7 @@ const STATS_WIDTH_SINGLE_COLUMN_THRESHOLD = 360; // ✨ determined by trial and
|
|||
export const MonitorHistory = () => {
|
||||
const [useGetUrlParams, updateUrlParams] = useUrlParams();
|
||||
const { dateRangeStart, dateRangeEnd } = useGetUrlParams();
|
||||
const { from, to } = useAbsoluteDate({ from: dateRangeStart, to: dateRangeEnd });
|
||||
|
||||
const { elementRef: statsRef, width: statsWidth } = useDimensions<HTMLDivElement>();
|
||||
const statsColumns = statsWidth && statsWidth < STATS_WIDTH_SINGLE_COLUMN_THRESHOLD ? 1 : 2;
|
||||
|
@ -56,45 +57,45 @@ export const MonitorHistory = () => {
|
|||
<EuiFlexItem>
|
||||
<EuiFlexGroup gutterSize="xs">
|
||||
<EuiFlexItem>
|
||||
<MonitorCompleteCount from={dateRangeStart} to={dateRangeEnd} />
|
||||
<MonitorCompleteCount from={from} to={to} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<MonitorCompleteSparklines from={dateRangeStart} to={dateRangeEnd} />
|
||||
<MonitorCompleteSparklines from={from} to={to} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFlexGroup gutterSize="xs">
|
||||
<EuiFlexItem>
|
||||
<AvailabilityPanel from={dateRangeStart} to={dateRangeEnd} />
|
||||
<AvailabilityPanel from={from} to={to} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<AvailabilitySparklines from={dateRangeStart} to={dateRangeEnd} />
|
||||
<AvailabilitySparklines from={from} to={to} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFlexGroup gutterSize="xs">
|
||||
<EuiFlexItem>
|
||||
<MonitorErrorsCount from={dateRangeStart} to={dateRangeEnd} />
|
||||
<MonitorErrorsCount from={from} to={to} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<MonitorErrorSparklines from={dateRangeStart} to={dateRangeEnd} />
|
||||
<MonitorErrorSparklines from={from} to={to} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiFlexGroup gutterSize="xs">
|
||||
<EuiFlexItem>
|
||||
<DurationPanel from={dateRangeStart} to={dateRangeEnd} />
|
||||
<DurationPanel from={from} to={to} />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<DurationSparklines from={dateRangeStart} to={dateRangeEnd} />
|
||||
<DurationSparklines from={from} to={to} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<MonitorTotalRunsCount from={dateRangeStart} to={dateRangeEnd} />
|
||||
<MonitorTotalRunsCount from={from} to={to} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGrid>
|
||||
</EuiPanel>
|
||||
|
@ -104,15 +105,15 @@ export const MonitorHistory = () => {
|
|||
<EuiTitle size="xs">
|
||||
<h3>{DURATION_TREND_LABEL}</h3>
|
||||
</EuiTitle>
|
||||
<MonitorDurationTrend from={dateRangeStart} to={dateRangeEnd} />
|
||||
<MonitorDurationTrend from={from} to={to} />
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<MonitorStatusPanel
|
||||
from={dateRangeStart}
|
||||
to={dateRangeEnd}
|
||||
from={from}
|
||||
to={to}
|
||||
showViewHistoryButton={false}
|
||||
periodCaption={''}
|
||||
brushable={true}
|
||||
|
@ -120,7 +121,7 @@ export const MonitorHistory = () => {
|
|||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<TestRunsTable from={dateRangeStart} to={dateRangeEnd} />
|
||||
<TestRunsTable from={from} to={to} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
|
|
|
@ -30,7 +30,7 @@ import {
|
|||
} from '../../../../../../common/runtime_types';
|
||||
import { formatTestRunAt } from '../../../utils/monitor_test_result/test_time_formats';
|
||||
|
||||
import { useSyntheticsSettingsContext } from '../../../contexts';
|
||||
import { useSyntheticsRefreshContext, useSyntheticsSettingsContext } from '../../../contexts';
|
||||
import { BrowserStepsList } from '../../common/monitor_test_result/browser_steps_list';
|
||||
import { SinglePingResult } from '../../common/monitor_test_result/single_ping_result';
|
||||
import { parseBadgeStatus, StatusBadge } from '../../common/monitor_test_result/status_badge';
|
||||
|
@ -43,10 +43,12 @@ import { useMonitorLatestPing } from '../hooks/use_monitor_latest_ping';
|
|||
export const LastTestRun = () => {
|
||||
const { euiTheme } = useEuiTheme();
|
||||
const { latestPing, loading: pingsLoading } = useMonitorLatestPing();
|
||||
const { lastRefresh } = useSyntheticsRefreshContext();
|
||||
const { monitor } = useSelectedMonitor();
|
||||
|
||||
const { data: stepsData, loading: stepsLoading } = useJourneySteps(
|
||||
latestPing?.monitor?.check_group
|
||||
latestPing?.monitor?.check_group,
|
||||
lastRefresh
|
||||
);
|
||||
|
||||
const loading = stepsLoading || pingsLoading;
|
||||
|
|
|
@ -31,10 +31,13 @@ import { AvailabilitySparklines } from './availability_sparklines';
|
|||
import { LastTestRun } from './last_test_run';
|
||||
import { LAST_10_TEST_RUNS, TestRunsTable } from './test_runs_table';
|
||||
import { MonitorErrorsCount } from './monitor_errors_count';
|
||||
import { useAbsoluteDate } from '../../../hooks';
|
||||
|
||||
export const MonitorSummary = () => {
|
||||
const { from, loading } = useEarliestStartDate();
|
||||
const to = 'now';
|
||||
const { from: fromRelative, loading } = useEarliestStartDate();
|
||||
const toRelative = 'now';
|
||||
|
||||
const { from, to } = useAbsoluteDate({ from: fromRelative, to: toRelative });
|
||||
|
||||
if (loading) {
|
||||
return <EuiLoadingSpinner size="xl" />;
|
||||
|
|
|
@ -16,9 +16,11 @@ import { useMonitorQueryId } from '../hooks/use_monitor_query_id';
|
|||
import { useSelectedMonitor } from '../hooks/use_selected_monitor';
|
||||
import { ClientPluginsStart } from '../../../../../plugin';
|
||||
import { useSelectedLocation } from '../hooks/use_selected_location';
|
||||
import { useAbsoluteDate } from '../../../hooks';
|
||||
|
||||
export const StepDurationPanel = ({ legendPosition }: { legendPosition?: Position }) => {
|
||||
const { observability } = useKibana<ClientPluginsStart>().services;
|
||||
const time = useAbsoluteDate({ from: 'now-24h/h', to: 'now' });
|
||||
|
||||
const { ExploratoryViewEmbeddable } = observability;
|
||||
|
||||
|
@ -60,6 +62,7 @@ export const StepDurationPanel = ({ legendPosition }: { legendPosition?: Positio
|
|||
legendPosition={legendPosition}
|
||||
attributes={[
|
||||
{
|
||||
time,
|
||||
name: DURATION_BY_STEP_LABEL,
|
||||
reportDefinitions: {
|
||||
'monitor.id': [monitorId],
|
||||
|
@ -67,7 +70,6 @@ export const StepDurationPanel = ({ legendPosition }: { legendPosition?: Positio
|
|||
},
|
||||
selectedMetricField: isBrowser ? 'synthetics.step.duration.us' : 'monitor.duration.us',
|
||||
dataType: 'synthetics',
|
||||
time: { from: 'now-24h/h', to: 'now' },
|
||||
breakdown: isBrowser ? 'synthetics.step.name.keyword' : 'observer.geo.name',
|
||||
operationType: 'last_value',
|
||||
seriesType: 'area_stacked',
|
||||
|
|
|
@ -35,6 +35,7 @@ import { parseBadgeStatus, StatusBadge } from '../../common/monitor_test_result/
|
|||
import { useSelectedMonitor } from '../hooks/use_selected_monitor';
|
||||
import { useMonitorPings } from '../hooks/use_monitor_pings';
|
||||
import { JourneyScreenshot } from '../../common/screenshot/journey_screenshot';
|
||||
import { useSyntheticsRefreshContext } from '../../../contexts';
|
||||
|
||||
type SortableField = 'timestamp' | 'monitor.status' | 'monitor.duration.us';
|
||||
|
||||
|
@ -52,6 +53,7 @@ export const TestRunsTable = ({ paginable = true, from, to }: TestRunsTableProps
|
|||
|
||||
const [sortField, setSortField] = useState<SortableField>('timestamp');
|
||||
const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('desc');
|
||||
const { lastRefresh } = useSyntheticsRefreshContext();
|
||||
const {
|
||||
pings,
|
||||
total,
|
||||
|
@ -59,6 +61,7 @@ export const TestRunsTable = ({ paginable = true, from, to }: TestRunsTableProps
|
|||
} = useMonitorPings({
|
||||
from,
|
||||
to,
|
||||
lastRefresh,
|
||||
pageSize: page.size,
|
||||
pageIndex: page.index,
|
||||
});
|
||||
|
|
|
@ -37,20 +37,13 @@ export const OverviewPage: React.FC = () => {
|
|||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const { refreshApp, lastRefresh } = useSyntheticsRefreshContext();
|
||||
const { lastRefresh } = useSyntheticsRefreshContext();
|
||||
const { query } = useGetUrlParams();
|
||||
const { search } = useLocation();
|
||||
|
||||
const pageState = useSelector(selectOverviewPageState);
|
||||
const { loading: locationsLoading, locationsLoaded } = useSelector(selectServiceLocationsState);
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
refreshApp();
|
||||
}, 1000 * 30);
|
||||
return () => clearInterval(interval);
|
||||
}, [refreshApp]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!locationsLoading && !locationsLoaded) {
|
||||
dispatch(getServiceLocations());
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { createContext, useContext, useMemo, useState } from 'react';
|
||||
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
interface SyntheticsRefreshContext {
|
||||
lastRefresh: number;
|
||||
|
@ -24,14 +24,21 @@ export const SyntheticsRefreshContext = createContext(defaultContext);
|
|||
export const SyntheticsRefreshContextProvider: React.FC = ({ children }) => {
|
||||
const [lastRefresh, setLastRefresh] = useState<number>(Date.now());
|
||||
|
||||
const refreshApp = () => {
|
||||
const refreshApp = useCallback(() => {
|
||||
const refreshTime = Date.now();
|
||||
setLastRefresh(refreshTime);
|
||||
};
|
||||
}, [setLastRefresh]);
|
||||
|
||||
const value = useMemo(() => {
|
||||
return { lastRefresh, refreshApp };
|
||||
}, [lastRefresh]);
|
||||
}, [lastRefresh, refreshApp]);
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
refreshApp();
|
||||
}, 1000 * 30);
|
||||
return () => clearInterval(interval);
|
||||
}, [refreshApp]);
|
||||
|
||||
return <SyntheticsRefreshContext.Provider value={value} children={children} />;
|
||||
};
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export * from './use_absolute_date';
|
||||
export * from './use_url_params';
|
||||
export * from './use_breadcrumbs';
|
||||
export * from './use_service_allowed';
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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 datemath from '@elastic/datemath';
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import moment, { Moment } from 'moment';
|
||||
import { useAbsoluteDate } from './use_absolute_date';
|
||||
|
||||
describe('useAbsoluteDate', () => {
|
||||
let datemathSpy: jest.SpyInstance<Moment | undefined>;
|
||||
|
||||
beforeEach(() => {
|
||||
datemathSpy = jest.spyOn(datemath, 'parse');
|
||||
});
|
||||
|
||||
afterEach(() => jest.clearAllMocks());
|
||||
|
||||
it('returns a parsed value for `from` and `to`', () => {
|
||||
datemathSpy.mockReturnValueOnce(moment('2022-11-18T18:54:06.342Z'));
|
||||
datemathSpy.mockReturnValueOnce(moment('2022-11-19T18:54:06.342Z'));
|
||||
|
||||
const {
|
||||
result: {
|
||||
current: { from, to },
|
||||
},
|
||||
} = renderHook(() => useAbsoluteDate({ from: 'now-15m', to: 'now' }));
|
||||
|
||||
expect(datemathSpy).toHaveBeenCalledTimes(2);
|
||||
expect(datemathSpy.mock.calls).toEqual([['now-15m'], ['now']]);
|
||||
expect(from).toEqual('2022-11-18T18:54:06.342Z');
|
||||
expect(to).toEqual('2022-11-19T18:54:06.342Z');
|
||||
});
|
||||
|
||||
it('returns the original string if datemath cannot parse the value', () => {
|
||||
datemathSpy.mockReturnValue(undefined);
|
||||
const {
|
||||
result: {
|
||||
current: { from, to },
|
||||
},
|
||||
} = renderHook(() => useAbsoluteDate({ from: 'someinvalidvalue', to: 'anotherinvalidvalue' }));
|
||||
|
||||
expect(datemathSpy).toHaveBeenCalledTimes(2);
|
||||
expect(datemathSpy.mock.calls).toEqual([['someinvalidvalue'], ['anotherinvalidvalue']]);
|
||||
expect(from).toEqual('someinvalidvalue');
|
||||
expect(to).toEqual('anotherinvalidvalue');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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 datemath from '@elastic/datemath';
|
||||
import { useMemo } from 'react';
|
||||
import { useSyntheticsRefreshContext } from '../contexts';
|
||||
|
||||
export function useAbsoluteDate({ from, to }: { from: string; to: string }) {
|
||||
const { lastRefresh } = useSyntheticsRefreshContext();
|
||||
return useMemo(
|
||||
() => ({
|
||||
from: datemath.parse(from)?.toISOString() ?? from,
|
||||
to: datemath.parse(to)?.toISOString() ?? to,
|
||||
}),
|
||||
// we want to recompute these any time the app refreshes
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[from, to, lastRefresh]
|
||||
);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue