From 3040db172819c61cb305cd565acef0b4db3611f7 Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Thu, 23 May 2024 16:52:08 -0400 Subject: [PATCH 01/15] Handled bounded search volume with multiple requests. --- .../server/common/pings/query_pings.ts | 65 ++++++++++++++++--- 1 file changed, 57 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/observability_solution/synthetics/server/common/pings/query_pings.ts b/x-pack/plugins/observability_solution/synthetics/server/common/pings/query_pings.ts index 7b60fc8fcf536..4c36fb4af2eab 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/common/pings/query_pings.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/common/pings/query_pings.ts @@ -10,6 +10,7 @@ import { QueryDslFieldAndFormat, QueryDslQueryContainer, } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { SortResults } from '@elastic/elasticsearch/lib/api/types'; import { SUMMARY_FILTER } from '../../../common/constants/client_defaults'; import { UptimeEsClient } from '../../lib'; import { @@ -36,6 +37,20 @@ type GetParamsWithFields = GetPingsParams & { type GetParamsWithoutFields = GetPingsParams; +interface QueryPingsSearchBody { + size: number; + from: number; + query: { + bool: { + filter: QueryDslQueryContainer[]; + }; + }; + sort: Array<{ '@timestamp': { order: 'asc' | 'desc' } }>; + _source: boolean; + fields: QueryFields; + search_after?: SortResults; +} + export async function queryPings( params: (GetParamsWithFields | GetParamsWithoutFields) & { uptimeEsClient: UptimeEsClient } ): Promise { @@ -53,7 +68,7 @@ export async function queryPings( } = params; const size = sizeParam ?? DEFAULT_PAGE_SIZE; - const searchBody = { + const searchBody: QueryPingsSearchBody = { size, from: pageIndex !== undefined ? pageIndex * size : 0, ...(index ? { from: index * size } : {}), @@ -96,15 +111,36 @@ export async function queryPings( searchBody._source = false; searchBody.fields = params.fields; - const { - body: { - hits: { hits, total }, - }, - } = await uptimeEsClient.search({ body: searchBody }); + let latestTotal = 0; + let hitCount; + let afterKey; + const mapPromises: Array> = []; + + /** + * By default Elasticsearch only returns 10k documents in a single search. + * Users may have many thousands of documents in a given time range. + * This procedure paginates the results and offloads excess fields in parallel. + */ + do { + if (afterKey) { + searchBody.search_after = afterKey; + } + const { + body: { + hits: { hits, total }, + }, + } = await uptimeEsClient.search({ body: searchBody }); + mapPromises.push(extractFieldsFromDocs(hits, params.fieldsExtractorFn)); + hitCount = hits.length; + latestTotal = total.value; + if (hitCount > 0) { + afterKey = hits[hitCount - 1].sort; + } + } while (hitCount === searchBody.size && searchBody.size === 10_000); return { - total: total.value, - pings: hits.map((doc: any) => params.fieldsExtractorFn(doc)), + total: latestTotal, + pings: (await Promise.all(mapPromises)).flat(), }; } @@ -133,6 +169,19 @@ export async function queryPings( }; } +async function extractFieldsFromDocs( + hits: Doc[], + extractor: (doc: Doc) => Fn +): Promise { + return new Promise((resolve, reject) => { + try { + resolve(hits.map(extractor)); + } catch (e: any) { + reject(e); + } + }); +} + function isGetParamsWithFields( params: GetParamsWithFields | GetParamsWithoutFields ): params is GetParamsWithFields { From 1860181b64aaf9001191111d2e27c207039c583f Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Wed, 29 May 2024 16:32:58 -0400 Subject: [PATCH 02/15] Use aggs for monitor status heatmap chart. --- .../common/constants/synthetics/rest_api.ts | 1 + .../common/runtime_types/ping/ping.ts | 14 +++ .../hooks/use_ping_statuses.tsx | 90 ------------------- .../monitor_status/monitor_status_data.ts | 39 ++++---- .../monitor_status/monitor_status_panel.tsx | 12 ++- .../monitor_status/use_monitor_status_data.ts | 76 ++++++++++------ .../apps/synthetics/state/root_effect.ts | 2 + .../apps/synthetics/state/root_reducer.ts | 3 + .../state/status_heatmap/actions.ts | 16 ++++ .../synthetics/state/status_heatmap/api.ts | 31 +++++++ .../state/status_heatmap/effects.ts | 23 +++++ .../synthetics/state/status_heatmap/index.ts | 46 ++++++++++ .../synthetics/state/status_heatmap/models.ts | 14 +++ .../state/status_heatmap/selectors.ts | 14 +++ .../__mocks__/synthetics_store.mock.ts | 5 ++ .../common/pings/monitor_status_heatmap.ts | 90 +++++++++++++++++++ .../synthetics/server/routes/index.ts | 7 +- .../synthetics/server/routes/pings/index.ts | 1 + .../server/routes/pings/ping_heatmap.ts | 42 +++++++++ 19 files changed, 380 insertions(+), 146 deletions(-) delete mode 100644 x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_ping_statuses.tsx create mode 100644 x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/actions.ts create mode 100644 x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/api.ts create mode 100644 x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/effects.ts create mode 100644 x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/index.ts create mode 100644 x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/models.ts create mode 100644 x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/selectors.ts create mode 100644 x-pack/plugins/observability_solution/synthetics/server/common/pings/monitor_status_heatmap.ts create mode 100644 x-pack/plugins/observability_solution/synthetics/server/routes/pings/ping_heatmap.ts diff --git a/x-pack/plugins/observability_solution/synthetics/common/constants/synthetics/rest_api.ts b/x-pack/plugins/observability_solution/synthetics/common/constants/synthetics/rest_api.ts index 0003360c707b6..aea9747ae4128 100644 --- a/x-pack/plugins/observability_solution/synthetics/common/constants/synthetics/rest_api.ts +++ b/x-pack/plugins/observability_solution/synthetics/common/constants/synthetics/rest_api.ts @@ -26,6 +26,7 @@ export enum SYNTHETICS_API_URLS { SYNTHETICS_OVERVIEW = '/internal/synthetics/overview', PINGS = '/internal/synthetics/pings', PING_STATUSES = '/internal/synthetics/ping_statuses', + MONITOR_STATUS_HEATMAP = '/internal/synthetics/ping_heatmap', OVERVIEW_STATUS = `/internal/synthetics/overview_status`, INDEX_SIZE = `/internal/synthetics/index_size`, AGENT_POLICIES = `/internal/synthetics/agent_policies`, diff --git a/x-pack/plugins/observability_solution/synthetics/common/runtime_types/ping/ping.ts b/x-pack/plugins/observability_solution/synthetics/common/runtime_types/ping/ping.ts index 5d481038b0869..5d5beca95aaae 100644 --- a/x-pack/plugins/observability_solution/synthetics/common/runtime_types/ping/ping.ts +++ b/x-pack/plugins/observability_solution/synthetics/common/runtime_types/ping/ping.ts @@ -306,3 +306,17 @@ export const GetPingsParamsType = t.intersection([ ]); export type GetPingsParams = t.TypeOf; + +export const MonitorStatusHeatmapResponseType = t.type({ + doc_count: t.number, + down: t.type({ + value: t.number, + }), + up: t.type({ + value: t.number, + }), + key: t.number, + key_as_string: t.string, +}); + +export type MonitorStatusHeatmapResponse = t.TypeOf; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_ping_statuses.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_ping_statuses.tsx deleted file mode 100644 index cb408678c022c..0000000000000 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/hooks/use_ping_statuses.tsx +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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, useCallback, useRef } from 'react'; -import { useSelector, useDispatch } from 'react-redux'; - -import { ConfigKey, PingStatus } from '../../../../../../common/runtime_types'; -import { - getMonitorPingStatusesAction, - selectIsMonitorStatusesLoading, - selectPingStatusesForMonitorAndLocationAsc, -} from '../../../state'; - -import { useSelectedMonitor } from './use_selected_monitor'; -import { useSelectedLocation } from './use_selected_location'; - -export const usePingStatuses = ({ - from, - to, - size, - monitorInterval, - lastRefresh, -}: { - from: number; - to: number; - size: number; - monitorInterval: number; - lastRefresh: number; -}) => { - const { monitor } = useSelectedMonitor(); - const location = useSelectedLocation(); - - const pingStatusesSelector = useCallback(() => { - return selectPingStatusesForMonitorAndLocationAsc( - monitor?.[ConfigKey.CONFIG_ID] ?? '', - location?.label ?? '' - ); - }, [monitor, location?.label]); - const isLoading = useSelector(selectIsMonitorStatusesLoading); - const pingStatuses = useSelector(pingStatusesSelector()) as PingStatus[]; - const dispatch = useDispatch(); - - const lastCall = useRef({ monitorId: '', locationLabel: '', to: 0, from: 0, lastRefresh: 0 }); - const toDiff = Math.abs(lastCall.current.to - to) / (1000 * 60); - const fromDiff = Math.abs(lastCall.current.from - from) / (1000 * 60); - const lastRefreshDiff = Math.abs(lastCall.current.lastRefresh - lastRefresh) / (1000 * 60); - const isDataChangedEnough = - toDiff >= monitorInterval || - fromDiff >= monitorInterval || - lastRefreshDiff >= 3 || // Minimum monitor interval - monitor?.id !== lastCall.current.monitorId || - location?.label !== lastCall.current.locationLabel; - - useEffect(() => { - if (!isLoading && isDataChangedEnough && monitor?.id && location?.label && from && to && size) { - dispatch( - getMonitorPingStatusesAction.get({ - monitorId: monitor.id, - locationId: location.label, - from, - to, - size, - }) - ); - - lastCall.current = { - monitorId: monitor.id, - locationLabel: location?.label, - to, - from, - lastRefresh, - }; - } - // `isLoading` shouldn't be included in deps - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [dispatch, monitor?.id, location?.label, from, to, size, isDataChangedEnough, lastRefresh]); - - return pingStatuses.filter(({ timestamp }) => { - const timestampN = Number(new Date(timestamp)); - return timestampN >= from && timestampN <= to; - }); -}; - -export const usePingStatusesIsLoading = () => { - return useSelector(selectIsMonitorStatusesLoading) as boolean; -}; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_data.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_data.ts index f588ab242adf9..ecec63ee74ad4 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_data.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_data.ts @@ -16,7 +16,7 @@ import { COLOR_MODES_STANDARD, } from '@elastic/eui'; import type { BrushEvent } from '@elastic/charts'; -import { PingStatus } from '../../../../../../common/runtime_types'; +import { MonitorStatusHeatmapResponse } from '../../../../../../common/runtime_types'; export const SUCCESS_VIZ_COLOR = VISUALIZATION_COLORS[0]; export const DANGER_VIZ_COLOR = VISUALIZATION_COLORS[VISUALIZATION_COLORS.length - 1]; @@ -114,28 +114,25 @@ export function createTimeBuckets(intervalMinutes: number, from: number, to: num export function createStatusTimeBins( timeBuckets: MonitorStatusTimeBucket[], - pingStatuses: PingStatus[] + pingStatuses: MonitorStatusHeatmapResponse[] ): MonitorStatusTimeBin[] { - let iPingStatus = 0; - return (timeBuckets ?? []).map((bucket) => { - const currentBin: MonitorStatusTimeBin = { - start: bucket.start, - end: bucket.end, - ups: 0, - downs: 0, - value: 0, + return timeBuckets.map(({ start, end }) => { + const { up: ups, down: downs } = pingStatuses + .filter(({ key }) => key >= start && key <= end) + .reduce( + (acc, cur) => ({ + up: acc.up + cur.up.value, + down: acc.down + cur.down.value, + }), + { up: 0, down: 0 } + ); + return { + start, + end, + ups, + downs, + value: ups + downs === 0 ? 0 : getStatusEffectiveValue(ups, downs), }; - while ( - iPingStatus < pingStatuses.length && - moment(pingStatuses[iPingStatus].timestamp).valueOf() < bucket.end - ) { - currentBin.ups += pingStatuses[iPingStatus]?.summary.up ?? 0; - currentBin.downs += pingStatuses[iPingStatus]?.summary.down ?? 0; - currentBin.value = getStatusEffectiveValue(currentBin.ups, currentBin.downs); - iPingStatus++; - } - - return currentBin; }); } diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_panel.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_panel.tsx index fc75701098761..0d0285650f95b 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_panel.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_panel.tsx @@ -10,7 +10,6 @@ import React, { useMemo } from 'react'; import { EuiPanel, useEuiTheme, EuiResizeObserver, EuiSpacer } from '@elastic/eui'; import { Chart, Settings, Heatmap, ScaleType, Tooltip, LEGACY_LIGHT_THEME } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; -import { usePingStatusesIsLoading } from '../hooks/use_ping_statuses'; import { MonitorStatusHeader } from './monitor_status_header'; import { MonitorStatusCellTooltip } from './monitor_status_cell_tooltip'; import { MonitorStatusLegend } from './monitor_status_legend'; @@ -32,9 +31,8 @@ export const MonitorStatusPanel = ({ onBrushed, }: MonitorStatusPanelProps) => { const { euiTheme, colorMode } = useEuiTheme(); - const { timeBins, handleResize, getTimeBinByXValue, xDomain, intervalByWidth } = + const { loading, timeBins, handleResize, getTimeBinByXValue, xDomain, minsPerBin } = useMonitorStatusData({ from, to }); - const isPingStatusesLoading = usePingStatusesIsLoading(); const heatmap = useMemo(() => { return getMonitorStatusChartTheme(euiTheme, brushable); @@ -65,7 +63,7 @@ export const MonitorStatusPanel = ({ customTooltip={({ values }) => ( )} /> @@ -87,18 +85,18 @@ export const MonitorStatusPanel = ({ bands: getColorBands(euiTheme, colorMode), }} data={timeBins} - xAccessor={(timeBin) => timeBin.end} + xAccessor={({ end }) => end} yAccessor={() => 'T'} valueAccessor={(timeBin) => timeBin.value} valueFormatter={(d) => d.toFixed(2)} - xAxisLabelFormatter={getXAxisLabelFormatter(intervalByWidth)} + xAxisLabelFormatter={getXAxisLabelFormatter(minsPerBin)} timeZone="UTC" xScale={{ type: ScaleType.Time, interval: { type: 'calendar', unit: 'm', - value: intervalByWidth, + value: minsPerBin, }, }} /> diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/use_monitor_status_data.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/use_monitor_status_data.ts index 3465c229c65ce..74758d58f2fd9 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/use_monitor_status_data.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/use_monitor_status_data.ts @@ -5,22 +5,25 @@ * 2.0. */ -import { useCallback, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { throttle } from 'lodash'; +import { useSelector, useDispatch } from 'react-redux'; import { scheduleToMinutes } from '../../../../../../common/lib/schedule_to_time'; import { useSyntheticsRefreshContext } from '../../../contexts/synthetics_refresh_context'; import { useSelectedMonitor } from '../hooks/use_selected_monitor'; -import { usePingStatuses } from '../hooks/use_ping_statuses'; import { dateToMilli, createTimeBuckets, - createStatusTimeBins, CHART_CELL_WIDTH, indexBinsByEndTime, MonitorStatusPanelProps, + createStatusTimeBins, + MonitorStatusTimeBin, } from './monitor_status_data'; +import { useSelectedLocation } from '../hooks/use_selected_location'; +import { getMonitorStatusHeatmapAction, selectHeatmap } from '../../../state/status_heatmap'; export const useMonitorStatusData = ({ from, @@ -28,24 +31,38 @@ export const useMonitorStatusData = ({ }: Pick) => { const { lastRefresh } = useSyntheticsRefreshContext(); const { monitor } = useSelectedMonitor(); + const location = useSelectedLocation(); const monitorInterval = Math.max(3, monitor?.schedule ? scheduleToMinutes(monitor?.schedule) : 3); const fromMillis = dateToMilli(from); const toMillis = dateToMilli(to); const totalMinutes = Math.ceil(toMillis - fromMillis) / (1000 * 60); - const pingStatuses = usePingStatuses({ - from: fromMillis, - to: toMillis, - size: Math.min(10000, Math.ceil((totalMinutes / monitorInterval) * 2)), // Acts as max size between from - to - monitorInterval, - lastRefresh, - }); - const [binsAvailableByWidth, setBinsAvailableByWidth] = useState(50); - const intervalByWidth = Math.floor( - Math.max(monitorInterval, totalMinutes / binsAvailableByWidth) + const [binsAvailableByWidth, setBinsAvailableByWidth] = useState(null); + const minsPerBin = Math.floor( + Math.max( + monitorInterval, + binsAvailableByWidth !== null ? totalMinutes / binsAvailableByWidth : 0 + ) ); + const dispatch = useDispatch(); + const { heatmap: dateHistogram, loading } = useSelector(selectHeatmap); + + useEffect(() => { + if (monitor?.id && location?.label) { + dispatch( + getMonitorStatusHeatmapAction.get({ + monitorId: monitor.id, + location: location.label, + from, + to, + interval: minsPerBin, + }) + ); + } + }, [dispatch, from, to, minsPerBin, location?.label, monitor?.id, lastRefresh]); + // Disabling deps warning as we wanna throttle the callback // eslint-disable-next-line react-hooks/exhaustive-deps const handleResize = useCallback( @@ -55,25 +72,30 @@ export const useMonitorStatusData = ({ [] ); - const { timeBins, timeBinsByEndTime, xDomain } = useMemo(() => { - const timeBuckets = createTimeBuckets(intervalByWidth, fromMillis, toMillis); - const bins = createStatusTimeBins(timeBuckets, pingStatuses); - const indexedBins = indexBinsByEndTime(bins); - - const timeDomain = { - min: bins?.[0]?.end ?? fromMillis, - max: bins?.[bins.length - 1]?.end ?? toMillis, + const { timeBins, timeBinMap, xDomain } = useMemo((): { + timeBins: MonitorStatusTimeBin[]; + timeBinMap: Map; + xDomain: { min: number; max: number }; + } => { + const timeBuckets = createTimeBuckets(minsPerBin, fromMillis, toMillis); + const bins = createStatusTimeBins(timeBuckets, dateHistogram); + return { + timeBins: bins, + timeBinMap: indexBinsByEndTime(bins), + xDomain: { + min: bins?.[0]?.end ?? fromMillis, + max: bins?.[bins.length - 1]?.end ?? toMillis, + }, }; - - return { timeBins: bins, timeBinsByEndTime: indexedBins, xDomain: timeDomain }; - }, [intervalByWidth, pingStatuses, fromMillis, toMillis]); + }, [minsPerBin, fromMillis, toMillis, dateHistogram]); return { - intervalByWidth, + loading, + minsPerBin, timeBins, + getTimeBinByXValue: (xValue: number | undefined) => + xValue === undefined ? undefined : timeBinMap.get(xValue), xDomain, handleResize, - getTimeBinByXValue: (xValue: number | undefined) => - xValue === undefined ? undefined : timeBinsByEndTime.get(xValue), }; }; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/root_effect.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/root_effect.ts index 62d671d8e98fd..955371832ae4f 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/root_effect.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/root_effect.ts @@ -38,6 +38,7 @@ import { fetchServiceLocationsEffect } from './service_locations'; import { browserJourneyEffects, fetchJourneyStepsEffect } from './browser_journey'; import { fetchPingStatusesEffect } from './ping_status'; import { fetchOverviewStatusEffect } from './overview_status'; +import { fetchMonitorStatusHeatmap } from './status_heatmap'; export const rootEffect = function* root(): Generator { yield all([ @@ -71,5 +72,6 @@ export const rootEffect = function* root(): Generator { fork(getCertsListEffect), fork(getDefaultAlertingEffect), fork(enableDefaultAlertingSilentlyEffect), + fork(fetchMonitorStatusHeatmap), ]); }; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/root_reducer.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/root_reducer.ts index a17749de498ff..000e7b2225dec 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/root_reducer.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/root_reducer.ts @@ -31,6 +31,7 @@ import { serviceLocationsReducer, ServiceLocationsState } from './service_locati import { monitorOverviewReducer, MonitorOverviewState } from './overview'; import { BrowserJourneyState } from './browser_journey/models'; import { pingStatusReducer, PingStatusState } from './ping_status'; +import { monitorStatusHeatmapReducer, MonitorStatusHeatmap } from './status_heatmap'; export interface SyntheticsAppState { ui: UiState; @@ -52,6 +53,7 @@ export interface SyntheticsAppState { serviceLocations: ServiceLocationsState; overviewStatus: OverviewStatusStateReducer; syntheticsEnablement: SyntheticsEnablementState; + monitorStatusHeatmap: MonitorStatusHeatmap; } export const rootReducer = combineReducers({ @@ -74,4 +76,5 @@ export const rootReducer = combineReducers({ syntheticsEnablement: syntheticsEnablementReducer, certificates: certificatesReducer, certsList: certsListReducer, + monitorStatusHeatmap: monitorStatusHeatmapReducer, }); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/actions.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/actions.ts new file mode 100644 index 0000000000000..ef4f62b263cab --- /dev/null +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/actions.ts @@ -0,0 +1,16 @@ +/* + * 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 { MonitorStatusHeatmapResponse } from '../../../../../common/runtime_types'; +import { createAsyncAction } from '../utils/actions'; + +import { MonitorStatusHeatmapActionArgs } from './models'; + +export const getMonitorStatusHeatmapAction = createAsyncAction< + MonitorStatusHeatmapActionArgs, + MonitorStatusHeatmapResponse[] +>('MONITOR STATUS HEATMAP'); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/api.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/api.ts new file mode 100644 index 0000000000000..9e79a98aaabb9 --- /dev/null +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/api.ts @@ -0,0 +1,31 @@ +/* + * 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 { SYNTHETICS_API_URLS } from '../../../../../common/constants'; +import { MonitorStatusHeatmapResponse } from '../../../../../common/runtime_types'; +import { apiService } from '../../../../utils/api_service'; + +export const fetchMonitorStatusHeatmap = async ({ + monitorId, + location, + from, + to, + interval, +}: { + monitorId: string; + location: string; + from: string | number; + to: string | number; + interval: number; +}): Promise => + apiService.get(SYNTHETICS_API_URLS.MONITOR_STATUS_HEATMAP, { + monitorId, + location, + from, + to, + interval, + }); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/effects.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/effects.ts new file mode 100644 index 0000000000000..08ad6c819c206 --- /dev/null +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/effects.ts @@ -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 { takeLatest } from 'redux-saga/effects'; +import { fetchEffectFactory } from '../utils/fetch_effect'; +import { fetchMonitorStatusHeatmap as api } from './api'; + +import { getMonitorStatusHeatmapAction } from './actions'; + +export function* fetchMonitorStatusHeatmap() { + yield takeLatest( + getMonitorStatusHeatmapAction.get, + fetchEffectFactory( + api, + getMonitorStatusHeatmapAction.success, + getMonitorStatusHeatmapAction.fail + ) as ReturnType + ); +} diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/index.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/index.ts new file mode 100644 index 0000000000000..c80b57a0ebd54 --- /dev/null +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/index.ts @@ -0,0 +1,46 @@ +/* + * 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 { createReducer } from '@reduxjs/toolkit'; + +import { MonitorStatusHeatmapResponse } from '../../../../../common/runtime_types'; + +import { IHttpSerializedFetchError } from '../utils/http_error'; + +import { getMonitorStatusHeatmapAction } from './actions'; + +export interface MonitorStatusHeatmap { + heatmap: MonitorStatusHeatmapResponse[]; + loading: boolean; + error: IHttpSerializedFetchError | null; +} + +const initialState: MonitorStatusHeatmap = { + heatmap: [], + loading: false, + error: null, +}; + +export const monitorStatusHeatmapReducer = createReducer(initialState, (builder) => { + builder + .addCase(getMonitorStatusHeatmapAction.get, (state) => { + state.loading = true; + state.heatmap = []; + }) + .addCase(getMonitorStatusHeatmapAction.success, (state, action) => { + state.heatmap = action.payload; + state.loading = false; + }) + .addCase(getMonitorStatusHeatmapAction.fail, (state, action) => { + state.error = action.payload; + state.loading = false; + }); +}); + +export * from './actions'; +export * from './effects'; +export * from './selectors'; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/models.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/models.ts new file mode 100644 index 0000000000000..fbf5812306511 --- /dev/null +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/models.ts @@ -0,0 +1,14 @@ +/* + * 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. + */ + +export interface MonitorStatusHeatmapActionArgs { + from: string | number; + to: string | number; + interval: number; + monitorId: string; + location: string; +} diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/selectors.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/selectors.ts new file mode 100644 index 0000000000000..dc28e8473d12d --- /dev/null +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/selectors.ts @@ -0,0 +1,14 @@ +/* + * 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 { createSelector } from 'reselect'; + +import { SyntheticsAppState } from '../root_reducer'; + +const getState = (appState: SyntheticsAppState) => appState.monitorStatusHeatmap; + +export const selectHeatmap = createSelector(getState, (state) => state); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts index abfc08919b33a..8bac6e38535b4 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts @@ -164,6 +164,11 @@ export const mockState: SyntheticsAppState = { certs: [], }, }, + monitorStatusHeatmap: { + heatmap: [], + loading: false, + error: null, + }, }; function getBrowserJourneyMockSlice() { diff --git a/x-pack/plugins/observability_solution/synthetics/server/common/pings/monitor_status_heatmap.ts b/x-pack/plugins/observability_solution/synthetics/server/common/pings/monitor_status_heatmap.ts new file mode 100644 index 0000000000000..7fc742c12d95a --- /dev/null +++ b/x-pack/plugins/observability_solution/synthetics/server/common/pings/monitor_status_heatmap.ts @@ -0,0 +1,90 @@ +/* + * 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 { UptimeEsClient } from '../../lib'; + +export async function queryMonitorHeatmap({ + uptimeEsClient, + from, + to, + monitorId, + location, + interval, +}: { + uptimeEsClient: UptimeEsClient; + from: number | string; + to: number | string; + monitorId: string; + location: string; + interval: number; +}) { + return uptimeEsClient.search({ + body: { + size: 0, + query: { + bool: { + filter: [ + { + exists: { + field: 'summary', + }, + }, + { + range: { + '@timestamp': { + // @ts-expect-error strings work + gte: from, + // @ts-expect-error strings work + lte: to, + }, + }, + }, + { + term: { + 'monitor.id': monitorId, + }, + }, + ], + }, + }, + sort: [ + { + '@timestamp': { + order: 'desc', + }, + }, + ], + post_filter: { + terms: { + 'observer.geo.name': [location], + }, + }, + _source: false, + fields: ['@timestamp', 'config_id', 'summary.*', 'error.*', 'observer.geo.name'], + aggs: { + heatmap: { + date_histogram: { + field: '@timestamp', + fixed_interval: `${interval}m`, + }, + aggs: { + up: { + sum: { + field: 'summary.up', + }, + }, + down: { + sum: { + field: 'summary.down', + }, + }, + }, + }, + }, + }, + }); +} diff --git a/x-pack/plugins/observability_solution/synthetics/server/routes/index.ts b/x-pack/plugins/observability_solution/synthetics/server/routes/index.ts index c97abe44a6c5a..59478d95c8e97 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/routes/index.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/routes/index.ts @@ -46,7 +46,11 @@ import { installIndexTemplatesRoute } from './synthetics_service/install_index_t import { editSyntheticsMonitorRoute } from './monitor_cruds/edit_monitor'; import { addSyntheticsMonitorRoute } from './monitor_cruds/add_monitor'; import { addSyntheticsProjectMonitorRoute } from './monitor_cruds/add_monitor_project'; -import { syntheticsGetPingsRoute, syntheticsGetPingStatusesRoute } from './pings'; +import { + syntheticsGetPingsRoute, + syntheticsGetPingStatusesRoute, + syntheticsGetPingHeatmapRoute, +} from './pings'; import { createGetCurrentStatusRoute } from './overview_status/overview_status'; import { getHasIntegrationMonitorsRoute } from './fleet/get_has_integration_monitors'; import { enableDefaultAlertingRoute } from './default_alerts/enable_default_alert'; @@ -101,6 +105,7 @@ export const syntheticsAppRestApiRoutes: SyntheticsRestApiRouteFactory[] = [ getConnectorTypesRoute, createGetDynamicSettingsRoute, createPostDynamicSettingsRoute, + syntheticsGetPingHeatmapRoute, ]; export const syntheticsAppPublicRestApiRoutes: SyntheticsRestApiRouteFactory[] = [ diff --git a/x-pack/plugins/observability_solution/synthetics/server/routes/pings/index.ts b/x-pack/plugins/observability_solution/synthetics/server/routes/pings/index.ts index 89fa3194d4dc2..8402fa99ef642 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/routes/pings/index.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/routes/pings/index.ts @@ -7,3 +7,4 @@ export { syntheticsGetPingsRoute } from './get_pings'; export { syntheticsGetPingStatusesRoute } from './get_ping_statuses'; +export { syntheticsGetPingHeatmapRoute } from './ping_heatmap'; diff --git a/x-pack/plugins/observability_solution/synthetics/server/routes/pings/ping_heatmap.ts b/x-pack/plugins/observability_solution/synthetics/server/routes/pings/ping_heatmap.ts new file mode 100644 index 0000000000000..2281fecb4412a --- /dev/null +++ b/x-pack/plugins/observability_solution/synthetics/server/routes/pings/ping_heatmap.ts @@ -0,0 +1,42 @@ +/* + * 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 { schema } from '@kbn/config-schema'; +import { SYNTHETICS_API_URLS } from '../../../common/constants'; +import { queryMonitorHeatmap } from '../../common/pings/monitor_status_heatmap'; +import { SyntheticsRestApiRouteFactory } from '../types'; + +export const syntheticsGetPingHeatmapRoute: SyntheticsRestApiRouteFactory = () => ({ + method: 'GET', + path: SYNTHETICS_API_URLS.MONITOR_STATUS_HEATMAP, + validate: { + query: schema.object({ + from: schema.maybe(schema.oneOf([schema.number(), schema.string()])), + to: schema.maybe(schema.oneOf([schema.number(), schema.string()])), + interval: schema.number(), + monitorId: schema.string(), + location: schema.string(), + }), + }, + handler: async ({ uptimeEsClient, request, response }): Promise => { + const { from, to, interval, monitorId, location } = request.query; + + const result = await queryMonitorHeatmap({ + uptimeEsClient, + from, + to, + monitorId, + location, + interval, + }); + + return response.ok({ + // @ts-expect-error heatmap exists in the query results + body: result.body.aggregations?.heatmap?.buckets, + }); + }, +}); From d897d05018f7d36dee126c7167366cd9a48c06f9 Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Thu, 30 May 2024 12:28:56 -0400 Subject: [PATCH 03/15] Rename response type. --- .../synthetics/common/runtime_types/ping/ping.ts | 4 ++-- .../monitor_details/monitor_status/monitor_status_data.ts | 4 ++-- .../public/apps/synthetics/state/status_heatmap/actions.ts | 4 ++-- .../public/apps/synthetics/state/status_heatmap/api.ts | 4 ++-- .../public/apps/synthetics/state/status_heatmap/index.ts | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/observability_solution/synthetics/common/runtime_types/ping/ping.ts b/x-pack/plugins/observability_solution/synthetics/common/runtime_types/ping/ping.ts index 5d5beca95aaae..5acc72cae351c 100644 --- a/x-pack/plugins/observability_solution/synthetics/common/runtime_types/ping/ping.ts +++ b/x-pack/plugins/observability_solution/synthetics/common/runtime_types/ping/ping.ts @@ -307,7 +307,7 @@ export const GetPingsParamsType = t.intersection([ export type GetPingsParams = t.TypeOf; -export const MonitorStatusHeatmapResponseType = t.type({ +export const MonitorStatusHeatmapType = t.type({ doc_count: t.number, down: t.type({ value: t.number, @@ -319,4 +319,4 @@ export const MonitorStatusHeatmapResponseType = t.type({ key_as_string: t.string, }); -export type MonitorStatusHeatmapResponse = t.TypeOf; +export type MonitorStatusHeatmap = t.TypeOf; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_data.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_data.ts index ecec63ee74ad4..951a222faf6d6 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_data.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_data.ts @@ -16,7 +16,7 @@ import { COLOR_MODES_STANDARD, } from '@elastic/eui'; import type { BrushEvent } from '@elastic/charts'; -import { MonitorStatusHeatmapResponse } from '../../../../../../common/runtime_types'; +import { MonitorStatusHeatmap } from '../../../../../../common/runtime_types'; export const SUCCESS_VIZ_COLOR = VISUALIZATION_COLORS[0]; export const DANGER_VIZ_COLOR = VISUALIZATION_COLORS[VISUALIZATION_COLORS.length - 1]; @@ -114,7 +114,7 @@ export function createTimeBuckets(intervalMinutes: number, from: number, to: num export function createStatusTimeBins( timeBuckets: MonitorStatusTimeBucket[], - pingStatuses: MonitorStatusHeatmapResponse[] + pingStatuses: MonitorStatusHeatmap[] ): MonitorStatusTimeBin[] { return timeBuckets.map(({ start, end }) => { const { up: ups, down: downs } = pingStatuses diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/actions.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/actions.ts index ef4f62b263cab..1df380bdf7841 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/actions.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/actions.ts @@ -5,12 +5,12 @@ * 2.0. */ -import { MonitorStatusHeatmapResponse } from '../../../../../common/runtime_types'; +import { MonitorStatusHeatmap } from '../../../../../common/runtime_types'; import { createAsyncAction } from '../utils/actions'; import { MonitorStatusHeatmapActionArgs } from './models'; export const getMonitorStatusHeatmapAction = createAsyncAction< MonitorStatusHeatmapActionArgs, - MonitorStatusHeatmapResponse[] + MonitorStatusHeatmap[] >('MONITOR STATUS HEATMAP'); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/api.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/api.ts index 9e79a98aaabb9..94ef9beae08f3 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/api.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/api.ts @@ -6,7 +6,7 @@ */ import { SYNTHETICS_API_URLS } from '../../../../../common/constants'; -import { MonitorStatusHeatmapResponse } from '../../../../../common/runtime_types'; +import { MonitorStatusHeatmap } from '../../../../../common/runtime_types'; import { apiService } from '../../../../utils/api_service'; export const fetchMonitorStatusHeatmap = async ({ @@ -21,7 +21,7 @@ export const fetchMonitorStatusHeatmap = async ({ from: string | number; to: string | number; interval: number; -}): Promise => +}): Promise => apiService.get(SYNTHETICS_API_URLS.MONITOR_STATUS_HEATMAP, { monitorId, location, diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/index.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/index.ts index c80b57a0ebd54..04cfceb36dadb 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/index.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/index.ts @@ -7,14 +7,14 @@ import { createReducer } from '@reduxjs/toolkit'; -import { MonitorStatusHeatmapResponse } from '../../../../../common/runtime_types'; +import { MonitorStatusHeatmap as HeatmapType } from '../../../../../common/runtime_types'; import { IHttpSerializedFetchError } from '../utils/http_error'; import { getMonitorStatusHeatmapAction } from './actions'; export interface MonitorStatusHeatmap { - heatmap: MonitorStatusHeatmapResponse[]; + heatmap: HeatmapType[]; loading: boolean; error: IHttpSerializedFetchError | null; } From 02f5d080be30040cfa5bd3ac3207102ca108e76f Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Thu, 30 May 2024 12:57:22 -0400 Subject: [PATCH 04/15] Revert changes to query pings endpoint. --- .../server/common/pings/query_pings.ts | 65 +++---------------- 1 file changed, 8 insertions(+), 57 deletions(-) diff --git a/x-pack/plugins/observability_solution/synthetics/server/common/pings/query_pings.ts b/x-pack/plugins/observability_solution/synthetics/server/common/pings/query_pings.ts index 4c36fb4af2eab..7b60fc8fcf536 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/common/pings/query_pings.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/common/pings/query_pings.ts @@ -10,7 +10,6 @@ import { QueryDslFieldAndFormat, QueryDslQueryContainer, } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { SortResults } from '@elastic/elasticsearch/lib/api/types'; import { SUMMARY_FILTER } from '../../../common/constants/client_defaults'; import { UptimeEsClient } from '../../lib'; import { @@ -37,20 +36,6 @@ type GetParamsWithFields = GetPingsParams & { type GetParamsWithoutFields = GetPingsParams; -interface QueryPingsSearchBody { - size: number; - from: number; - query: { - bool: { - filter: QueryDslQueryContainer[]; - }; - }; - sort: Array<{ '@timestamp': { order: 'asc' | 'desc' } }>; - _source: boolean; - fields: QueryFields; - search_after?: SortResults; -} - export async function queryPings( params: (GetParamsWithFields | GetParamsWithoutFields) & { uptimeEsClient: UptimeEsClient } ): Promise { @@ -68,7 +53,7 @@ export async function queryPings( } = params; const size = sizeParam ?? DEFAULT_PAGE_SIZE; - const searchBody: QueryPingsSearchBody = { + const searchBody = { size, from: pageIndex !== undefined ? pageIndex * size : 0, ...(index ? { from: index * size } : {}), @@ -111,36 +96,15 @@ export async function queryPings( searchBody._source = false; searchBody.fields = params.fields; - let latestTotal = 0; - let hitCount; - let afterKey; - const mapPromises: Array> = []; - - /** - * By default Elasticsearch only returns 10k documents in a single search. - * Users may have many thousands of documents in a given time range. - * This procedure paginates the results and offloads excess fields in parallel. - */ - do { - if (afterKey) { - searchBody.search_after = afterKey; - } - const { - body: { - hits: { hits, total }, - }, - } = await uptimeEsClient.search({ body: searchBody }); - mapPromises.push(extractFieldsFromDocs(hits, params.fieldsExtractorFn)); - hitCount = hits.length; - latestTotal = total.value; - if (hitCount > 0) { - afterKey = hits[hitCount - 1].sort; - } - } while (hitCount === searchBody.size && searchBody.size === 10_000); + const { + body: { + hits: { hits, total }, + }, + } = await uptimeEsClient.search({ body: searchBody }); return { - total: latestTotal, - pings: (await Promise.all(mapPromises)).flat(), + total: total.value, + pings: hits.map((doc: any) => params.fieldsExtractorFn(doc)), }; } @@ -169,19 +133,6 @@ export async function queryPings( }; } -async function extractFieldsFromDocs( - hits: Doc[], - extractor: (doc: Doc) => Fn -): Promise { - return new Promise((resolve, reject) => { - try { - resolve(hits.map(extractor)); - } catch (e: any) { - reject(e); - } - }); -} - function isGetParamsWithFields( params: GetParamsWithFields | GetParamsWithoutFields ): params is GetParamsWithFields { From 6d4c1c9249e965d75e5f8ed0601824f45e50c256 Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Thu, 30 May 2024 12:59:34 -0400 Subject: [PATCH 05/15] Simplify. --- .../monitor_details/monitor_status/monitor_status_data.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_data.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_data.ts index 951a222faf6d6..12c7e4fe85f28 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_data.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_data.ts @@ -117,14 +117,14 @@ export function createStatusTimeBins( pingStatuses: MonitorStatusHeatmap[] ): MonitorStatusTimeBin[] { return timeBuckets.map(({ start, end }) => { - const { up: ups, down: downs } = pingStatuses + const { ups, downs } = pingStatuses .filter(({ key }) => key >= start && key <= end) .reduce( (acc, cur) => ({ - up: acc.up + cur.up.value, - down: acc.down + cur.down.value, + ups: acc.ups + cur.up.value, + downs: acc.downs + cur.down.value, }), - { up: 0, down: 0 } + { ups: 0, downs: 0 } ); return { start, From 232c61de75608f8e64d966343efd8eb718d63a29 Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Mon, 3 Jun 2024 09:37:59 -0400 Subject: [PATCH 06/15] Rename type. --- .../synthetics/common/runtime_types/ping/ping.ts | 4 ++-- .../monitor_details/monitor_status/monitor_status_data.ts | 7 ++++--- .../public/apps/synthetics/state/status_heatmap/actions.ts | 4 ++-- .../public/apps/synthetics/state/status_heatmap/api.ts | 4 ++-- .../public/apps/synthetics/state/status_heatmap/index.ts | 4 ++-- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/observability_solution/synthetics/common/runtime_types/ping/ping.ts b/x-pack/plugins/observability_solution/synthetics/common/runtime_types/ping/ping.ts index 5acc72cae351c..5f45b2f613c79 100644 --- a/x-pack/plugins/observability_solution/synthetics/common/runtime_types/ping/ping.ts +++ b/x-pack/plugins/observability_solution/synthetics/common/runtime_types/ping/ping.ts @@ -307,7 +307,7 @@ export const GetPingsParamsType = t.intersection([ export type GetPingsParams = t.TypeOf; -export const MonitorStatusHeatmapType = t.type({ +export const MonitorStatusHeatmapBucketType = t.type({ doc_count: t.number, down: t.type({ value: t.number, @@ -319,4 +319,4 @@ export const MonitorStatusHeatmapType = t.type({ key_as_string: t.string, }); -export type MonitorStatusHeatmap = t.TypeOf; +export type MonitorStatusHeatmapBucket = t.TypeOf; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_data.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_data.ts index 12c7e4fe85f28..e5ee43aa04f8d 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_data.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_data.ts @@ -16,7 +16,7 @@ import { COLOR_MODES_STANDARD, } from '@elastic/eui'; import type { BrushEvent } from '@elastic/charts'; -import { MonitorStatusHeatmap } from '../../../../../../common/runtime_types'; +import { MonitorStatusHeatmapBucket } from '../../../../../../common/runtime_types'; export const SUCCESS_VIZ_COLOR = VISUALIZATION_COLORS[0]; export const DANGER_VIZ_COLOR = VISUALIZATION_COLORS[VISUALIZATION_COLORS.length - 1]; @@ -114,10 +114,10 @@ export function createTimeBuckets(intervalMinutes: number, from: number, to: num export function createStatusTimeBins( timeBuckets: MonitorStatusTimeBucket[], - pingStatuses: MonitorStatusHeatmap[] + heatmapData: MonitorStatusHeatmapBucket[] ): MonitorStatusTimeBin[] { return timeBuckets.map(({ start, end }) => { - const { ups, downs } = pingStatuses + const { ups, downs } = heatmapData .filter(({ key }) => key >= start && key <= end) .reduce( (acc, cur) => ({ @@ -126,6 +126,7 @@ export function createStatusTimeBins( }), { ups: 0, downs: 0 } ); + return { start, end, diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/actions.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/actions.ts index 1df380bdf7841..d3f1819caf66d 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/actions.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/actions.ts @@ -5,12 +5,12 @@ * 2.0. */ -import { MonitorStatusHeatmap } from '../../../../../common/runtime_types'; +import { MonitorStatusHeatmapBucket } from '../../../../../common/runtime_types'; import { createAsyncAction } from '../utils/actions'; import { MonitorStatusHeatmapActionArgs } from './models'; export const getMonitorStatusHeatmapAction = createAsyncAction< MonitorStatusHeatmapActionArgs, - MonitorStatusHeatmap[] + MonitorStatusHeatmapBucket[] >('MONITOR STATUS HEATMAP'); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/api.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/api.ts index 94ef9beae08f3..60067dadbd1b1 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/api.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/api.ts @@ -6,7 +6,7 @@ */ import { SYNTHETICS_API_URLS } from '../../../../../common/constants'; -import { MonitorStatusHeatmap } from '../../../../../common/runtime_types'; +import { MonitorStatusHeatmapBucket } from '../../../../../common/runtime_types'; import { apiService } from '../../../../utils/api_service'; export const fetchMonitorStatusHeatmap = async ({ @@ -21,7 +21,7 @@ export const fetchMonitorStatusHeatmap = async ({ from: string | number; to: string | number; interval: number; -}): Promise => +}): Promise => apiService.get(SYNTHETICS_API_URLS.MONITOR_STATUS_HEATMAP, { monitorId, location, diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/index.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/index.ts index 04cfceb36dadb..e1ccc158d5886 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/index.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/index.ts @@ -7,14 +7,14 @@ import { createReducer } from '@reduxjs/toolkit'; -import { MonitorStatusHeatmap as HeatmapType } from '../../../../../common/runtime_types'; +import { MonitorStatusHeatmapBucket } from '../../../../../common/runtime_types'; import { IHttpSerializedFetchError } from '../utils/http_error'; import { getMonitorStatusHeatmapAction } from './actions'; export interface MonitorStatusHeatmap { - heatmap: HeatmapType[]; + heatmap: MonitorStatusHeatmapBucket[]; loading: boolean; error: IHttpSerializedFetchError | null; } From af444ef9146374ff3e5cfa45a45cbed1a513124e Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Mon, 3 Jun 2024 11:45:31 -0400 Subject: [PATCH 07/15] Don't render time bins until there is a proper initial size. --- .../monitor_status/monitor_status_panel.tsx | 116 +++++++++--------- .../monitor_status/use_monitor_status_data.ts | 81 ++++++++---- .../apps/synthetics/state/root_effect.ts | 3 +- .../state/status_heatmap/actions.ts | 8 ++ .../state/status_heatmap/effects.ts | 13 +- .../synthetics/state/status_heatmap/index.ts | 16 ++- 6 files changed, 156 insertions(+), 81 deletions(-) diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_panel.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_panel.tsx index 0d0285650f95b..d19b1cb2d66dd 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_panel.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_panel.tsx @@ -5,9 +5,9 @@ * 2.0. */ -import React, { useMemo } from 'react'; +import React, { useMemo, useRef } from 'react'; -import { EuiPanel, useEuiTheme, EuiResizeObserver, EuiSpacer } from '@elastic/eui'; +import { EuiPanel, useEuiTheme, EuiResizeObserver, EuiSpacer, EuiProgress } from '@elastic/eui'; import { Chart, Settings, Heatmap, ScaleType, Tooltip, LEGACY_LIGHT_THEME } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; import { MonitorStatusHeader } from './monitor_status_header'; @@ -31,8 +31,9 @@ export const MonitorStatusPanel = ({ onBrushed, }: MonitorStatusPanelProps) => { const { euiTheme, colorMode } = useEuiTheme(); + const initialSizeRef = useRef(null); const { loading, timeBins, handleResize, getTimeBinByXValue, xDomain, minsPerBin } = - useMonitorStatusData({ from, to }); + useMonitorStatusData({ from, to, initSize: initialSizeRef.current?.clientWidth }); const heatmap = useMemo(() => { return getMonitorStatusChartTheme(euiTheme, brushable); @@ -51,61 +52,66 @@ export const MonitorStatusPanel = ({ - - {(resizeRef) => ( -
- - ( - + handleResize(e)}> + {(resizeRef) => ( +
+ {minsPerBin && ( + + ( + + )} /> - )} - /> - { - onBrushed?.(getBrushData(brushArea)); - }} - locale={i18n.getLocale()} - /> - end} - yAccessor={() => 'T'} - valueAccessor={(timeBin) => timeBin.value} - valueFormatter={(d) => d.toFixed(2)} - xAxisLabelFormatter={getXAxisLabelFormatter(minsPerBin)} - timeZone="UTC" - xScale={{ - type: ScaleType.Time, - interval: { - type: 'calendar', - unit: 'm', - value: minsPerBin, - }, - }} - /> - -
- )} -
+ { + onBrushed?.(getBrushData(brushArea)); + }} + locale={i18n.getLocale()} + /> + end} + yAccessor={() => 'T'} + valueAccessor={(timeBin) => timeBin.value} + valueFormatter={(d) => d.toFixed(2)} + xAxisLabelFormatter={getXAxisLabelFormatter(minsPerBin)} + timeZone="UTC" + xScale={{ + type: ScaleType.Time, + interval: { + type: 'calendar', + unit: 'm', + value: minsPerBin, + }, + }} + /> +
+ )} +
+ )} +
+ + {loading && } ); }; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/use_monitor_status_data.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/use_monitor_status_data.ts index 74758d58f2fd9..c27fb7d299061 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/use_monitor_status_data.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/use_monitor_status_data.ts @@ -6,10 +6,10 @@ */ import { useCallback, useEffect, useMemo, useState } from 'react'; -import { throttle } from 'lodash'; import { useSelector, useDispatch } from 'react-redux'; +import { useDebounce } from 'react-use'; +import { useLocation } from 'react-router-dom'; -import { scheduleToMinutes } from '../../../../../../common/lib/schedule_to_time'; import { useSyntheticsRefreshContext } from '../../../contexts/synthetics_refresh_context'; import { useSelectedMonitor } from '../hooks/use_selected_monitor'; @@ -23,36 +23,43 @@ import { MonitorStatusTimeBin, } from './monitor_status_data'; import { useSelectedLocation } from '../hooks/use_selected_location'; -import { getMonitorStatusHeatmapAction, selectHeatmap } from '../../../state/status_heatmap'; +import { + clearMonitorStatusHeatmapAction, + quietGetMonitorStatusHeatmapAction, + selectHeatmap, +} from '../../../state/status_heatmap'; + +type Props = Pick & { initSize?: number }; -export const useMonitorStatusData = ({ - from, - to, -}: Pick) => { +export const useMonitorStatusData = ({ from, to, initSize }: Props) => { const { lastRefresh } = useSyntheticsRefreshContext(); const { monitor } = useSelectedMonitor(); const location = useSelectedLocation(); - const monitorInterval = Math.max(3, monitor?.schedule ? scheduleToMinutes(monitor?.schedule) : 3); + const pageLocation = useLocation(); const fromMillis = dateToMilli(from); const toMillis = dateToMilli(to); const totalMinutes = Math.ceil(toMillis - fromMillis) / (1000 * 60); const [binsAvailableByWidth, setBinsAvailableByWidth] = useState(null); - const minsPerBin = Math.floor( - Math.max( - monitorInterval, - binsAvailableByWidth !== null ? totalMinutes / binsAvailableByWidth : 0 - ) - ); + const [debouncedBinsCount, setDebouncedCount] = useState(null); + + const minsPerBin = + debouncedBinsCount !== null ? Math.floor(totalMinutes / debouncedBinsCount) : null; const dispatch = useDispatch(); const { heatmap: dateHistogram, loading } = useSelector(selectHeatmap); useEffect(() => { - if (monitor?.id && location?.label) { + if (binsAvailableByWidth === null && initSize) { + setBinsAvailableByWidth(Math.floor(initSize / CHART_CELL_WIDTH)); + } + }, [binsAvailableByWidth, initSize]); + + useEffect(() => { + if (monitor?.id && location?.label && debouncedBinsCount !== null && minsPerBin !== null) { dispatch( - getMonitorStatusHeatmapAction.get({ + quietGetMonitorStatusHeatmapAction.get({ monitorId: monitor.id, location: location.label, from, @@ -61,23 +68,51 @@ export const useMonitorStatusData = ({ }) ); } - }, [dispatch, from, to, minsPerBin, location?.label, monitor?.id, lastRefresh]); + }, [ + dispatch, + from, + to, + minsPerBin, + location?.label, + monitor?.id, + lastRefresh, + debouncedBinsCount, + ]); + + useEffect(() => { + dispatch(clearMonitorStatusHeatmapAction()); + }, [dispatch, pageLocation.pathname]); - // Disabling deps warning as we wanna throttle the callback - // eslint-disable-next-line react-hooks/exhaustive-deps const handleResize = useCallback( - throttle((e: { width: number; height: number }) => { - setBinsAvailableByWidth(Math.floor(e.width / CHART_CELL_WIDTH)); - }, 500), + (e: { width: number; height: number }) => + setBinsAvailableByWidth(Math.floor(e.width / CHART_CELL_WIDTH)), [] ); + useDebounce( + async () => { + setDebouncedCount(binsAvailableByWidth); + }, + 500, + [binsAvailableByWidth] + ); + const { timeBins, timeBinMap, xDomain } = useMemo((): { timeBins: MonitorStatusTimeBin[]; timeBinMap: Map; xDomain: { min: number; max: number }; } => { - const timeBuckets = createTimeBuckets(minsPerBin, fromMillis, toMillis); + if (minsPerBin === null) { + return { + timeBins: [], + timeBinMap: new Map(), + xDomain: { + min: fromMillis, + max: toMillis, + }, + }; + } + const timeBuckets = createTimeBuckets(minsPerBin ?? 50, fromMillis, toMillis); const bins = createStatusTimeBins(timeBuckets, dateHistogram); return { timeBins: bins, diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/root_effect.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/root_effect.ts index 955371832ae4f..00d9beac14cf9 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/root_effect.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/root_effect.ts @@ -38,7 +38,7 @@ import { fetchServiceLocationsEffect } from './service_locations'; import { browserJourneyEffects, fetchJourneyStepsEffect } from './browser_journey'; import { fetchPingStatusesEffect } from './ping_status'; import { fetchOverviewStatusEffect } from './overview_status'; -import { fetchMonitorStatusHeatmap } from './status_heatmap'; +import { fetchMonitorStatusHeatmap, quietFetchMonitorStatusHeatmap } from './status_heatmap'; export const rootEffect = function* root(): Generator { yield all([ @@ -73,5 +73,6 @@ export const rootEffect = function* root(): Generator { fork(getDefaultAlertingEffect), fork(enableDefaultAlertingSilentlyEffect), fork(fetchMonitorStatusHeatmap), + fork(quietFetchMonitorStatusHeatmap), ]); }; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/actions.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/actions.ts index d3f1819caf66d..f56fe727e7cbb 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/actions.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/actions.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { createAction } from '@reduxjs/toolkit'; import { MonitorStatusHeatmapBucket } from '../../../../../common/runtime_types'; import { createAsyncAction } from '../utils/actions'; @@ -14,3 +15,10 @@ export const getMonitorStatusHeatmapAction = createAsyncAction< MonitorStatusHeatmapActionArgs, MonitorStatusHeatmapBucket[] >('MONITOR STATUS HEATMAP'); + +export const clearMonitorStatusHeatmapAction = createAction('CLEAR MONITOR STATUS HEATMAP'); + +export const quietGetMonitorStatusHeatmapAction = createAsyncAction< + MonitorStatusHeatmapActionArgs, + MonitorStatusHeatmapBucket[] +>('QUIET GET MONITOR STATUS HEATMAP'); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/effects.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/effects.ts index 08ad6c819c206..beffc42367e9a 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/effects.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/effects.ts @@ -9,7 +9,7 @@ import { takeLatest } from 'redux-saga/effects'; import { fetchEffectFactory } from '../utils/fetch_effect'; import { fetchMonitorStatusHeatmap as api } from './api'; -import { getMonitorStatusHeatmapAction } from './actions'; +import { getMonitorStatusHeatmapAction, quietGetMonitorStatusHeatmapAction } from './actions'; export function* fetchMonitorStatusHeatmap() { yield takeLatest( @@ -21,3 +21,14 @@ export function* fetchMonitorStatusHeatmap() { ) as ReturnType ); } + +export function* quietFetchMonitorStatusHeatmap() { + yield takeLatest( + quietGetMonitorStatusHeatmapAction.get, + fetchEffectFactory( + api, + getMonitorStatusHeatmapAction.success, + getMonitorStatusHeatmapAction.fail + ) as ReturnType + ); +} diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/index.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/index.ts index e1ccc158d5886..29f8a1ba87345 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/index.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/index.ts @@ -11,7 +11,11 @@ import { MonitorStatusHeatmapBucket } from '../../../../../common/runtime_types' import { IHttpSerializedFetchError } from '../utils/http_error'; -import { getMonitorStatusHeatmapAction } from './actions'; +import { + clearMonitorStatusHeatmapAction, + getMonitorStatusHeatmapAction, + quietGetMonitorStatusHeatmapAction, +} from './actions'; export interface MonitorStatusHeatmap { heatmap: MonitorStatusHeatmapBucket[]; @@ -27,6 +31,13 @@ const initialState: MonitorStatusHeatmap = { export const monitorStatusHeatmapReducer = createReducer(initialState, (builder) => { builder + .addCase(quietGetMonitorStatusHeatmapAction.success, (state, action) => { + state.heatmap = action.payload; + state.loading = false; + }) + .addCase(quietGetMonitorStatusHeatmapAction.get, (state) => { + state.loading = true; + }) .addCase(getMonitorStatusHeatmapAction.get, (state) => { state.loading = true; state.heatmap = []; @@ -38,6 +49,9 @@ export const monitorStatusHeatmapReducer = createReducer(initialState, (builder) .addCase(getMonitorStatusHeatmapAction.fail, (state, action) => { state.error = action.payload; state.loading = false; + }) + .addCase(clearMonitorStatusHeatmapAction, (state) => { + state.heatmap = []; }); }); From 6c9ee1f633dca9d2e99ba7cba428127d95148e20 Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Mon, 1 Jul 2024 11:40:38 -0400 Subject: [PATCH 08/15] Pass entire ref to monitor status data hook. --- .../monitor_status/monitor_status_panel.tsx | 2 +- .../monitor_status/use_monitor_status_data.ts | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_panel.tsx b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_panel.tsx index d19b1cb2d66dd..cb8da0ea599d6 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_panel.tsx +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/monitor_status_panel.tsx @@ -33,7 +33,7 @@ export const MonitorStatusPanel = ({ const { euiTheme, colorMode } = useEuiTheme(); const initialSizeRef = useRef(null); const { loading, timeBins, handleResize, getTimeBinByXValue, xDomain, minsPerBin } = - useMonitorStatusData({ from, to, initSize: initialSizeRef.current?.clientWidth }); + useMonitorStatusData({ from, to, initialSizeRef }); const heatmap = useMemo(() => { return getMonitorStatusChartTheme(euiTheme, brushable); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/use_monitor_status_data.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/use_monitor_status_data.ts index c27fb7d299061..6502745904867 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/use_monitor_status_data.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/use_monitor_status_data.ts @@ -29,9 +29,11 @@ import { selectHeatmap, } from '../../../state/status_heatmap'; -type Props = Pick & { initSize?: number }; +type Props = Pick & { + initialSizeRef?: React.MutableRefObject; +}; -export const useMonitorStatusData = ({ from, to, initSize }: Props) => { +export const useMonitorStatusData = ({ from, to, initialSizeRef }: Props) => { const { lastRefresh } = useSyntheticsRefreshContext(); const { monitor } = useSelectedMonitor(); const location = useSelectedLocation(); @@ -51,10 +53,10 @@ export const useMonitorStatusData = ({ from, to, initSize }: Props) => { const { heatmap: dateHistogram, loading } = useSelector(selectHeatmap); useEffect(() => { - if (binsAvailableByWidth === null && initSize) { - setBinsAvailableByWidth(Math.floor(initSize / CHART_CELL_WIDTH)); + if (binsAvailableByWidth === null && initialSizeRef?.current) { + setBinsAvailableByWidth(Math.floor(initialSizeRef?.current?.clientWidth / CHART_CELL_WIDTH)); } - }, [binsAvailableByWidth, initSize]); + }, [binsAvailableByWidth, initialSizeRef]); useEffect(() => { if (monitor?.id && location?.label && debouncedBinsCount !== null && minsPerBin !== null) { From 1d94688a2787fe004a82cec86470cda5e1bf9160 Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Fri, 30 Aug 2024 16:04:11 -0400 Subject: [PATCH 09/15] Update stale code. --- .../server/common/pings/monitor_status_heatmap.ts | 10 ++++------ .../synthetics/server/routes/pings/ping_heatmap.ts | 5 ++--- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/observability_solution/synthetics/server/common/pings/monitor_status_heatmap.ts b/x-pack/plugins/observability_solution/synthetics/server/common/pings/monitor_status_heatmap.ts index 7fc742c12d95a..ea262113cd729 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/common/pings/monitor_status_heatmap.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/common/pings/monitor_status_heatmap.ts @@ -5,24 +5,24 @@ * 2.0. */ -import { UptimeEsClient } from '../../lib'; +import { SyntheticsEsClient } from '../../lib'; export async function queryMonitorHeatmap({ - uptimeEsClient, + syntheticsEsClient, from, to, monitorId, location, interval, }: { - uptimeEsClient: UptimeEsClient; + syntheticsEsClient: SyntheticsEsClient; from: number | string; to: number | string; monitorId: string; location: string; interval: number; }) { - return uptimeEsClient.search({ + return syntheticsEsClient.search({ body: { size: 0, query: { @@ -36,9 +36,7 @@ export async function queryMonitorHeatmap({ { range: { '@timestamp': { - // @ts-expect-error strings work gte: from, - // @ts-expect-error strings work lte: to, }, }, diff --git a/x-pack/plugins/observability_solution/synthetics/server/routes/pings/ping_heatmap.ts b/x-pack/plugins/observability_solution/synthetics/server/routes/pings/ping_heatmap.ts index 2281fecb4412a..7d82d92b1e0e3 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/routes/pings/ping_heatmap.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/routes/pings/ping_heatmap.ts @@ -22,11 +22,11 @@ export const syntheticsGetPingHeatmapRoute: SyntheticsRestApiRouteFactory = () = location: schema.string(), }), }, - handler: async ({ uptimeEsClient, request, response }): Promise => { + handler: async ({ syntheticsEsClient, request, response }): Promise => { const { from, to, interval, monitorId, location } = request.query; const result = await queryMonitorHeatmap({ - uptimeEsClient, + syntheticsEsClient, from, to, monitorId, @@ -35,7 +35,6 @@ export const syntheticsGetPingHeatmapRoute: SyntheticsRestApiRouteFactory = () = }); return response.ok({ - // @ts-expect-error heatmap exists in the query results body: result.body.aggregations?.heatmap?.buckets, }); }, From a088319808f4c3a494f5f421f1d97a676507f57b Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Fri, 30 Aug 2024 16:06:14 -0400 Subject: [PATCH 10/15] PR feedback. --- .../monitor_details/monitor_status/use_monitor_status_data.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/use_monitor_status_data.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/use_monitor_status_data.ts index 6502745904867..59d807cb3bef8 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/use_monitor_status_data.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/components/monitor_details/monitor_status/use_monitor_status_data.ts @@ -121,7 +121,7 @@ export const useMonitorStatusData = ({ from, to, initialSizeRef }: Props) => { timeBinMap: indexBinsByEndTime(bins), xDomain: { min: bins?.[0]?.end ?? fromMillis, - max: bins?.[bins.length - 1]?.end ?? toMillis, + max: bins?.at(-1)?.end ?? toMillis, }, }; }, [minsPerBin, fromMillis, toMillis, dateHistogram]); From 344095423d4a01112899bbb91c56b1c9c6bfab69 Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Wed, 4 Sep 2024 11:38:47 -0400 Subject: [PATCH 11/15] No `any` plz. --- .../synthetics/server/routes/pings/ping_heatmap.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/observability_solution/synthetics/server/routes/pings/ping_heatmap.ts b/x-pack/plugins/observability_solution/synthetics/server/routes/pings/ping_heatmap.ts index 7d82d92b1e0e3..0ed7b7d83c365 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/routes/pings/ping_heatmap.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/routes/pings/ping_heatmap.ts @@ -22,7 +22,7 @@ export const syntheticsGetPingHeatmapRoute: SyntheticsRestApiRouteFactory = () = location: schema.string(), }), }, - handler: async ({ syntheticsEsClient, request, response }): Promise => { + handler: async ({ syntheticsEsClient, request, response }) => { const { from, to, interval, monitorId, location } = request.query; const result = await queryMonitorHeatmap({ From 899f4cbb70027749d8e8b30a3f340d19e7626eac Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Wed, 4 Sep 2024 12:03:55 -0400 Subject: [PATCH 12/15] Rename a var. --- .../server/common/pings/monitor_status_heatmap.ts | 6 +++--- .../synthetics/server/routes/pings/ping_heatmap.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/observability_solution/synthetics/server/common/pings/monitor_status_heatmap.ts b/x-pack/plugins/observability_solution/synthetics/server/common/pings/monitor_status_heatmap.ts index ea262113cd729..2ccc3c86b67b4 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/common/pings/monitor_status_heatmap.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/common/pings/monitor_status_heatmap.ts @@ -13,14 +13,14 @@ export async function queryMonitorHeatmap({ to, monitorId, location, - interval, + intervalInMinutes, }: { syntheticsEsClient: SyntheticsEsClient; from: number | string; to: number | string; monitorId: string; location: string; - interval: number; + intervalInMinutes: number; }) { return syntheticsEsClient.search({ body: { @@ -67,7 +67,7 @@ export async function queryMonitorHeatmap({ heatmap: { date_histogram: { field: '@timestamp', - fixed_interval: `${interval}m`, + fixed_interval: `${intervalInMinutes}m`, }, aggs: { up: { diff --git a/x-pack/plugins/observability_solution/synthetics/server/routes/pings/ping_heatmap.ts b/x-pack/plugins/observability_solution/synthetics/server/routes/pings/ping_heatmap.ts index 0ed7b7d83c365..1c7773796a3bd 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/routes/pings/ping_heatmap.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/routes/pings/ping_heatmap.ts @@ -23,7 +23,7 @@ export const syntheticsGetPingHeatmapRoute: SyntheticsRestApiRouteFactory = () = }), }, handler: async ({ syntheticsEsClient, request, response }) => { - const { from, to, interval, monitorId, location } = request.query; + const { from, to, interval: intervalInMinutes, monitorId, location } = request.query; const result = await queryMonitorHeatmap({ syntheticsEsClient, @@ -31,7 +31,7 @@ export const syntheticsGetPingHeatmapRoute: SyntheticsRestApiRouteFactory = () = to, monitorId, location, - interval, + intervalInMinutes, }); return response.ok({ From 062db3a626f0523acd05a68bb27df07e7cdb7910 Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Thu, 5 Sep 2024 12:10:09 -0400 Subject: [PATCH 13/15] Do not rely on `post_filter` for this query. --- .../server/common/pings/monitor_status_heatmap.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/observability_solution/synthetics/server/common/pings/monitor_status_heatmap.ts b/x-pack/plugins/observability_solution/synthetics/server/common/pings/monitor_status_heatmap.ts index 2ccc3c86b67b4..cc6015463e9a5 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/common/pings/monitor_status_heatmap.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/common/pings/monitor_status_heatmap.ts @@ -46,6 +46,11 @@ export async function queryMonitorHeatmap({ 'monitor.id': monitorId, }, }, + { + term: { + 'observer.geo.name': location, + }, + }, ], }, }, @@ -56,11 +61,6 @@ export async function queryMonitorHeatmap({ }, }, ], - post_filter: { - terms: { - 'observer.geo.name': [location], - }, - }, _source: false, fields: ['@timestamp', 'config_id', 'summary.*', 'error.*', 'observer.geo.name'], aggs: { From d6fc63d7c71223e99f9dd4c3b4d4831974ea6de8 Mon Sep 17 00:00:00 2001 From: shahzad31 Date: Fri, 6 Sep 2024 11:11:57 +0200 Subject: [PATCH 14/15] remove unused code --- .../common/constants/synthetics/rest_api.ts | 1 - .../common/runtime_types/ping/ping.ts | 27 ------ .../public/apps/synthetics/state/index.ts | 1 - .../synthetics/state/ping_status/actions.ts | 16 ---- .../apps/synthetics/state/ping_status/api.ts | 36 -------- .../synthetics/state/ping_status/effects.ts | 23 ------ .../synthetics/state/ping_status/index.ts | 64 --------------- .../synthetics/state/ping_status/models.ts | 14 ---- .../synthetics/state/ping_status/selectors.ts | 29 ------- .../apps/synthetics/state/root_effect.ts | 2 - .../apps/synthetics/state/root_reducer.ts | 3 - .../__mocks__/synthetics_store.mock.ts | 31 ------- .../synthetics/server/routes/index.ts | 7 +- .../server/routes/pings/get_ping_statuses.ts | 82 ------------------- .../synthetics/server/routes/pings/index.ts | 1 - 15 files changed, 1 insertion(+), 336 deletions(-) delete mode 100644 x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/ping_status/actions.ts delete mode 100644 x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/ping_status/api.ts delete mode 100644 x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/ping_status/effects.ts delete mode 100644 x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/ping_status/index.ts delete mode 100644 x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/ping_status/models.ts delete mode 100644 x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/ping_status/selectors.ts delete mode 100644 x-pack/plugins/observability_solution/synthetics/server/routes/pings/get_ping_statuses.ts diff --git a/x-pack/plugins/observability_solution/synthetics/common/constants/synthetics/rest_api.ts b/x-pack/plugins/observability_solution/synthetics/common/constants/synthetics/rest_api.ts index 0d68c1d9de31c..ac3e4154b6683 100644 --- a/x-pack/plugins/observability_solution/synthetics/common/constants/synthetics/rest_api.ts +++ b/x-pack/plugins/observability_solution/synthetics/common/constants/synthetics/rest_api.ts @@ -25,7 +25,6 @@ export enum SYNTHETICS_API_URLS { SYNTHETICS_OVERVIEW = '/internal/synthetics/overview', PINGS = '/internal/synthetics/pings', - PING_STATUSES = '/internal/synthetics/ping_statuses', MONITOR_STATUS_HEATMAP = '/internal/synthetics/ping_heatmap', OVERVIEW_TRENDS = '/internal/synthetics/overview_trends', OVERVIEW_STATUS = `/internal/synthetics/overview_status`, diff --git a/x-pack/plugins/observability_solution/synthetics/common/runtime_types/ping/ping.ts b/x-pack/plugins/observability_solution/synthetics/common/runtime_types/ping/ping.ts index e97088fd793ae..cf06fb899c948 100644 --- a/x-pack/plugins/observability_solution/synthetics/common/runtime_types/ping/ping.ts +++ b/x-pack/plugins/observability_solution/synthetics/common/runtime_types/ping/ping.ts @@ -255,24 +255,6 @@ export const PingStateType = t.type({ export type Ping = t.TypeOf; export type PingState = t.TypeOf; -export const PingStatusType = t.intersection([ - t.type({ - timestamp: t.string, - docId: t.string, - config_id: t.string, - locationId: t.string, - summary: t.partial({ - down: t.number, - up: t.number, - }), - }), - t.partial({ - error: PingErrorType, - }), -]); - -export type PingStatus = t.TypeOf; - export const PingsResponseType = t.type({ total: t.number, pings: t.array(PingType), @@ -280,15 +262,6 @@ export const PingsResponseType = t.type({ export type PingsResponse = t.TypeOf; -export const PingStatusesResponseType = t.type({ - total: t.number, - pings: t.array(PingStatusType), - from: t.string, - to: t.string, -}); - -export type PingStatusesResponse = t.TypeOf; - export const GetPingsParamsType = t.intersection([ t.type({ dateRange: DateRangeType, diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/index.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/index.ts index ef319d13740b2..079a68d4444a8 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/index.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/index.ts @@ -17,7 +17,6 @@ export * from './monitor_list'; export * from './monitor_details'; export * from './overview'; export * from './browser_journey'; -export * from './ping_status'; export * from './private_locations'; export type { UpsertMonitorResponse } from './monitor_management/api'; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/ping_status/actions.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/ping_status/actions.ts deleted file mode 100644 index f268a5a2c5600..0000000000000 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/ping_status/actions.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * 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 { PingStatusesResponse } from '../../../../../common/runtime_types'; -import { createAsyncAction } from '../utils/actions'; - -import { PingStatusActionArgs } from './models'; - -export const getMonitorPingStatusesAction = createAsyncAction< - PingStatusActionArgs, - PingStatusesResponse ->('[PING STATUSES] GET PING STATUSES'); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/ping_status/api.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/ping_status/api.ts deleted file mode 100644 index 38930dfb02cb8..0000000000000 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/ping_status/api.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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 { SYNTHETICS_API_URLS } from '../../../../../common/constants'; -import { - PingStatusesResponse, - PingStatusesResponseType, -} from '../../../../../common/runtime_types'; -import { apiService } from '../../../../utils/api_service'; - -export const fetchMonitorPingStatuses = async ({ - monitorId, - locationId, - from, - to, - size, -}: { - monitorId: string; - locationId: string; - from: string; - to: string; - size: number; -}): Promise => { - const locations = JSON.stringify([locationId]); - const sort = 'desc'; - - return await apiService.get( - SYNTHETICS_API_URLS.PING_STATUSES, - { monitorId, from, to, locations, sort, size }, - PingStatusesResponseType - ); -}; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/ping_status/effects.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/ping_status/effects.ts deleted file mode 100644 index cffae2fb859f5..0000000000000 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/ping_status/effects.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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 { takeEvery } from 'redux-saga/effects'; -import { fetchEffectFactory } from '../utils/fetch_effect'; -import { fetchMonitorPingStatuses } from './api'; - -import { getMonitorPingStatusesAction } from './actions'; - -export function* fetchPingStatusesEffect() { - yield takeEvery( - getMonitorPingStatusesAction.get, - fetchEffectFactory( - fetchMonitorPingStatuses, - getMonitorPingStatusesAction.success, - getMonitorPingStatusesAction.fail - ) as ReturnType - ); -} diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/ping_status/index.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/ping_status/index.ts deleted file mode 100644 index 350db7cb41177..0000000000000 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/ping_status/index.ts +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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 { createReducer } from '@reduxjs/toolkit'; - -import { PingStatus } from '../../../../../common/runtime_types'; - -import { IHttpSerializedFetchError } from '../utils/http_error'; - -import { getMonitorPingStatusesAction } from './actions'; - -export interface PingStatusState { - pingStatuses: { - [monitorId: string]: { - [locationId: string]: { - [timestamp: string]: PingStatus; - }; - }; - }; - loading: boolean; - error: IHttpSerializedFetchError | null; -} - -const initialState: PingStatusState = { - pingStatuses: {}, - loading: false, - error: null, -}; - -export const pingStatusReducer = createReducer(initialState, (builder) => { - builder - .addCase(getMonitorPingStatusesAction.get, (state) => { - state.loading = true; - }) - .addCase(getMonitorPingStatusesAction.success, (state, action) => { - (action.payload.pings ?? []).forEach((ping) => { - // eslint-disable-next-line @typescript-eslint/naming-convention - const { config_id, locationId, timestamp } = ping; - if (!state.pingStatuses[config_id]) { - state.pingStatuses[config_id] = {}; - } - - if (!state.pingStatuses[config_id][locationId]) { - state.pingStatuses[config_id][locationId] = {}; - } - - state.pingStatuses[config_id][locationId][timestamp] = ping; - }); - - state.loading = false; - }) - .addCase(getMonitorPingStatusesAction.fail, (state, action) => { - state.error = action.payload; - state.loading = false; - }); -}); - -export * from './actions'; -export * from './effects'; -export * from './selectors'; diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/ping_status/models.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/ping_status/models.ts deleted file mode 100644 index bae8ae9acb3fa..0000000000000 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/ping_status/models.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * 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. - */ - -export interface PingStatusActionArgs { - monitorId: string; - locationId: string; - from: string | number; - to: string | number; - size: number; -} diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/ping_status/selectors.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/ping_status/selectors.ts deleted file mode 100644 index cf3061e0fab33..0000000000000 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/ping_status/selectors.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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 { createSelector } from 'reselect'; - -import { PingStatus } from '../../../../../common/runtime_types'; -import { SyntheticsAppState } from '../root_reducer'; - -import { PingStatusState } from '.'; - -type PingSelectorReturnType = (state: SyntheticsAppState) => PingStatus[]; - -const getState = (appState: SyntheticsAppState) => appState.pingStatus; - -export const selectIsMonitorStatusesLoading = createSelector(getState, (state) => state.loading); - -export const selectPingStatusesForMonitorAndLocationAsc = ( - monitorId: string, - locationId: string -): PingSelectorReturnType => - createSelector([(state: SyntheticsAppState) => state.pingStatus], (state: PingStatusState) => { - return Object.values(state?.pingStatuses?.[monitorId]?.[locationId] ?? {}).sort( - (a, b) => Number(new Date(a.timestamp)) - Number(new Date(b.timestamp)) - ); - }); diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/root_effect.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/root_effect.ts index e2524a2c3bacd..80a3144aef511 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/root_effect.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/root_effect.ts @@ -40,7 +40,6 @@ import { } from './overview'; import { fetchServiceLocationsEffect } from './service_locations'; import { browserJourneyEffects, fetchJourneyStepsEffect } from './browser_journey'; -import { fetchPingStatusesEffect } from './ping_status'; import { fetchOverviewStatusEffect } from './overview_status'; import { fetchMonitorStatusHeatmap, quietFetchMonitorStatusHeatmap } from './status_heatmap'; @@ -56,7 +55,6 @@ export const rootEffect = function* root(): Generator { fork(browserJourneyEffects), fork(fetchOverviewStatusEffect), fork(fetchNetworkEventsEffect), - fork(fetchPingStatusesEffect), fork(fetchAgentPoliciesEffect), fork(fetchPrivateLocationsEffect), fork(fetchDynamicSettingsEffect), diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/root_reducer.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/root_reducer.ts index 000e7b2225dec..f8ace41e93191 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/root_reducer.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/root_reducer.ts @@ -30,13 +30,11 @@ import { monitorListReducer, MonitorListState } from './monitor_list'; import { serviceLocationsReducer, ServiceLocationsState } from './service_locations'; import { monitorOverviewReducer, MonitorOverviewState } from './overview'; import { BrowserJourneyState } from './browser_journey/models'; -import { pingStatusReducer, PingStatusState } from './ping_status'; import { monitorStatusHeatmapReducer, MonitorStatusHeatmap } from './status_heatmap'; export interface SyntheticsAppState { ui: UiState; settings: SettingsState; - pingStatus: PingStatusState; elasticsearch: QueriesState; monitorList: MonitorListState; overview: MonitorOverviewState; @@ -59,7 +57,6 @@ export interface SyntheticsAppState { export const rootReducer = combineReducers({ ui: uiReducer, settings: settingsReducer, - pingStatus: pingStatusReducer, monitorList: monitorListReducer, overview: monitorOverviewReducer, globalParams: globalParamsReducer, diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts index 615ac873cf719..b861fe36b9b96 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/utils/testing/__mocks__/synthetics_store.mock.ts @@ -119,7 +119,6 @@ export const mockState: SyntheticsAppState = { monitorDetails: getMonitorDetailsMockSlice(), browserJourney: getBrowserJourneyMockSlice(), networkEvents: {}, - pingStatus: getPingStatusesMockSlice(), agentPolicies: { loading: false, error: null, @@ -496,33 +495,3 @@ function getMonitorDetailsMockSlice() { selectedLocationId: 'us_central', } as MonitorDetailsState; } - -function getPingStatusesMockSlice() { - const monitorDetails = getMonitorDetailsMockSlice(); - - return { - pingStatuses: monitorDetails.pings.data.reduce((acc, cur) => { - const geoName = cur.observer.geo?.name!; - if (!acc[cur.monitor.id]) { - acc[cur.monitor.id] = {}; - } - - if (!acc[cur.monitor.id][geoName]) { - acc[cur.monitor.id][geoName] = {}; - } - - acc[cur.monitor.id][geoName][cur.timestamp] = { - timestamp: cur.timestamp, - error: undefined, - locationId: geoName, - config_id: cur.config_id!, - docId: cur.docId, - summary: cur.summary!, - }; - - return acc; - }, {} as SyntheticsAppState['pingStatus']['pingStatuses']), - loading: false, - error: null, - } as SyntheticsAppState['pingStatus']; -} diff --git a/x-pack/plugins/observability_solution/synthetics/server/routes/index.ts b/x-pack/plugins/observability_solution/synthetics/server/routes/index.ts index f3711da19e120..8e62964c2d833 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/routes/index.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/routes/index.ts @@ -46,11 +46,7 @@ import { installIndexTemplatesRoute } from './synthetics_service/install_index_t import { editSyntheticsMonitorRoute } from './monitor_cruds/edit_monitor'; import { addSyntheticsMonitorRoute } from './monitor_cruds/add_monitor'; import { addSyntheticsProjectMonitorRoute } from './monitor_cruds/add_monitor_project'; -import { - syntheticsGetPingsRoute, - syntheticsGetPingStatusesRoute, - syntheticsGetPingHeatmapRoute, -} from './pings'; +import { syntheticsGetPingsRoute, syntheticsGetPingHeatmapRoute } from './pings'; import { createGetCurrentStatusRoute } from './overview_status/overview_status'; import { getHasIntegrationMonitorsRoute } from './fleet/get_has_integration_monitors'; import { enableDefaultAlertingRoute } from './default_alerts/enable_default_alert'; @@ -81,7 +77,6 @@ export const syntheticsAppRestApiRoutes: SyntheticsRestApiRouteFactory[] = [ getServiceAllowedRoute, getAPIKeySyntheticsRoute, syntheticsGetPingsRoute, - syntheticsGetPingStatusesRoute, getHasIntegrationMonitorsRoute, createGetCurrentStatusRoute, getIndexSizesRoute, diff --git a/x-pack/plugins/observability_solution/synthetics/server/routes/pings/get_ping_statuses.ts b/x-pack/plugins/observability_solution/synthetics/server/routes/pings/get_ping_statuses.ts deleted file mode 100644 index ffb10e5a931ef..0000000000000 --- a/x-pack/plugins/observability_solution/synthetics/server/routes/pings/get_ping_statuses.ts +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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 { SyntheticsRestApiRouteFactory } from '../types'; -import { SYNTHETICS_API_URLS } from '../../../common/constants'; -import { PingError, PingStatus } from '../../../common/runtime_types'; -import { queryPings } from '../../common/pings/query_pings'; - -import { getPingsRouteQuerySchema } from './get_pings'; - -export const syntheticsGetPingStatusesRoute: SyntheticsRestApiRouteFactory = () => ({ - method: 'GET', - path: SYNTHETICS_API_URLS.PING_STATUSES, - validate: { - query: getPingsRouteQuerySchema, - }, - handler: async ({ syntheticsEsClient, request, response }): Promise => { - const { - from, - to, - index, - monitorId, - status, - sort, - size, - pageIndex, - locations, - excludedLocations, - } = request.query; - - const result = await queryPings({ - syntheticsEsClient, - dateRange: { from, to }, - index, - monitorId, - status, - sort, - size, - pageIndex, - locations: locations ? JSON.parse(locations) : [], - excludedLocations, - fields: ['@timestamp', 'config_id', 'summary.*', 'error.*', 'observer.geo.name'], - fieldsExtractorFn: extractPingStatus, - }); - - return { - ...result, - from, - to, - }; - }, -}); - -function grabPingError(doc: any): PingError | undefined { - const docContainsError = Object.keys(doc?.fields ?? {}).some((key) => key.startsWith('error.')); - if (!docContainsError) { - return undefined; - } - - return { - code: doc.fields['error.code']?.[0], - id: doc.fields['error.id']?.[0], - stack_trace: doc.fields['error.stack_trace']?.[0], - type: doc.fields['error.type']?.[0], - message: doc.fields['error.message']?.[0], - }; -} - -function extractPingStatus(doc: any) { - return { - timestamp: doc.fields['@timestamp']?.[0], - docId: doc._id, - config_id: doc.fields.config_id?.[0], - locationId: doc.fields['observer.geo.name']?.[0], - summary: { up: doc.fields['summary.up']?.[0], down: doc.fields['summary.down']?.[0] }, - error: grabPingError(doc), - } as PingStatus; -} diff --git a/x-pack/plugins/observability_solution/synthetics/server/routes/pings/index.ts b/x-pack/plugins/observability_solution/synthetics/server/routes/pings/index.ts index 8402fa99ef642..b18a1dcc3a7b8 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/routes/pings/index.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/routes/pings/index.ts @@ -6,5 +6,4 @@ */ export { syntheticsGetPingsRoute } from './get_pings'; -export { syntheticsGetPingStatusesRoute } from './get_ping_statuses'; export { syntheticsGetPingHeatmapRoute } from './ping_heatmap'; From eb1f8a36456de66cd338f9f9b244a09150a9435e Mon Sep 17 00:00:00 2001 From: shahzad31 Date: Fri, 6 Sep 2024 11:37:42 +0200 Subject: [PATCH 15/15] fix types --- .../public/apps/synthetics/state/status_heatmap/api.ts | 8 ++++++-- .../synthetics/server/routes/pings/ping_heatmap.ts | 10 ++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/api.ts b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/api.ts index 60067dadbd1b1..482e7d3c15939 100644 --- a/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/api.ts +++ b/x-pack/plugins/observability_solution/synthetics/public/apps/synthetics/state/status_heatmap/api.ts @@ -21,11 +21,15 @@ export const fetchMonitorStatusHeatmap = async ({ from: string | number; to: string | number; interval: number; -}): Promise => - apiService.get(SYNTHETICS_API_URLS.MONITOR_STATUS_HEATMAP, { +}): Promise => { + const response = await apiService.get<{ + result: MonitorStatusHeatmapBucket[]; + }>(SYNTHETICS_API_URLS.MONITOR_STATUS_HEATMAP, { monitorId, location, from, to, interval, }); + return response.result; +}; diff --git a/x-pack/plugins/observability_solution/synthetics/server/routes/pings/ping_heatmap.ts b/x-pack/plugins/observability_solution/synthetics/server/routes/pings/ping_heatmap.ts index 1c7773796a3bd..a7eb44967c81c 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/routes/pings/ping_heatmap.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/routes/pings/ping_heatmap.ts @@ -6,6 +6,7 @@ */ import { schema } from '@kbn/config-schema'; +import { MonitorStatusHeatmapBucket } from '../../../common/runtime_types'; import { SYNTHETICS_API_URLS } from '../../../common/constants'; import { queryMonitorHeatmap } from '../../common/pings/monitor_status_heatmap'; import { SyntheticsRestApiRouteFactory } from '../types'; @@ -22,7 +23,10 @@ export const syntheticsGetPingHeatmapRoute: SyntheticsRestApiRouteFactory = () = location: schema.string(), }), }, - handler: async ({ syntheticsEsClient, request, response }) => { + handler: async ({ + syntheticsEsClient, + request, + }): Promise => { const { from, to, interval: intervalInMinutes, monitorId, location } = request.query; const result = await queryMonitorHeatmap({ @@ -34,8 +38,6 @@ export const syntheticsGetPingHeatmapRoute: SyntheticsRestApiRouteFactory = () = intervalInMinutes, }); - return response.ok({ - body: result.body.aggregations?.heatmap?.buckets, - }); + return result.body.aggregations?.heatmap?.buckets as MonitorStatusHeatmapBucket[]; }, });