Skip to content

Commit

Permalink
[DataUsage][Serverless] Handle usage metrics errors (#197056)
Browse files Browse the repository at this point in the history
  • Loading branch information
ashokaditya authored Oct 24, 2024
1 parent c6f4178 commit 1267bd7
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { css } from '@emotion/react';
import { EuiFlexGroup, EuiFlexItem, EuiLoadingElastic } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { Charts } from './charts';
import { useBreadcrumbs } from '../../utils/use_breadcrumbs';
import { useKibanaContextForPlugin } from '../../utils/use_kibana';
Expand All @@ -29,7 +30,7 @@ const FlexItemWithCss = ({ children }: { children: React.ReactNode }) => (

export const DataUsageMetrics = () => {
const {
services: { chrome, appParams },
services: { chrome, appParams, notifications },
} = useKibanaContextForPlugin();
useBreadcrumbs([{ text: PLUGIN_NAME }], appParams, chrome);

Expand All @@ -43,10 +44,15 @@ export const DataUsageMetrics = () => {
setUrlDateRangeFilter,
} = useDataUsageMetricsUrlParams();

const { data: dataStreams, isFetching: isFetchingDataStreams } = useGetDataUsageDataStreams({
const {
error: errorFetchingDataStreams,
data: dataStreams,
isFetching: isFetchingDataStreams,
} = useGetDataUsageDataStreams({
selectedDataStreams: dataStreamsFromUrl,
options: {
enabled: true,
retry: false,
},
});

Expand Down Expand Up @@ -93,6 +99,7 @@ export const DataUsageMetrics = () => {
const { dateRangePickerState, onRefreshChange, onTimeChange } = useDateRangePicker();

const {
error: errorFetchingDataUsageMetrics,
data,
isFetching,
isFetched,
Expand Down Expand Up @@ -157,6 +164,23 @@ export const DataUsageMetrics = () => {
onChangeMetricTypesFilter,
]);

if (errorFetchingDataUsageMetrics) {
notifications.toasts.addDanger({
title: i18n.translate('xpack.dataUsage.getMetrics.addFailure.toast.title', {
defaultMessage: 'Error getting usage metrics',
}),
text: errorFetchingDataUsageMetrics.message,
});
}
if (errorFetchingDataStreams) {
notifications.toasts.addDanger({
title: i18n.translate('xpack.dataUsage.getDataStreams.addFailure.toast.title', {
defaultMessage: 'Error getting data streams',
}),
text: errorFetchingDataStreams.message,
});
}

return (
<EuiFlexGroup alignItems="flexStart" direction="column">
<FlexItemWithCss>
Expand Down
24 changes: 6 additions & 18 deletions x-pack/plugins/data_usage/public/hooks/use_get_data_streams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/

import type { UseQueryOptions, UseQueryResult } from '@tanstack/react-query';
import { i18n } from '@kbn/i18n';
import { useQuery } from '@tanstack/react-query';
import type { IHttpFetchError } from '@kbn/core-http-browser';
import { DATA_USAGE_DATA_STREAMS_API_ROUTE } from '../../common';
Expand All @@ -33,22 +32,19 @@ export const useGetDataUsageDataStreams = ({
options?: UseQueryOptions<GetDataUsageDataStreamsResponse, IHttpFetchError>;
}): UseQueryResult<GetDataUsageDataStreamsResponse, IHttpFetchError> => {
const http = useKibanaContextForPlugin().services.http;
const {
services: { notifications },
} = useKibanaContextForPlugin();

return useQuery<GetDataUsageDataStreamsResponse, IHttpFetchError>({
queryKey: ['get-data-usage-data-streams'],
...options,
keepPreviousData: true,
queryFn: async () => {
const dataStreamsResponse = await http.get<GetDataUsageDataStreamsResponse>(
DATA_USAGE_DATA_STREAMS_API_ROUTE,
{
const dataStreamsResponse = await http
.get<GetDataUsageDataStreamsResponse>(DATA_USAGE_DATA_STREAMS_API_ROUTE, {
version: '1',
// query: {},
}
);
})
.catch((error) => {
throw error.body;
});

const augmentedDataStreamsBasedOnSelectedItems = dataStreamsResponse.reduce<{
selected: GetDataUsageDataStreamsResponse;
Expand Down Expand Up @@ -87,13 +83,5 @@ export const useGetDataUsageDataStreams = ({
: PAGING_PARAMS.default
);
},
onError: (error: IHttpFetchError) => {
notifications.toasts.addDanger({
title: i18n.translate('xpack.dataUsage.getDataStreams.addFailure.toast.title', {
defaultMessage: 'Error getting data streams',
}),
text: error.message,
});
},
});
};
36 changes: 14 additions & 22 deletions x-pack/plugins/data_usage/public/hooks/use_get_usage_metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/

import type { UseQueryOptions, UseQueryResult } from '@tanstack/react-query';
import { i18n } from '@kbn/i18n';
import { useQuery } from '@tanstack/react-query';
import type { IHttpFetchError } from '@kbn/core-http-browser';
import { UsageMetricsRequestBody, UsageMetricsResponseSchemaBody } from '../../common/rest_types';
Expand All @@ -23,33 +22,26 @@ export const useGetDataUsageMetrics = (
options: UseQueryOptions<UsageMetricsResponseSchemaBody, IHttpFetchError<ErrorType>> = {}
): UseQueryResult<UsageMetricsResponseSchemaBody, IHttpFetchError<ErrorType>> => {
const http = useKibanaContextForPlugin().services.http;
const {
services: { notifications },
} = useKibanaContextForPlugin();

return useQuery<UsageMetricsResponseSchemaBody, IHttpFetchError<ErrorType>>({
queryKey: ['get-data-usage-metrics', body],
...options,
keepPreviousData: true,
queryFn: async ({ signal }) => {
return http.post<UsageMetricsResponseSchemaBody>(DATA_USAGE_METRICS_API_ROUTE, {
signal,
version: '1',
body: JSON.stringify({
from: body.from,
to: body.to,
metricTypes: body.metricTypes,
dataStreams: body.dataStreams,
}),
});
},
onError: (error: IHttpFetchError<ErrorType>) => {
notifications.toasts.addDanger({
title: i18n.translate('xpack.dataUsage.getMetrics.addFailure.toast.title', {
defaultMessage: 'Error getting usage metrics',
}),
text: error.message,
});
return http
.post<UsageMetricsResponseSchemaBody>(DATA_USAGE_METRICS_API_ROUTE, {
signal,
version: '1',
body: JSON.stringify({
from: body.from,
to: body.to,
metricTypes: body.metricTypes,
dataStreams: body.dataStreams,
}),
})
.catch((error) => {
throw error.body;
});
},
});
};
8 changes: 8 additions & 0 deletions x-pack/plugins/data_usage/server/routes/error_handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import type { IKibanaResponse, KibanaResponseFactory, Logger } from '@kbn/core/server';
import { CustomHttpRequestError } from '../utils/custom_http_request_error';
import { BaseError } from '../common/errors';
import { AutoOpsError } from '../services/errors';

export class NotFoundError extends BaseError {}

Expand All @@ -31,6 +32,13 @@ export const errorHandler = <E extends Error>(
});
}

if (error instanceof AutoOpsError) {
return res.customError({
statusCode: 503,
body: error,
});
}

if (error instanceof NotFoundError) {
return res.notFound({ body: error });
}
Expand Down
26 changes: 13 additions & 13 deletions x-pack/plugins/data_usage/server/services/autoops_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ import {
} from '../../common/rest_types';
import { AppContextService } from './app_context';
import { AutoOpsConfig } from '../types';
import { AutoOpsError } from './errors';

const AGENT_CREATION_FAILED_ERROR = 'AutoOps API could not create the autoops agent';
const AUTO_OPS_AGENT_CREATION_PREFIX = '[AutoOps API] Creating autoops agent failed';
const AUTO_OPS_MISSING_CONFIG_ERROR = 'Missing autoops configuration';
export class AutoOpsAPIService {
constructor(private appContextService: AppContextService) {}
public async autoOpsUsageMetricsAPI(requestBody: UsageMetricsRequestBody) {
Expand All @@ -34,8 +38,8 @@ export class AutoOpsAPIService {

const autoopsConfig = this.appContextService.getConfig()?.autoops;
if (!autoopsConfig) {
logger.error('[AutoOps API] Missing autoops configuration', errorMetadata);
throw new Error('missing autoops configuration');
logger.error(`[AutoOps API] ${AUTO_OPS_MISSING_CONFIG_ERROR}`, errorMetadata);
throw new AutoOpsError(AUTO_OPS_MISSING_CONFIG_ERROR);
}

logger.debug(
Expand Down Expand Up @@ -86,7 +90,7 @@ export class AutoOpsAPIService {
(error: Error | AxiosError) => {
if (!axios.isAxiosError(error)) {
logger.error(
`[AutoOps API] Creating autoops failed with an error ${error} ${requestConfigDebugStatus}`,
`${AUTO_OPS_AGENT_CREATION_PREFIX} with an error ${error} ${requestConfigDebugStatus}`,
errorMetadataWithRequestConfig
);
throw new Error(withRequestIdMessage(error.message));
Expand All @@ -97,7 +101,7 @@ export class AutoOpsAPIService {
if (error.response) {
// The request was made and the server responded with a status code and error data
logger.error(
`[AutoOps API] Creating autoops failed because the AutoOps API responding with a status code that falls out of the range of 2xx: ${JSON.stringify(
`${AUTO_OPS_AGENT_CREATION_PREFIX} because the AutoOps API responded with a status code that falls out of the range of 2xx: ${JSON.stringify(
error.response.status
)}} ${JSON.stringify(error.response.data)}} ${requestConfigDebugStatus}`,
{
Expand All @@ -111,30 +115,26 @@ export class AutoOpsAPIService {
},
}
);
throw new Error(
withRequestIdMessage(`the AutoOps API could not create the autoops agent`)
);
throw new AutoOpsError(withRequestIdMessage(AGENT_CREATION_FAILED_ERROR));
} else if (error.request) {
// The request was made but no response was received
logger.error(
`[AutoOps API] Creating autoops agent failed while sending the request to the AutoOps API: ${errorLogCodeCause} ${requestConfigDebugStatus}`,
`${AUTO_OPS_AGENT_CREATION_PREFIX} while sending the request to the AutoOps API: ${errorLogCodeCause} ${requestConfigDebugStatus}`,
errorMetadataWithRequestConfig
);
throw new Error(withRequestIdMessage(`no response received from the AutoOps API`));
} else {
// Something happened in setting up the request that triggered an Error
logger.error(
`[AutoOps API] Creating autoops agent failed to be created ${errorLogCodeCause} ${requestConfigDebugStatus}`,
`${AUTO_OPS_AGENT_CREATION_PREFIX} to be created ${errorLogCodeCause} ${requestConfigDebugStatus}`,
errorMetadataWithRequestConfig
);
throw new Error(
withRequestIdMessage('the AutoOps API could not create the autoops agent')
);
throw new AutoOpsError(withRequestIdMessage(AGENT_CREATION_FAILED_ERROR));
}
}
);

logger.debug(`[AutoOps API] Created an autoops agent ${response}`);
logger.debug(`[AutoOps API] Successfully created an autoops agent ${response}`);
return response;
}

Expand Down
10 changes: 10 additions & 0 deletions x-pack/plugins/data_usage/server/services/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* 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 { BaseError } from '../common/errors';

export class AutoOpsError extends BaseError {}
24 changes: 17 additions & 7 deletions x-pack/plugins/data_usage/server/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { ValidationError } from '@kbn/config-schema';
import { AppContextService } from './app_context';
import { AutoOpsAPIService } from './autoops_api';
import type { DataUsageContext } from '../types';
import { MetricTypes } from '../../common/rest_types';
import { AutoOpsError } from './errors';

export class DataUsageService {
private appContextService: AppContextService;
Expand All @@ -32,12 +34,20 @@ export class DataUsageService {
metricTypes: MetricTypes[];
dataStreams: string[];
}) {
const response = await this.autoOpsAPIService.autoOpsUsageMetricsAPI({
from,
to,
metricTypes,
dataStreams,
});
return response.data;
try {
const response = await this.autoOpsAPIService.autoOpsUsageMetricsAPI({
from,
to,
metricTypes,
dataStreams,
});
return response.data;
} catch (error) {
if (error instanceof ValidationError) {
throw new AutoOpsError(error.message);
}

throw error;
}
}
}

0 comments on commit 1267bd7

Please sign in to comment.