Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DataUsage][Serverless] Usage metrics improvements/tests #197056

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}
}
}