From 2a4d39aae4dd2c30cc23e4ccf94f3a7073b8336d Mon Sep 17 00:00:00 2001 From: Dario Gieselaar Date: Tue, 2 Feb 2021 14:44:43 +0100 Subject: [PATCH] [APM] Abort browser requests when appropriate (#89557) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../anomaly_detection_setup_link.tsx | 4 +- .../error_count_alert_trigger/index.tsx | 30 +++--- .../index.tsx | 50 +++++----- .../index.tsx | 44 +++++---- .../app/Correlations/ErrorCorrelations.tsx | 58 +++++------ .../app/Correlations/LatencyCorrelations.tsx | 62 ++++++------ .../app/ErrorGroupDetails/index.tsx | 38 ++++---- .../app/ErrorGroupOverview/index.tsx | 42 ++++---- .../app/RumDashboard/ux_overview_fetchers.ts | 2 + .../components/app/ServiceMap/index.tsx | 46 ++++----- .../SettingsPage/saveConfig.ts | 1 + .../List/ConfirmDeleteModal.tsx | 1 + .../app/Settings/ApmIndices/index.tsx | 1 + .../DeleteButton.tsx | 1 + .../LinkPreview.tsx | 1 + .../saveCustomLink.ts | 2 + .../Settings/anomaly_detection/create_jobs.ts | 1 + .../use_anomaly_detection_jobs_fetcher.ts | 4 +- .../index.tsx | 40 ++++---- .../service_overview_errors_table/index.tsx | 86 +++++++++-------- ...ice_overview_instances_chart_and_table.tsx | 44 +++++---- .../service_overview_throughput_chart.tsx | 38 ++++---- .../index.tsx | 96 +++++++++---------- .../transaction_error_rate_chart/index.tsx | 42 ++++---- .../annotations/annotations_context.tsx | 36 +++---- .../apm/public/hooks/useLocalUIFilters.ts | 54 ++++++----- .../public/hooks/use_environments_fetcher.tsx | 30 +++--- .../plugins/apm/public/hooks/use_fetcher.tsx | 35 +++++-- .../apm_observability_overview_fetchers.ts | 2 + .../apm/public/services/rest/callApi.ts | 3 +- .../public/services/rest/createCallApmApi.ts | 5 +- .../apm/public/services/rest/index_pattern.ts | 2 + .../create_apm_event_client/index.test.ts | 8 +- .../server/lib/helpers/setup_request.test.ts | 2 +- .../apm/server/routes/create_api/index.ts | 10 ++ x-pack/plugins/apm/server/routes/typings.ts | 15 ++- 36 files changed, 512 insertions(+), 424 deletions(-) diff --git a/x-pack/plugins/apm/public/application/action_menu/anomaly_detection_setup_link.tsx b/x-pack/plugins/apm/public/application/action_menu/anomaly_detection_setup_link.tsx index 8a1d73c818944..a221f4bfb05a9 100644 --- a/x-pack/plugins/apm/public/application/action_menu/anomaly_detection_setup_link.tsx +++ b/x-pack/plugins/apm/public/application/action_menu/anomaly_detection_setup_link.tsx @@ -57,7 +57,9 @@ export function AnomalyDetectionSetupLink() { export function MissingJobsAlert({ environment }: { environment?: string }) { const { data = DEFAULT_DATA, status } = useFetcher( (callApmApi) => - callApmApi({ endpoint: `GET /api/apm/settings/anomaly-detection/jobs` }), + callApmApi({ + endpoint: `GET /api/apm/settings/anomaly-detection/jobs`, + }), [], { preservePreviousData: false, showToastOnError: false } ); diff --git a/x-pack/plugins/apm/public/components/alerting/error_count_alert_trigger/index.tsx b/x-pack/plugins/apm/public/components/alerting/error_count_alert_trigger/index.tsx index d7375d14e17cf..3b68eccbd9dc4 100644 --- a/x-pack/plugins/apm/public/components/alerting/error_count_alert_trigger/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/error_count_alert_trigger/index.tsx @@ -13,7 +13,6 @@ import { asInteger } from '../../../../common/utils/formatters'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; import { useEnvironmentsFetcher } from '../../../hooks/use_environments_fetcher'; import { useFetcher } from '../../../hooks/use_fetcher'; -import { callApmApi } from '../../../services/rest/createCallApmApi'; import { ChartPreview } from '../chart_preview'; import { EnvironmentField, IsAboveField, ServiceField } from '../fields'; import { getAbsoluteTimeRange } from '../helper'; @@ -46,20 +45,23 @@ export function ErrorCountAlertTrigger(props: Props) { const { threshold, windowSize, windowUnit, environment } = alertParams; - const { data } = useFetcher(() => { - if (windowSize && windowUnit) { - return callApmApi({ - endpoint: 'GET /api/apm/alerts/chart_preview/transaction_error_count', - params: { - query: { - ...getAbsoluteTimeRange(windowSize, windowUnit), - environment, - serviceName, + const { data } = useFetcher( + (callApmApi) => { + if (windowSize && windowUnit) { + return callApmApi({ + endpoint: 'GET /api/apm/alerts/chart_preview/transaction_error_count', + params: { + query: { + ...getAbsoluteTimeRange(windowSize, windowUnit), + environment, + serviceName, + }, }, - }, - }); - } - }, [windowSize, windowUnit, environment, serviceName]); + }); + } + }, + [windowSize, windowUnit, environment, serviceName] + ); const defaults = { threshold: 25, diff --git a/x-pack/plugins/apm/public/components/alerting/transaction_duration_alert_trigger/index.tsx b/x-pack/plugins/apm/public/components/alerting/transaction_duration_alert_trigger/index.tsx index 7c0a74f2e1b60..4d28cdaec3782 100644 --- a/x-pack/plugins/apm/public/components/alerting/transaction_duration_alert_trigger/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/transaction_duration_alert_trigger/index.tsx @@ -8,7 +8,6 @@ import { i18n } from '@kbn/i18n'; import { map } from 'lodash'; import React from 'react'; import { useParams } from 'react-router-dom'; -import { useFetcher } from '../../../../../observability/public'; import { ForLastExpression } from '../../../../../triggers_actions_ui/public'; import { ENVIRONMENT_ALL } from '../../../../common/environment_filter_values'; import { getDurationFormatter } from '../../../../common/utils/formatters'; @@ -16,7 +15,7 @@ import { TimeSeries } from '../../../../typings/timeseries'; import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; import { useEnvironmentsFetcher } from '../../../hooks/use_environments_fetcher'; -import { callApmApi } from '../../../services/rest/createCallApmApi'; +import { useFetcher } from '../../../hooks/use_fetcher'; import { getMaxY, getResponseTimeTickFormatter, @@ -88,29 +87,32 @@ export function TransactionDurationAlertTrigger(props: Props) { windowUnit, } = alertParams; - const { data } = useFetcher(() => { - if (windowSize && windowUnit) { - return callApmApi({ - endpoint: 'GET /api/apm/alerts/chart_preview/transaction_duration', - params: { - query: { - ...getAbsoluteTimeRange(windowSize, windowUnit), - aggregationType, - environment, - serviceName, - transactionType: alertParams.transactionType, + const { data } = useFetcher( + (callApmApi) => { + if (windowSize && windowUnit) { + return callApmApi({ + endpoint: 'GET /api/apm/alerts/chart_preview/transaction_duration', + params: { + query: { + ...getAbsoluteTimeRange(windowSize, windowUnit), + aggregationType, + environment, + serviceName, + transactionType: alertParams.transactionType, + }, }, - }, - }); - } - }, [ - aggregationType, - environment, - serviceName, - alertParams.transactionType, - windowSize, - windowUnit, - ]); + }); + } + }, + [ + aggregationType, + environment, + serviceName, + alertParams.transactionType, + windowSize, + windowUnit, + ] + ); const maxY = getMaxY([ { data: data ?? [] } as TimeSeries<{ x: number; y: number | null }>, diff --git a/x-pack/plugins/apm/public/components/alerting/transaction_error_rate_alert_trigger/index.tsx b/x-pack/plugins/apm/public/components/alerting/transaction_error_rate_alert_trigger/index.tsx index e06f39ec10220..58adbb7b172c8 100644 --- a/x-pack/plugins/apm/public/components/alerting/transaction_error_rate_alert_trigger/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/transaction_error_rate_alert_trigger/index.tsx @@ -12,7 +12,6 @@ import { useApmServiceContext } from '../../../context/apm_service/use_apm_servi import { useUrlParams } from '../../../context/url_params_context/use_url_params'; import { useEnvironmentsFetcher } from '../../../hooks/use_environments_fetcher'; import { useFetcher } from '../../../hooks/use_fetcher'; -import { callApmApi } from '../../../services/rest/createCallApmApi'; import { ChartPreview } from '../chart_preview'; import { EnvironmentField, @@ -54,27 +53,30 @@ export function TransactionErrorRateAlertTrigger(props: Props) { const thresholdAsPercent = (threshold ?? 0) / 100; - const { data } = useFetcher(() => { - if (windowSize && windowUnit) { - return callApmApi({ - endpoint: 'GET /api/apm/alerts/chart_preview/transaction_error_rate', - params: { - query: { - ...getAbsoluteTimeRange(windowSize, windowUnit), - environment, - serviceName, - transactionType: alertParams.transactionType, + const { data } = useFetcher( + (callApmApi) => { + if (windowSize && windowUnit) { + return callApmApi({ + endpoint: 'GET /api/apm/alerts/chart_preview/transaction_error_rate', + params: { + query: { + ...getAbsoluteTimeRange(windowSize, windowUnit), + environment, + serviceName, + transactionType: alertParams.transactionType, + }, }, - }, - }); - } - }, [ - alertParams.transactionType, - environment, - serviceName, - windowSize, - windowUnit, - ]); + }); + } + }, + [ + alertParams.transactionType, + environment, + serviceName, + windowSize, + windowUnit, + ] + ); if (serviceName && !transactionTypes.length) { return null; diff --git a/x-pack/plugins/apm/public/components/app/Correlations/ErrorCorrelations.tsx b/x-pack/plugins/apm/public/components/app/Correlations/ErrorCorrelations.tsx index 25973b9bda388..891a2081804cc 100644 --- a/x-pack/plugins/apm/public/components/app/Correlations/ErrorCorrelations.tsx +++ b/x-pack/plugins/apm/public/components/app/Correlations/ErrorCorrelations.tsx @@ -25,10 +25,7 @@ import { } from '@elastic/eui'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; -import { - APIReturnType, - callApmApi, -} from '../../../services/rest/createCallApmApi'; +import { APIReturnType } from '../../../services/rest/createCallApmApi'; import { px } from '../../../style/variables'; import { SignificantTermsTable } from './SignificantTermsTable'; import { ChartContainer } from '../../shared/charts/chart_container'; @@ -65,32 +62,35 @@ export function ErrorCorrelations() { const { urlParams, uiFilters } = useUrlParams(); const { transactionName, transactionType, start, end } = urlParams; - const { data, status } = useFetcher(() => { - if (start && end) { - return callApmApi({ - endpoint: 'GET /api/apm/correlations/failed_transactions', - params: { - query: { - serviceName, - transactionName, - transactionType, - start, - end, - uiFilters: JSON.stringify(uiFilters), - fieldNames: fieldNames.map((field) => field.label).join(','), + const { data, status } = useFetcher( + (callApmApi) => { + if (start && end) { + return callApmApi({ + endpoint: 'GET /api/apm/correlations/failed_transactions', + params: { + query: { + serviceName, + transactionName, + transactionType, + start, + end, + uiFilters: JSON.stringify(uiFilters), + fieldNames: fieldNames.map((field) => field.label).join(','), + }, }, - }, - }); - } - }, [ - serviceName, - start, - end, - transactionName, - transactionType, - uiFilters, - fieldNames, - ]); + }); + } + }, + [ + serviceName, + start, + end, + transactionName, + transactionType, + uiFilters, + fieldNames, + ] + ); return ( <> diff --git a/x-pack/plugins/apm/public/components/app/Correlations/LatencyCorrelations.tsx b/x-pack/plugins/apm/public/components/app/Correlations/LatencyCorrelations.tsx index 438303110fbc4..493c6e04ffbb1 100644 --- a/x-pack/plugins/apm/public/components/app/Correlations/LatencyCorrelations.tsx +++ b/x-pack/plugins/apm/public/components/app/Correlations/LatencyCorrelations.tsx @@ -26,10 +26,7 @@ import { import { getDurationFormatter } from '../../../../common/utils/formatters'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; -import { - APIReturnType, - callApmApi, -} from '../../../services/rest/createCallApmApi'; +import { APIReturnType } from '../../../services/rest/createCallApmApi'; import { SignificantTermsTable } from './SignificantTermsTable'; import { ChartContainer } from '../../shared/charts/chart_container'; @@ -65,34 +62,37 @@ export function LatencyCorrelations() { const { urlParams, uiFilters } = useUrlParams(); const { transactionName, transactionType, start, end } = urlParams; - const { data, status } = useFetcher(() => { - if (start && end) { - return callApmApi({ - endpoint: 'GET /api/apm/correlations/slow_transactions', - params: { - query: { - serviceName, - transactionName, - transactionType, - start, - end, - uiFilters: JSON.stringify(uiFilters), - durationPercentile, - fieldNames: fieldNames.map((field) => field.label).join(','), + const { data, status } = useFetcher( + (callApmApi) => { + if (start && end) { + return callApmApi({ + endpoint: 'GET /api/apm/correlations/slow_transactions', + params: { + query: { + serviceName, + transactionName, + transactionType, + start, + end, + uiFilters: JSON.stringify(uiFilters), + durationPercentile, + fieldNames: fieldNames.map((field) => field.label).join(','), + }, }, - }, - }); - } - }, [ - serviceName, - start, - end, - transactionName, - transactionType, - uiFilters, - durationPercentile, - fieldNames, - ]); + }); + } + }, + [ + serviceName, + start, + end, + transactionName, + transactionType, + uiFilters, + durationPercentile, + fieldNames, + ] + ); return ( <> diff --git a/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx index 95ebd5d4036de..9501474e3be65 100644 --- a/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx @@ -23,7 +23,6 @@ import { useTrackPageview } from '../../../../../observability/public'; import { NOT_AVAILABLE_LABEL } from '../../../../common/i18n'; import { useFetcher } from '../../../hooks/use_fetcher'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; -import { callApmApi } from '../../../services/rest/createCallApmApi'; import { fontFamilyCode, fontSizes, px, units } from '../../../style/variables'; import { ApmHeader } from '../../shared/ApmHeader'; import { SearchBar } from '../../shared/search_bar'; @@ -70,24 +69,27 @@ export function ErrorGroupDetails({ location, match }: ErrorGroupDetailsProps) { const { urlParams, uiFilters } = useUrlParams(); const { start, end } = urlParams; - const { data: errorGroupData } = useFetcher(() => { - if (start && end) { - return callApmApi({ - endpoint: 'GET /api/apm/services/{serviceName}/errors/{groupId}', - params: { - path: { - serviceName, - groupId, + const { data: errorGroupData } = useFetcher( + (callApmApi) => { + if (start && end) { + return callApmApi({ + endpoint: 'GET /api/apm/services/{serviceName}/errors/{groupId}', + params: { + path: { + serviceName, + groupId, + }, + query: { + start, + end, + uiFilters: JSON.stringify(uiFilters), + }, }, - query: { - start, - end, - uiFilters: JSON.stringify(uiFilters), - }, - }, - }); - } - }, [serviceName, start, end, groupId, uiFilters]); + }); + } + }, + [serviceName, start, end, groupId, uiFilters] + ); const { errorDistributionData } = useErrorGroupDistributionFetcher({ serviceName, diff --git a/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/index.tsx b/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/index.tsx index 71cb8e0e01602..af8c667df6ca2 100644 --- a/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/index.tsx @@ -18,7 +18,6 @@ import { useTrackPageview } from '../../../../../observability/public'; import { Projection } from '../../../../common/projections'; import { useFetcher } from '../../../hooks/use_fetcher'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; -import { callApmApi } from '../../../services/rest/createCallApmApi'; import { LocalUIFilters } from '../../shared/LocalUIFilters'; import { SearchBar } from '../../shared/search_bar'; import { ErrorDistribution } from '../ErrorGroupDetails/Distribution'; @@ -37,27 +36,30 @@ function ErrorGroupOverview({ serviceName }: ErrorGroupOverviewProps) { groupId: undefined, }); - const { data: errorGroupListData } = useFetcher(() => { - const normalizedSortDirection = sortDirection === 'asc' ? 'asc' : 'desc'; + const { data: errorGroupListData } = useFetcher( + (callApmApi) => { + const normalizedSortDirection = sortDirection === 'asc' ? 'asc' : 'desc'; - if (start && end) { - return callApmApi({ - endpoint: 'GET /api/apm/services/{serviceName}/errors', - params: { - path: { - serviceName, + if (start && end) { + return callApmApi({ + endpoint: 'GET /api/apm/services/{serviceName}/errors', + params: { + path: { + serviceName, + }, + query: { + start, + end, + sortField, + sortDirection: normalizedSortDirection, + uiFilters: JSON.stringify(uiFilters), + }, }, - query: { - start, - end, - sortField, - sortDirection: normalizedSortDirection, - uiFilters: JSON.stringify(uiFilters), - }, - }, - }); - } - }, [serviceName, start, end, sortField, sortDirection, uiFilters]); + }); + } + }, + [serviceName, start, end, sortField, sortDirection, uiFilters] + ); useTrackPageview({ app: 'apm', diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/ux_overview_fetchers.ts b/x-pack/plugins/apm/public/components/app/RumDashboard/ux_overview_fetchers.ts index 7ce9d3f25354c..0468fbabcdb41 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/ux_overview_fetchers.ts +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/ux_overview_fetchers.ts @@ -21,6 +21,7 @@ export const fetchUxOverviewDate = async ({ }: FetchDataParams): Promise => { const data = await callApmApi({ endpoint: 'GET /api/apm/rum-client/web-core-vitals', + signal: null, params: { query: { start: new Date(absoluteTime.start).toISOString(), @@ -41,6 +42,7 @@ export async function hasRumData({ }: HasDataParams): Promise { return await callApmApi({ endpoint: 'GET /api/apm/observability_overview/has_rum_data', + signal: null, params: { query: { start: new Date(absoluteTime.start).toISOString(), diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/index.tsx index 6f8d058903183..463c9f36fb2fe 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/index.tsx @@ -17,7 +17,6 @@ import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; import { useLicenseContext } from '../../../context/license/use_license_context'; import { useTheme } from '../../../hooks/use_theme'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; -import { callApmApi } from '../../../services/rest/createCallApmApi'; import { DatePicker } from '../../shared/DatePicker'; import { LicensePrompt } from '../../shared/LicensePrompt'; import { Controls } from './Controls'; @@ -86,28 +85,31 @@ export function ServiceMap({ const license = useLicenseContext(); const { urlParams } = useUrlParams(); - const { data = { elements: [] }, status, error } = useFetcher(() => { - // When we don't have a license or a valid license, don't make the request. - if (!license || !isActivePlatinumLicense(license)) { - return; - } - - const { start, end, environment } = urlParams; - if (start && end) { - return callApmApi({ - isCachable: false, - endpoint: 'GET /api/apm/service-map', - params: { - query: { - start, - end, - environment, - serviceName, + const { data = { elements: [] }, status, error } = useFetcher( + (callApmApi) => { + // When we don't have a license or a valid license, don't make the request. + if (!license || !isActivePlatinumLicense(license)) { + return; + } + + const { start, end, environment } = urlParams; + if (start && end) { + return callApmApi({ + isCachable: false, + endpoint: 'GET /api/apm/service-map', + params: { + query: { + start, + end, + environment, + serviceName, + }, }, - }, - }); - } - }, [license, serviceName, urlParams]); + }); + } + }, + [license, serviceName, urlParams] + ); const { ref, height } = useRefDimensions(); diff --git a/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/saveConfig.ts b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/saveConfig.ts index e15a57ff7539e..a9c334f414db2 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/saveConfig.ts +++ b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/saveConfig.ts @@ -26,6 +26,7 @@ export async function saveConfig({ try { await callApmApi({ endpoint: 'PUT /api/apm/settings/agent-configuration', + signal: null, params: { query: { overwrite: isEditMode }, body: { diff --git a/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/List/ConfirmDeleteModal.tsx b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/List/ConfirmDeleteModal.tsx index 958aafa8159df..09251efe8b977 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/List/ConfirmDeleteModal.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/List/ConfirmDeleteModal.tsx @@ -73,6 +73,7 @@ async function deleteConfig( try { await callApmApi({ endpoint: 'DELETE /api/apm/settings/agent-configuration', + signal: null, params: { body: { service: { diff --git a/x-pack/plugins/apm/public/components/app/Settings/ApmIndices/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/ApmIndices/index.tsx index 8c10b96c51ce2..29fca54547e7a 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/ApmIndices/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/ApmIndices/index.tsx @@ -74,6 +74,7 @@ async function saveApmIndices({ }) { await callApmApi({ endpoint: 'POST /api/apm/settings/apm-indices/save', + signal: null, params: { body: apmIndices, }, diff --git a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/DeleteButton.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/DeleteButton.tsx index ffcb85384642a..9257d5d78b6b3 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/DeleteButton.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/DeleteButton.tsx @@ -48,6 +48,7 @@ async function deleteConfig( try { await callApmApi({ endpoint: 'DELETE /api/apm/settings/custom_links/{id}', + signal: null, params: { path: { id: customLinkId }, }, diff --git a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/LinkPreview.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/LinkPreview.tsx index 25fd8f7ad3caf..626aca6218ea3 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/LinkPreview.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/LinkPreview.tsx @@ -31,6 +31,7 @@ interface Props { const fetchTransaction = debounce( async (filters: Filter[], callback: (transaction: Transaction) => void) => { const transaction = await callApmApi({ + signal: null, endpoint: 'GET /api/apm/settings/custom_links/transaction', params: { query: convertFiltersToQuery(filters) }, }); diff --git a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/saveCustomLink.ts b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/saveCustomLink.ts index cb1eaf6bca3f0..2d172d652ad81 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/saveCustomLink.ts +++ b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateEditCustomLinkFlyout/saveCustomLink.ts @@ -35,6 +35,7 @@ export async function saveCustomLink({ if (id) { await callApmApi({ endpoint: 'PUT /api/apm/settings/custom_links/{id}', + signal: null, params: { path: { id }, body: customLink, @@ -43,6 +44,7 @@ export async function saveCustomLink({ } else { await callApmApi({ endpoint: 'POST /api/apm/settings/custom_links', + signal: null, params: { body: customLink, }, diff --git a/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/create_jobs.ts b/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/create_jobs.ts index 7106a4c48ef70..dc73bf12ff4b8 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/create_jobs.ts +++ b/x-pack/plugins/apm/public/components/app/Settings/anomaly_detection/create_jobs.ts @@ -28,6 +28,7 @@ export async function createJobs({ try { await callApmApi({ endpoint: 'POST /api/apm/settings/anomaly-detection/jobs', + signal: null, params: { body: { environments }, }, diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/use_anomaly_detection_jobs_fetcher.ts b/x-pack/plugins/apm/public/components/app/service_inventory/use_anomaly_detection_jobs_fetcher.ts index 901841ac4d593..eacb09bde70ac 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/use_anomaly_detection_jobs_fetcher.ts +++ b/x-pack/plugins/apm/public/components/app/service_inventory/use_anomaly_detection_jobs_fetcher.ts @@ -8,7 +8,9 @@ import { useFetcher } from '../../../hooks/use_fetcher'; export function useAnomalyDetectionJobsFetcher() { const { data, status } = useFetcher( (callApmApi) => - callApmApi({ endpoint: `GET /api/apm/settings/anomaly-detection/jobs` }), + callApmApi({ + endpoint: `GET /api/apm/settings/anomaly-detection/jobs`, + }), [], { showToastOnError: false } ); diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx index 1bd7310e3251d..e93b29025426d 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_dependencies_table/index.tsx @@ -26,7 +26,6 @@ import { import { ServiceDependencyItem } from '../../../../../server/lib/services/get_service_dependencies'; import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; import { FETCH_STATUS, useFetcher } from '../../../../hooks/use_fetcher'; -import { callApmApi } from '../../../../services/rest/createCallApmApi'; import { px, unit } from '../../../../style/variables'; import { AgentIcon } from '../../../shared/AgentIcon'; import { SparkPlot } from '../../../shared/charts/spark_plot'; @@ -167,26 +166,29 @@ export function ServiceOverviewDependenciesTable({ serviceName }: Props) { }, ]; - const { data = [], status } = useFetcher(() => { - if (!start || !end) { - return; - } + const { data = [], status } = useFetcher( + (callApmApi) => { + if (!start || !end) { + return; + } - return callApmApi({ - endpoint: 'GET /api/apm/services/{serviceName}/dependencies', - params: { - path: { - serviceName, + return callApmApi({ + endpoint: 'GET /api/apm/services/{serviceName}/dependencies', + params: { + path: { + serviceName, + }, + query: { + start, + end, + environment: environment || ENVIRONMENT_ALL.value, + numBuckets: 20, + }, }, - query: { - start, - end, - environment: environment || ENVIRONMENT_ALL.value, - numBuckets: 20, - }, - }, - }); - }, [start, end, serviceName, environment]); + }); + }, + [start, end, serviceName, environment] + ); // need top-level sortable fields for the managed table const items = data.map((item) => ({ diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx index d14ef648c22d3..c523728025674 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx @@ -16,7 +16,6 @@ import { asInteger } from '../../../../../common/utils/formatters'; import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context'; import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; import { FETCH_STATUS, useFetcher } from '../../../../hooks/use_fetcher'; -import { callApmApi } from '../../../../services/rest/createCallApmApi'; import { px, unit } from '../../../../style/variables'; import { SparkPlot } from '../../../shared/charts/spark_plot'; import { ErrorDetailLink } from '../../../shared/Links/apm/ErrorDetailLink'; @@ -140,50 +139,53 @@ export function ServiceOverviewErrorsTable({ serviceName }: Props) { }, }, status, - } = useFetcher(() => { - if (!start || !end || !transactionType) { - return; - } + } = useFetcher( + (callApmApi) => { + if (!start || !end || !transactionType) { + return; + } - return callApmApi({ - endpoint: 'GET /api/apm/services/{serviceName}/error_groups', - params: { - path: { serviceName }, - query: { - start, - end, - uiFilters: JSON.stringify(uiFilters), - size: PAGE_SIZE, - numBuckets: 20, - pageIndex: tableOptions.pageIndex, - sortField: tableOptions.sort.field, - sortDirection: tableOptions.sort.direction, - transactionType, - }, - }, - }).then((response) => { - return { - items: response.error_groups, - totalItemCount: response.total_error_groups, - tableOptions: { - pageIndex: tableOptions.pageIndex, - sort: { - field: tableOptions.sort.field, - direction: tableOptions.sort.direction, + return callApmApi({ + endpoint: 'GET /api/apm/services/{serviceName}/error_groups', + params: { + path: { serviceName }, + query: { + start, + end, + uiFilters: JSON.stringify(uiFilters), + size: PAGE_SIZE, + numBuckets: 20, + pageIndex: tableOptions.pageIndex, + sortField: tableOptions.sort.field, + sortDirection: tableOptions.sort.direction, + transactionType, }, }, - }; - }); - }, [ - start, - end, - serviceName, - uiFilters, - tableOptions.pageIndex, - tableOptions.sort.field, - tableOptions.sort.direction, - transactionType, - ]); + }).then((response) => { + return { + items: response.error_groups, + totalItemCount: response.total_error_groups, + tableOptions: { + pageIndex: tableOptions.pageIndex, + sort: { + field: tableOptions.sort.field, + direction: tableOptions.sort.direction, + }, + }, + }; + }); + }, + [ + start, + end, + serviceName, + uiFilters, + tableOptions.pageIndex, + tableOptions.sort.field, + tableOptions.sort.direction, + transactionType, + ] + ); const { items, diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx index f7c2891bb3e65..a0528b7220cc1 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_chart_and_table.tsx @@ -9,7 +9,6 @@ import React from 'react'; import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; import { useFetcher } from '../../../hooks/use_fetcher'; -import { callApmApi } from '../../../services/rest/createCallApmApi'; import { InstancesLatencyDistributionChart } from '../../shared/charts/instances_latency_distribution_chart'; import { ServiceOverviewInstancesTable } from './service_overview_instances_table'; @@ -29,28 +28,31 @@ export function ServiceOverviewInstancesChartAndTable({ uiFilters, } = useUrlParams(); - const { data = [], status } = useFetcher(() => { - if (!start || !end || !transactionType) { - return; - } + const { data = [], status } = useFetcher( + (callApmApi) => { + if (!start || !end || !transactionType) { + return; + } - return callApmApi({ - endpoint: - 'GET /api/apm/services/{serviceName}/service_overview_instances', - params: { - path: { - serviceName, + return callApmApi({ + endpoint: + 'GET /api/apm/services/{serviceName}/service_overview_instances', + params: { + path: { + serviceName, + }, + query: { + start, + end, + transactionType, + uiFilters: JSON.stringify(uiFilters), + numBuckets: 20, + }, }, - query: { - start, - end, - transactionType, - uiFilters: JSON.stringify(uiFilters), - numBuckets: 20, - }, - }, - }); - }, [start, end, serviceName, transactionType, uiFilters]); + }); + }, + [start, end, serviceName, transactionType, uiFilters] + ); return ( <> diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_throughput_chart.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_throughput_chart.tsx index b79e011bde488..fae00822bb966 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_throughput_chart.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_throughput_chart.tsx @@ -13,7 +13,6 @@ import { useFetcher } from '../../../hooks/use_fetcher'; import { useTheme } from '../../../hooks/use_theme'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; -import { callApmApi } from '../../../services/rest/createCallApmApi'; import { TimeseriesChart } from '../../shared/charts/timeseries_chart'; export function ServiceOverviewThroughputChart({ @@ -27,24 +26,27 @@ export function ServiceOverviewThroughputChart({ const { transactionType } = useApmServiceContext(); const { start, end } = urlParams; - const { data, status } = useFetcher(() => { - if (serviceName && transactionType && start && end) { - return callApmApi({ - endpoint: 'GET /api/apm/services/{serviceName}/throughput', - params: { - path: { - serviceName, + const { data, status } = useFetcher( + (callApmApi) => { + if (serviceName && transactionType && start && end) { + return callApmApi({ + endpoint: 'GET /api/apm/services/{serviceName}/throughput', + params: { + path: { + serviceName, + }, + query: { + start, + end, + transactionType, + uiFilters: JSON.stringify(uiFilters), + }, }, - query: { - start, - end, - transactionType, - uiFilters: JSON.stringify(uiFilters), - }, - }, - }); - } - }, [serviceName, start, end, uiFilters, transactionType]); + }); + } + }, + [serviceName, start, end, uiFilters, transactionType] + ); return ( diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_transactions_table/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_transactions_table/index.tsx index c77e80d0176de..069c4466d28a0 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_transactions_table/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_transactions_table/index.tsx @@ -23,10 +23,7 @@ import { import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context'; import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; import { FETCH_STATUS, useFetcher } from '../../../../hooks/use_fetcher'; -import { - APIReturnType, - callApmApi, -} from '../../../../services/rest/createCallApmApi'; +import { APIReturnType } from '../../../../services/rest/createCallApmApi'; import { px, unit } from '../../../../style/variables'; import { SparkPlot } from '../../../shared/charts/spark_plot'; import { ImpactBar } from '../../../shared/ImpactBar'; @@ -110,53 +107,56 @@ export function ServiceOverviewTransactionsTable(props: Props) { }, }, status, - } = useFetcher(() => { - if (!start || !end || !latencyAggregationType || !transactionType) { - return; - } + } = useFetcher( + (callApmApi) => { + if (!start || !end || !latencyAggregationType || !transactionType) { + return; + } - return callApmApi({ - endpoint: - 'GET /api/apm/services/{serviceName}/transactions/groups/overview', - params: { - path: { serviceName }, - query: { - start, - end, - uiFilters: JSON.stringify(uiFilters), - size: PAGE_SIZE, - numBuckets: 20, - pageIndex: tableOptions.pageIndex, - sortField: tableOptions.sort.field, - sortDirection: tableOptions.sort.direction, - transactionType, - latencyAggregationType: latencyAggregationType as LatencyAggregationType, - }, - }, - }).then((response) => { - return { - items: response.transactionGroups, - totalItemCount: response.totalTransactionGroups, - tableOptions: { - pageIndex: tableOptions.pageIndex, - sort: { - field: tableOptions.sort.field, - direction: tableOptions.sort.direction, + return callApmApi({ + endpoint: + 'GET /api/apm/services/{serviceName}/transactions/groups/overview', + params: { + path: { serviceName }, + query: { + start, + end, + uiFilters: JSON.stringify(uiFilters), + size: PAGE_SIZE, + numBuckets: 20, + pageIndex: tableOptions.pageIndex, + sortField: tableOptions.sort.field, + sortDirection: tableOptions.sort.direction, + transactionType, + latencyAggregationType: latencyAggregationType as LatencyAggregationType, }, }, - }; - }); - }, [ - serviceName, - start, - end, - uiFilters, - tableOptions.pageIndex, - tableOptions.sort.field, - tableOptions.sort.direction, - transactionType, - latencyAggregationType, - ]); + }).then((response) => { + return { + items: response.transactionGroups, + totalItemCount: response.totalTransactionGroups, + tableOptions: { + pageIndex: tableOptions.pageIndex, + sort: { + field: tableOptions.sort.field, + direction: tableOptions.sort.direction, + }, + }, + }; + }); + }, + [ + serviceName, + start, + end, + uiFilters, + tableOptions.pageIndex, + tableOptions.sort.field, + tableOptions.sort.direction, + transactionType, + latencyAggregationType, + ] + ); const { items, diff --git a/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx index d712fa27c75ac..a16edfee5fb3d 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/transaction_error_rate_chart/index.tsx @@ -12,7 +12,6 @@ import { asPercent } from '../../../../../common/utils/formatters'; import { useFetcher } from '../../../../hooks/use_fetcher'; import { useTheme } from '../../../../hooks/use_theme'; import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; -import { callApmApi } from '../../../../services/rest/createCallApmApi'; import { TimeseriesChart } from '../timeseries_chart'; import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context'; @@ -35,26 +34,29 @@ export function TransactionErrorRateChart({ const { transactionType } = useApmServiceContext(); const { start, end, transactionName } = urlParams; - const { data, status } = useFetcher(() => { - if (transactionType && serviceName && start && end) { - return callApmApi({ - endpoint: - 'GET /api/apm/services/{serviceName}/transactions/charts/error_rate', - params: { - path: { - serviceName, + const { data, status } = useFetcher( + (callApmApi) => { + if (transactionType && serviceName && start && end) { + return callApmApi({ + endpoint: + 'GET /api/apm/services/{serviceName}/transactions/charts/error_rate', + params: { + path: { + serviceName, + }, + query: { + start, + end, + transactionType, + transactionName, + uiFilters: JSON.stringify(uiFilters), + }, }, - query: { - start, - end, - transactionType, - transactionName, - uiFilters: JSON.stringify(uiFilters), - }, - }, - }); - } - }, [serviceName, start, end, uiFilters, transactionType, transactionName]); + }); + } + }, + [serviceName, start, end, uiFilters, transactionType, transactionName] + ); const errorRates = data?.transactionErrorRate || []; diff --git a/x-pack/plugins/apm/public/context/annotations/annotations_context.tsx b/x-pack/plugins/apm/public/context/annotations/annotations_context.tsx index 77285f976d850..9d1b6d70f9738 100644 --- a/x-pack/plugins/apm/public/context/annotations/annotations_context.tsx +++ b/x-pack/plugins/apm/public/context/annotations/annotations_context.tsx @@ -9,7 +9,6 @@ import { useParams } from 'react-router-dom'; import { Annotation } from '../../../common/annotations'; import { useFetcher } from '../../hooks/use_fetcher'; import { useUrlParams } from '../url_params_context/use_url_params'; -import { callApmApi } from '../../services/rest/createCallApmApi'; export const AnnotationsContext = createContext({ annotations: [] } as { annotations: Annotation[]; @@ -27,23 +26,26 @@ export function AnnotationsContextProvider({ const { start, end } = urlParams; const { environment } = uiFilters; - const { data = INITIAL_STATE } = useFetcher(() => { - if (start && end && serviceName) { - return callApmApi({ - endpoint: 'GET /api/apm/services/{serviceName}/annotation/search', - params: { - path: { - serviceName, + const { data = INITIAL_STATE } = useFetcher( + (callApmApi) => { + if (start && end && serviceName) { + return callApmApi({ + endpoint: 'GET /api/apm/services/{serviceName}/annotation/search', + params: { + path: { + serviceName, + }, + query: { + start, + end, + environment, + }, }, - query: { - start, - end, - environment, - }, - }, - }); - } - }, [start, end, environment, serviceName]); + }); + } + }, + [start, end, environment, serviceName] + ); return ; } diff --git a/x-pack/plugins/apm/public/hooks/useLocalUIFilters.ts b/x-pack/plugins/apm/public/hooks/useLocalUIFilters.ts index dabdf41c63f04..fbdee617864df 100644 --- a/x-pack/plugins/apm/public/hooks/useLocalUIFilters.ts +++ b/x-pack/plugins/apm/public/hooks/useLocalUIFilters.ts @@ -16,7 +16,6 @@ import { } from '../../server/lib/ui_filters/local_ui_filters/config'; import { fromQuery, toQuery } from '../components/shared/Links/url_helpers'; import { removeUndefinedProps } from '../context/url_params_context/helpers'; -import { useCallApi } from './useCallApi'; import { useFetcher } from './use_fetcher'; import { useUrlParams } from '../context/url_params_context/use_url_params'; import { LocalUIFilterName } from '../../common/ui_filter'; @@ -43,7 +42,6 @@ export function useLocalUIFilters({ }) { const history = useHistory(); const { uiFilters, urlParams } = useUrlParams(); - const callApi = useCallApi(); const values = pickKeys(uiFilters, ...filterNames); @@ -69,30 +67,34 @@ export function useLocalUIFilters({ }); }; - const { data = getInitialData(filterNames), status } = useFetcher(() => { - if (shouldFetch) { - return callApi({ - method: 'GET', - pathname: `/api/apm/ui_filters/local_filters/${projection}`, - query: { - uiFilters: JSON.stringify(uiFilters), - start: urlParams.start, - end: urlParams.end, - filterNames: JSON.stringify(filterNames), - ...params, - }, - }); - } - }, [ - callApi, - projection, - uiFilters, - urlParams.start, - urlParams.end, - filterNames, - params, - shouldFetch, - ]); + const { data = getInitialData(filterNames), status } = useFetcher( + (callApmApi) => { + if (shouldFetch && urlParams.start && urlParams.end) { + return callApmApi({ + endpoint: `GET /api/apm/ui_filters/local_filters/${projection}` as const, + params: { + query: { + uiFilters: JSON.stringify(uiFilters), + start: urlParams.start, + end: urlParams.end, + // type expects string constants, but we have to send it as json + filterNames: JSON.stringify(filterNames) as any, + ...params, + }, + }, + }); + } + }, + [ + projection, + uiFilters, + urlParams.start, + urlParams.end, + filterNames, + params, + shouldFetch, + ] + ); const filters = data.map((filter) => ({ ...filter, diff --git a/x-pack/plugins/apm/public/hooks/use_environments_fetcher.tsx b/x-pack/plugins/apm/public/hooks/use_environments_fetcher.tsx index 1ad151b8c7e90..38a8610b82acb 100644 --- a/x-pack/plugins/apm/public/hooks/use_environments_fetcher.tsx +++ b/x-pack/plugins/apm/public/hooks/use_environments_fetcher.tsx @@ -10,7 +10,6 @@ import { ENVIRONMENT_ALL, ENVIRONMENT_NOT_DEFINED, } from '../../common/environment_filter_values'; -import { callApmApi } from '../services/rest/createCallApmApi'; function getEnvironmentOptions(environments: string[]) { const environmentOptions = environments @@ -32,20 +31,23 @@ export function useEnvironmentsFetcher({ start?: string; end?: string; }) { - const { data: environments = [], status = 'loading' } = useFetcher(() => { - if (start && end) { - return callApmApi({ - endpoint: 'GET /api/apm/ui_filters/environments', - params: { - query: { - start, - end, - serviceName, + const { data: environments = [], status = 'loading' } = useFetcher( + (callApmApi) => { + if (start && end) { + return callApmApi({ + endpoint: 'GET /api/apm/ui_filters/environments', + params: { + query: { + start, + end, + serviceName, + }, }, - }, - }); - } - }, [start, end, serviceName]); + }); + } + }, + [start, end, serviceName] + ); const environmentOptions = useMemo( () => getEnvironmentOptions(environments), diff --git a/x-pack/plugins/apm/public/hooks/use_fetcher.tsx b/x-pack/plugins/apm/public/hooks/use_fetcher.tsx index 2b58f30a9ec64..27427d80adc96 100644 --- a/x-pack/plugins/apm/public/hooks/use_fetcher.tsx +++ b/x-pack/plugins/apm/public/hooks/use_fetcher.tsx @@ -8,7 +8,10 @@ import { i18n } from '@kbn/i18n'; import React, { useEffect, useMemo, useState } from 'react'; import { IHttpFetchError } from 'src/core/public'; import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; -import { APMClient, callApmApi } from '../services/rest/createCallApmApi'; +import { + callApmApi, + AutoAbortedAPMClient, +} from '../services/rest/createCallApmApi'; import { useApmPluginContext } from '../context/apm_plugin/use_apm_plugin_context'; export enum FETCH_STATUS { @@ -39,6 +42,14 @@ function getDetailsFromErrorResponse(error: IHttpFetchError) { ); } +const createAutoAbortedAPMClient = ( + signal: AbortSignal +): AutoAbortedAPMClient => { + return ((options: Parameters[0]) => { + return callApmApi({ ...options, signal }); + }) as AutoAbortedAPMClient; +}; + // fetcher functions can return undefined OR a promise. Previously we had a more simple type // but it led to issues when using object destructuring with default values type InferResponseType = Exclude extends Promise< @@ -48,7 +59,7 @@ type InferResponseType = Exclude extends Promise< : unknown; export function useFetcher( - fn: (callApmApi: APMClient) => TReturn, + fn: (callApmApi: AutoAbortedAPMClient) => TReturn, fnDeps: any[], options: { preservePreviousData?: boolean; @@ -66,10 +77,16 @@ export function useFetcher( const [counter, setCounter] = useState(0); useEffect(() => { - let didCancel = false; + let controller: AbortController = new AbortController(); async function doFetch() { - const promise = fn(callApmApi); + controller.abort(); + + controller = new AbortController(); + + const signal = controller.signal; + + const promise = fn(createAutoAbortedAPMClient(signal)); // if `fn` doesn't return a promise it is a signal that data fetching was not initiated. // This can happen if the data fetching is conditional (based on certain inputs). // In these cases it is not desirable to invoke the global loading spinner, or change the status to success @@ -85,7 +102,11 @@ export function useFetcher( try { const data = await promise; - if (!didCancel) { + // when http fetches are aborted, the promise will be rejected + // and this code is never reached. For async operations that are + // not cancellable, we need to check whether the signal was + // aborted before updating the result. + if (!signal.aborted) { setResult({ data, status: FETCH_STATUS.SUCCESS, @@ -95,7 +116,7 @@ export function useFetcher( } catch (e) { const err = e as Error | IHttpFetchError; - if (!didCancel) { + if (!signal.aborted) { const errorDetails = 'response' in err ? getDetailsFromErrorResponse(err) : err.message; @@ -130,7 +151,7 @@ export function useFetcher( doFetch(); return () => { - didCancel = true; + controller.abort(); }; /* eslint-disable react-hooks/exhaustive-deps */ }, [ diff --git a/x-pack/plugins/apm/public/services/rest/apm_observability_overview_fetchers.ts b/x-pack/plugins/apm/public/services/rest/apm_observability_overview_fetchers.ts index a0ed51be685c7..ac98bbab5775b 100644 --- a/x-pack/plugins/apm/public/services/rest/apm_observability_overview_fetchers.ts +++ b/x-pack/plugins/apm/public/services/rest/apm_observability_overview_fetchers.ts @@ -20,6 +20,7 @@ export const fetchObservabilityOverviewPageData = async ({ }: FetchDataParams): Promise => { const data = await callApmApi({ endpoint: 'GET /api/apm/observability_overview', + signal: null, params: { query: { start: new Date(absoluteTime.start).toISOString(), @@ -59,5 +60,6 @@ export const fetchObservabilityOverviewPageData = async ({ export async function hasData() { return await callApmApi({ endpoint: 'GET /api/apm/observability_overview/has_data', + signal: null, }); } diff --git a/x-pack/plugins/apm/public/services/rest/callApi.ts b/x-pack/plugins/apm/public/services/rest/callApi.ts index 4ee12908b7c79..d14cbc5f6d63e 100644 --- a/x-pack/plugins/apm/public/services/rest/callApi.ts +++ b/x-pack/plugins/apm/public/services/rest/callApi.ts @@ -87,5 +87,6 @@ function isCachable(fetchOptions: FetchOptions) { // order the options object to make sure that two objects with the same arguments, produce produce the // same cache key regardless of the order of properties function getCacheKey(options: FetchOptions) { - return hash(options); + const { pathname, method, body, query, headers } = options; + return hash({ pathname, method, body, query, headers }); } diff --git a/x-pack/plugins/apm/public/services/rest/createCallApmApi.ts b/x-pack/plugins/apm/public/services/rest/createCallApmApi.ts index 2760ed558865a..b77233982ffc5 100644 --- a/x-pack/plugins/apm/public/services/rest/createCallApmApi.ts +++ b/x-pack/plugins/apm/public/services/rest/createCallApmApi.ts @@ -12,11 +12,14 @@ import { APMAPI } from '../../../server/routes/create_apm_api'; import { Client } from '../../../server/routes/typings'; export type APMClient = Client; +export type AutoAbortedAPMClient = Client; + export type APMClientOptions = Omit< FetchOptions, - 'query' | 'body' | 'pathname' + 'query' | 'body' | 'pathname' | 'signal' > & { endpoint: string; + signal: AbortSignal | null; params?: { body?: any; query?: any; diff --git a/x-pack/plugins/apm/public/services/rest/index_pattern.ts b/x-pack/plugins/apm/public/services/rest/index_pattern.ts index 6ec542ab6baf3..cea3bcc0b68cc 100644 --- a/x-pack/plugins/apm/public/services/rest/index_pattern.ts +++ b/x-pack/plugins/apm/public/services/rest/index_pattern.ts @@ -9,11 +9,13 @@ import { callApmApi } from './createCallApmApi'; export const createStaticIndexPattern = async () => { return await callApmApi({ endpoint: 'POST /api/apm/index_pattern/static', + signal: null, }); }; export const getApmIndexPatternTitle = async () => { return await callApmApi({ endpoint: 'GET /api/apm/index_pattern/title', + signal: null, }); }; diff --git a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.test.ts b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.test.ts index f58e04061254d..87cb60a543193 100644 --- a/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.test.ts +++ b/x-pack/plugins/apm/server/lib/helpers/create_es_client/create_apm_event_client/index.test.ts @@ -65,10 +65,12 @@ describe('createApmEventClient', () => { await new Promise((resolve) => { setTimeout(() => { + incomingRequest.on('abort', () => { + setTimeout(() => { + resolve(undefined); + }, 0); + }); incomingRequest.abort(); - setTimeout(() => { - resolve(undefined); - }, 0); }, 50); }); diff --git a/x-pack/plugins/apm/server/lib/helpers/setup_request.test.ts b/x-pack/plugins/apm/server/lib/helpers/setup_request.test.ts index f00941d6e6800..47a13185ff90f 100644 --- a/x-pack/plugins/apm/server/lib/helpers/setup_request.test.ts +++ b/x-pack/plugins/apm/server/lib/helpers/setup_request.test.ts @@ -92,7 +92,7 @@ function getMockRequest() { url: '', events: { aborted$: { - subscribe: jest.fn(), + subscribe: jest.fn().mockReturnValue({ unsubscribe: jest.fn() }), }, }, } as unknown) as KibanaRequest; diff --git a/x-pack/plugins/apm/server/routes/create_api/index.ts b/x-pack/plugins/apm/server/routes/create_api/index.ts index 721badf7fc025..6f6ec4f06b6cb 100644 --- a/x-pack/plugins/apm/server/routes/create_api/index.ts +++ b/x-pack/plugins/apm/server/routes/create_api/index.ts @@ -10,6 +10,7 @@ import * as t from 'io-ts'; import { PathReporter } from 'io-ts/lib/PathReporter'; import { isLeft } from 'fp-ts/lib/Either'; import { KibanaResponseFactory, RouteRegistrar } from 'src/core/server'; +import { RequestAbortedError } from '@elastic/elasticsearch/lib/errors'; import { merge } from '../../../common/runtime_types/merge'; import { strictKeysRt } from '../../../common/runtime_types/strict_keys_rt'; import { APMConfig } from '../..'; @@ -132,6 +133,15 @@ export function createApi() { if (Boom.isBoom(error)) { return convertBoomToKibanaResponse(error, response); } + + if (error instanceof RequestAbortedError) { + return response.custom({ + statusCode: 499, + body: { + message: 'Client closed request', + }, + }); + } throw error; } } diff --git a/x-pack/plugins/apm/server/routes/typings.ts b/x-pack/plugins/apm/server/routes/typings.ts index 7d7a5c3b0dab3..4cc3c747b201f 100644 --- a/x-pack/plugins/apm/server/routes/typings.ts +++ b/x-pack/plugins/apm/server/routes/typings.ts @@ -131,15 +131,20 @@ type MaybeOptional }> = RequiredKeys< ? { params?: T['params'] } : { params: T['params'] }; -export type Client = < - TEndpoint extends keyof TRouteState & string ->( - options: Omit & { +export type Client< + TRouteState, + TOptions extends { abortable: boolean } = { abortable: true } +> = ( + options: Omit< + FetchOptions, + 'query' | 'body' | 'pathname' | 'method' | 'signal' + > & { forceCache?: boolean; endpoint: TEndpoint; } & (TRouteState[TEndpoint] extends { params: t.Any } ? MaybeOptional<{ params: t.TypeOf }> - : {}) + : {}) & + (TOptions extends { abortable: true } ? { signal: AbortSignal | null } : {}) ) => Promise< TRouteState[TEndpoint] extends { ret: any } ? TRouteState[TEndpoint]['ret']