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

[SLOs] Try async creation of resources !! #192836

Merged
merged 21 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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 @@ -5,6 +5,7 @@
* 2.0.
*/

import React from 'react';
import { useMutation } from '@tanstack/react-query';
import { i18n } from '@kbn/i18n';
import { BASE_ALERTING_API_PATH, RuleTypeParams } from '@kbn/alerting-plugin/common';
Expand All @@ -13,14 +14,39 @@ import type {
CreateRuleRequestBody,
CreateRuleResponse,
} from '@kbn/alerting-plugin/common/routes/rule/apis/create';
import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui';
import { toMountPoint } from '@kbn/react-kibana-mount';
import { useKibana } from '../utils/kibana_react';

export function useCreateRule<Params extends RuleTypeParams = never>() {
const {
http,
i18n: i18nStart,
notifications: { toasts },
theme,
} = useKibana().services;

let loadingToastId = '';

const showLoadingToast = () => {
const loadingToast = toasts.addInfo({
title: toMountPoint(
<EuiFlexGroup justifyContent="center" alignItems="center">
<EuiFlexItem grow={false}>
{i18n.translate('xpack.slo.rules.createRule.loadingNotification.descriptionText', {
defaultMessage: 'Creating burn rate rule ...',
})}
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiLoadingSpinner size="s" />
</EuiFlexItem>
</EuiFlexGroup>,
{ i18n: i18nStart, theme }
),
});
loadingToastId = loadingToast.id;
};

const createRule = useMutation<
CreateRuleResponse<Params>,
Error,
Expand All @@ -31,6 +57,7 @@ export function useCreateRule<Params extends RuleTypeParams = never>() {
try {
const ruleId = v4();
const body = JSON.stringify(rule);
showLoadingToast();
shahzad31 marked this conversation as resolved.
Show resolved Hide resolved
return http.post(`${BASE_ALERTING_API_PATH}/rule/${ruleId}`, {
body,
});
Expand All @@ -40,6 +67,9 @@ export function useCreateRule<Params extends RuleTypeParams = never>() {
},
{
onError: (_err) => {
if (loadingToastId) {
shahzad31 marked this conversation as resolved.
Show resolved Hide resolved
toasts.remove(loadingToastId);
}
toasts.addDanger(
i18n.translate('xpack.slo.rules.createRule.errorNotification.descriptionText', {
defaultMessage: 'Failed to create burn rate rule.',
Expand All @@ -48,6 +78,9 @@ export function useCreateRule<Params extends RuleTypeParams = never>() {
},

onSuccess: () => {
if (loadingToastId) {
toasts.remove(loadingToastId);
}
toasts.addSuccess(
i18n.translate('xpack.slo.rules.createRule.successNotification.descriptionText', {
defaultMessage: 'Burn rate rule created successfully.',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export function useCreateSlo() {
<RedirectAppLinks coreStart={services} data-test-subj="observabilityMainContainer">
<FormattedMessage
id="xpack.slo.slo.create.successNotification"
defaultMessage="Successfully created {name}. {editSLO}"
defaultMessage='Successfully created SLO: "{name}". {editSLO}'
values={{
name: slo.name,
editSLO: (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type { GetSLOResponse } from '@kbn/slo-schema';
import React, { useCallback, useMemo } from 'react';
import { useFormContext } from 'react-hook-form';
import { InPortal } from 'react-reverse-portal';
import { useCreateRule } from '../../../hooks/use_create_rule';
import { useCreateRule } from '../../../hooks/use_create_burn_rate_rule';
import { useKibana } from '../../../utils/kibana_react';
import { sloEditFormFooterPortal } from '../shared_flyout/slo_add_form_flyout';
import { paths } from '../../../../common/locators/paths';
Expand Down Expand Up @@ -70,8 +70,10 @@ export function SloEditFormFooter({ slo, onSave }: Props) {
} else {
const processedValues = transformCreateSLOFormToCreateSLOInput(values);
const resp = await createSlo({ slo: processedValues });
await createBurnRateRule({
shahzad31 marked this conversation as resolved.
Show resolved Hide resolved
createBurnRateRule({
rule: createBurnRateRuleRequestBody({ ...processedValues, id: resp.id }),
}).catch((e) => {
// we handle error in hook
});
if (onSave) {
onSave();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { useFetchApmSuggestions } from '../../hooks/use_fetch_apm_suggestions';
import { useFetchIndices } from '../../hooks/use_fetch_indices';
import { useFetchSloDetails } from '../../hooks/use_fetch_slo_details';
import { usePermissions } from '../../hooks/use_permissions';
import { useCreateRule } from '../../hooks/use_create_rule';
import { useCreateRule } from '../../hooks/use_create_burn_rate_rule';
import { useUpdateSlo } from '../../hooks/use_update_slo';
import { useKibana } from '../../utils/kibana_react';
import { kibanaStartMock } from '../../utils/kibana_react.mock';
Expand All @@ -45,7 +45,7 @@ jest.mock('../../hooks/use_create_slo');
jest.mock('../../hooks/use_update_slo');
jest.mock('../../hooks/use_fetch_apm_suggestions');
jest.mock('../../hooks/use_permissions');
jest.mock('../../hooks/use_create_rule');
jest.mock('../../hooks/use_create_burn_rate_rule');

const mockUseKibanaReturnValue = kibanaStartMock.startContract();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import {
updateSLOParamsSchema,
} from '@kbn/slo-schema';
import { getOverviewParamsSchema } from '@kbn/slo-schema/src/rest_specs/routes/get_overview';
import { KibanaRequest } from '@kbn/core-http-server';
import { RegisterRoutesDependencies } from '../register_routes';
import { GetSLOsOverview } from '../../services/get_slos_overview';
import type { IndicatorTypes } from '../../domain/models';
import {
Expand Down Expand Up @@ -91,6 +93,11 @@ const assertPlatinumLicense = async (context: SloRequestHandlerContext) => {
}
};

const getSpaceId = async (deps: RegisterRoutesDependencies, request: KibanaRequest) => {
const spaces = await deps.getSpacesStart();
return (await spaces?.spacesService?.getActiveSpace(request))?.id ?? 'default';
};

const createSLORoute = createSloServerRoute({
endpoint: 'POST /api/observability/slos 2023-10-31',
options: {
Expand All @@ -101,18 +108,18 @@ const createSLORoute = createSloServerRoute({
handler: async ({ context, params, logger, dependencies, request }) => {
await assertPlatinumLicense(context);

const spaces = await dependencies.getSpacesStart();
const dataViews = await dependencies.getDataViewsStart();
const spaceId = (await spaces?.spacesService?.getActiveSpace(request))?.id ?? 'default';

const core = await context.core;
const scopedClusterClient = core.elasticsearch.client;
const esClient = core.elasticsearch.client.asCurrentUser;
const basePath = dependencies.pluginsSetup.core.http.basePath;
const soClient = core.savedObjects.client;
const repository = new KibanaSavedObjectsSLORepository(soClient, logger);

const dataViewsService = await dataViews.dataViewsServiceFactory(soClient, esClient);
const [spaceId, dataViewsService] = await Promise.all([
getSpaceId(dependencies, request),
dataViews.dataViewsServiceFactory(soClient, esClient),
]);
const transformManager = new DefaultTransformManager(
transformGenerators,
scopedClusterClient,
Expand All @@ -125,7 +132,6 @@ const createSLORoute = createSloServerRoute({
scopedClusterClient,
logger
);

const createSLO = new CreateSLO(
esClient,
scopedClusterClient,
Expand All @@ -137,9 +143,7 @@ const createSLORoute = createSloServerRoute({
basePath
);

const response = await createSLO.execute(params.body);

return response;
return await createSLO.execute(params.body);
},
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,16 +81,14 @@ describe('CreateSLO', () => {
createdAt: expect.any(Date),
updatedAt: expect.any(Date),
}),
{ throwOnConflict: true }
{ existingSavedObjectId: undefined }
);

expect(mockTransformManager.install).toHaveBeenCalled();
expect(mockTransformManager.start).toHaveBeenCalled();
expect(
mockScopedClusterClient.asSecondaryAuthUser.ingest.putPipeline.mock.calls[0]
).toMatchSnapshot();
expect(mockSummaryTransformManager.install).toHaveBeenCalled();
expect(mockSummaryTransformManager.start).toHaveBeenCalled();
expect(mockEsClient.index.mock.calls[0]).toMatchSnapshot();

expect(response).toEqual(expect.objectContaining({ id: 'unique-id' }));
Expand Down Expand Up @@ -123,7 +121,7 @@ describe('CreateSLO', () => {
createdAt: expect.any(Date),
updatedAt: expect.any(Date),
}),
{ throwOnConflict: true }
{ existingSavedObjectId: undefined }
);
});

Expand Down Expand Up @@ -156,7 +154,7 @@ describe('CreateSLO', () => {
createdAt: expect.any(Date),
updatedAt: expect.any(Date),
}),
{ throwOnConflict: true }
{ existingSavedObjectId: undefined }
);
});
});
Expand All @@ -173,16 +171,16 @@ describe('CreateSLO', () => {
expect(mockRepository.deleteById).toHaveBeenCalled();
expect(
mockScopedClusterClient.asSecondaryAuthUser.ingest.deletePipeline
).toHaveBeenCalledTimes(1);
).toHaveBeenCalledTimes(2);

expect(mockSummaryTransformManager.stop).not.toHaveBeenCalled();
expect(mockSummaryTransformManager.uninstall).not.toHaveBeenCalled();
expect(mockTransformManager.stop).not.toHaveBeenCalled();
expect(mockTransformManager.uninstall).not.toHaveBeenCalled();
expect(mockSummaryTransformManager.stop).toHaveBeenCalledTimes(1);
expect(mockSummaryTransformManager.uninstall).toHaveBeenCalledTimes(1);
expect(mockTransformManager.stop).toHaveBeenCalledTimes(1);
expect(mockTransformManager.uninstall).toHaveBeenCalledTimes(1);
});

it('rollbacks completed operations when summary transform start fails', async () => {
mockSummaryTransformManager.start.mockRejectedValue(
it('rollbacks completed operations when summary transform install fails', async () => {
mockSummaryTransformManager.install.mockRejectedValue(
new Error('Summary transform install error')
);
const sloParams = createSLOParams({ indicator: createAPMTransactionErrorRateIndicator() });
Expand All @@ -199,7 +197,7 @@ describe('CreateSLO', () => {
).toHaveBeenCalledTimes(2);
expect(mockSummaryTransformManager.uninstall).toHaveBeenCalled();

expect(mockSummaryTransformManager.stop).not.toHaveBeenCalled();
expect(mockSummaryTransformManager.stop).toHaveBeenCalled();
});

it('rollbacks completed operations when create temporary document fails', async () => {
Expand Down
Loading