[8.12] [SLOs] Fix cloning SLO by opening pre filled form (#172927) (#173504)

# Backport

This will backport the following commits from `main` to `8.12`:
- [[SLOs] Fix cloning SLO by opening pre filled form
(#172927)](https://github.com/elastic/kibana/pull/172927)

<!--- Backport version: 8.9.7 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT
[{"author":{"name":"Shahzad","email":"shahzad31comp@gmail.com"},"sourceCommit":{"committedDate":"2023-12-18T10:52:32Z","message":"[SLOs]
Fix cloning SLO by opening pre filled form
(#172927)","sha":"3419469e39e0bb4443267bc80fb2500629c772ba","branchLabelMapping":{"^v8.13.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","backport:prev-minor","Team:obs-ux-management","v8.13.0"],"number":172927,"url":"https://github.com/elastic/kibana/pull/172927","mergeCommit":{"message":"[SLOs]
Fix cloning SLO by opening pre filled form
(#172927)","sha":"3419469e39e0bb4443267bc80fb2500629c772ba"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v8.13.0","labelRegex":"^v8.13.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/172927","number":172927,"mergeCommit":{"message":"[SLOs]
Fix cloning SLO by opening pre filled form
(#172927)","sha":"3419469e39e0bb4443267bc80fb2500629c772ba"}}]}]
BACKPORT-->

Co-authored-by: Shahzad <shahzad31comp@gmail.com>
This commit is contained in:
Kibana Machine 2023-12-18 07:16:28 -05:00 committed by GitHub
parent dab8881b8f
commit 292b615297
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 109 additions and 212 deletions

View file

@ -112,7 +112,7 @@ export function SloOverview({
return (
<div ref={containerRef} style={{ width: '100%' }}>
<SloCardChart slo={slo} historicalSliData={historicalSliData ?? []} cardsPerRow={4} />
<SloCardChart slo={slo} historicalSliData={historicalSliData ?? []} />
<SloCardBadgesPortal containerRef={containerRef}>
<SloCardItemBadges
slo={slo}

View file

@ -5,83 +5,28 @@
* 2.0.
*/
import { IHttpFetchError, ResponseErrorBody } from '@kbn/core/public';
import { i18n } from '@kbn/i18n';
import type { CreateSLOInput, CreateSLOResponse, FindSLOResponse } from '@kbn/slo-schema';
import { QueryKey, useMutation, useQueryClient } from '@tanstack/react-query';
import { v4 as uuidv4 } from 'uuid';
import { encode } from '@kbn/rison';
import { useCallback } from 'react';
import { SLOWithSummaryResponse } from '@kbn/slo-schema';
import { paths } from '../../../common/locators/paths';
import { useKibana } from '../../utils/kibana_react';
import { sloKeys } from './query_key_factory';
type ServerError = IHttpFetchError<ResponseErrorBody>;
export function useCloneSlo() {
const {
http,
notifications: { toasts },
http: { basePath },
application: { navigateToUrl },
} = useKibana().services;
const queryClient = useQueryClient();
return useMutation<
CreateSLOResponse,
ServerError,
{ slo: CreateSLOInput; originalSloId?: string },
{ previousData?: FindSLOResponse; queryKey?: QueryKey }
>(
['cloneSlo'],
({ slo }: { slo: CreateSLOInput; originalSloId?: string }) => {
const body = JSON.stringify(slo);
return http.post<CreateSLOResponse>(`/api/observability/slos`, { body });
},
{
onMutate: async ({ slo, originalSloId }) => {
await queryClient.cancelQueries({ queryKey: sloKeys.lists(), exact: false });
const queriesData = queryClient.getQueriesData<FindSLOResponse>({
queryKey: sloKeys.lists(),
exact: false,
});
const [queryKey, previousData] = queriesData?.at(0) ?? [];
const originalSlo = previousData?.results?.find((el) => el.id === originalSloId);
const optimisticUpdate = {
page: previousData?.page ?? 1,
perPage: previousData?.perPage ?? 25,
total: previousData?.total ? previousData.total + 1 : 1,
results: [
...(previousData?.results ?? []),
{ ...originalSlo, name: slo.name, id: uuidv4(), summary: undefined },
],
};
if (queryKey) {
queryClient.setQueryData(queryKey, optimisticUpdate);
}
return { queryKey, previousData };
},
// If the mutation fails, use the context returned from onMutate to roll back
onError: (error, { slo }, context) => {
if (context?.previousData && context?.queryKey) {
queryClient.setQueryData(context.queryKey, context.previousData);
}
toasts.addError(new Error(error.body?.message ?? error.message), {
title: i18n.translate('xpack.observability.slo.clone.errorNotification', {
defaultMessage: 'Failed to clone {name}',
values: { name: slo.name },
}),
});
},
onSuccess: (_data, { slo }) => {
toasts.addSuccess(
i18n.translate('xpack.observability.slo.clone.successNotification', {
defaultMessage: 'Successfully created {name}',
values: { name: slo.name },
})
return useCallback(
(slo: SLOWithSummaryResponse) => {
navigateToUrl(
basePath.prepend(
paths.observability.sloCreateWithEncodedForm(
encode({ ...slo, name: `[Copy] ${slo.name}`, id: undefined })
)
)
);
queryClient.invalidateQueries({ queryKey: sloKeys.lists(), exact: false });
},
}
[navigateToUrl, basePath]
);
}

View file

@ -10,20 +10,16 @@ import { i18n } from '@kbn/i18n';
import { EuiButton, EuiContextMenuItem, EuiContextMenuPanel, EuiPopover } from '@elastic/eui';
import { SLOWithSummaryResponse } from '@kbn/slo-schema';
import { useCloneSlo } from '../../../hooks/slo/use_clone_slo';
import { SloDeleteConfirmationModal } from '../../../components/slo/delete_confirmation_modal/slo_delete_confirmation_modal';
import { useCapabilities } from '../../../hooks/slo/use_capabilities';
import { useKibana } from '../../../utils/kibana_react';
import { useCloneSlo } from '../../../hooks/slo/use_clone_slo';
import { useDeleteSlo } from '../../../hooks/slo/use_delete_slo';
import { isApmIndicatorType } from '../../../utils/slo/indicator';
import { convertSliApmParamsToApmAppDeeplinkUrl } from '../../../utils/slo/convert_sli_apm_params_to_apm_app_deeplink_url';
import { SLO_BURN_RATE_RULE_TYPE_ID } from '../../../../common/constants';
import { rulesLocatorID, sloFeatureId } from '../../../../common';
import { paths } from '../../../../common/locators/paths';
import {
transformSloResponseToCreateSloForm,
transformCreateSLOFormToCreateSLOInput,
} from '../../slo_edit/helpers/process_slo_form_values';
import type { RulesParams } from '../../../locators/rules';
export interface Props {
@ -47,7 +43,6 @@ export function HeaderControl({ isLoading, slo }: Props) {
const [isRuleFlyoutVisible, setRuleFlyoutVisibility] = useState<boolean>(false);
const [isDeleteConfirmationModalOpen, setDeleteConfirmationModalOpen] = useState(false);
const { mutate: cloneSlo } = useCloneSlo();
const { mutate: deleteSlo } = useDeleteSlo();
const handleActionsClick = () => setIsPopoverOpen((value) => !value);
@ -101,17 +96,12 @@ export function HeaderControl({ isLoading, slo }: Props) {
}
};
const navigateToClone = useCloneSlo();
const handleClone = async () => {
if (slo) {
setIsPopoverOpen(false);
const newSlo = transformCreateSLOFormToCreateSLOInput(
transformSloResponseToCreateSloForm({ ...slo, name: `[Copy] ${slo.name}` })!
);
cloneSlo({ slo: newSlo, originalSloId: slo.id });
navigate(basePath.prepend(paths.observability.slos));
navigateToClone(slo);
}
};

View file

@ -17,7 +17,6 @@ import { useFetchSloDetails } from '../../hooks/slo/use_fetch_slo_details';
import { useFetchHistoricalSummary } from '../../hooks/slo/use_fetch_historical_summary';
import { useFetchActiveAlerts } from '../../hooks/slo/use_fetch_active_alerts';
import { ActiveAlerts } from '../../hooks/slo/active_alerts';
import { useCloneSlo } from '../../hooks/slo/use_clone_slo';
import { useDeleteSlo } from '../../hooks/slo/use_delete_slo';
import { render } from '../../utils/test_helper';
import { SloDetailsPage } from './slo_details';
@ -30,6 +29,7 @@ import {
import { chartPluginMock } from '@kbn/charts-plugin/public/mocks';
import { buildApmAvailabilityIndicator } from '../../data/slo/indicator';
import { ALL_VALUE } from '@kbn/slo-schema';
import { encode } from '@kbn/rison';
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
@ -44,7 +44,6 @@ jest.mock('../../hooks/slo/use_capabilities');
jest.mock('../../hooks/slo/use_fetch_active_alerts');
jest.mock('../../hooks/slo/use_fetch_slo_details');
jest.mock('../../hooks/slo/use_fetch_historical_summary');
jest.mock('../../hooks/slo/use_clone_slo');
jest.mock('../../hooks/slo/use_delete_slo');
const useKibanaMock = useKibana as jest.Mock;
@ -55,12 +54,10 @@ const useCapabilitiesMock = useCapabilities as jest.Mock;
const useFetchActiveAlertsMock = useFetchActiveAlerts as jest.Mock;
const useFetchSloDetailsMock = useFetchSloDetails as jest.Mock;
const useFetchHistoricalSummaryMock = useFetchHistoricalSummary as jest.Mock;
const useCloneSloMock = useCloneSlo as jest.Mock;
const useDeleteSloMock = useDeleteSlo as jest.Mock;
const mockNavigate = jest.fn();
const mockLocator = jest.fn();
const mockClone = jest.fn();
const mockDelete = jest.fn();
const mockCapabilities = {
apm: { show: true },
@ -120,7 +117,6 @@ describe('SLO Details Page', () => {
data: historicalSummaryData,
});
useFetchActiveAlertsMock.mockReturnValue({ isLoading: false, data: new ActiveAlerts() });
useCloneSloMock.mockReturnValue({ mutate: mockClone });
useDeleteSloMock.mockReturnValue({ mutate: mockDelete });
useLocationMock.mockReturnValue({ search: '' });
});
@ -248,29 +244,12 @@ describe('SLO Details Page', () => {
fireEvent.click(button!);
const {
id,
createdAt,
enabled,
revision,
summary,
settings,
updatedAt,
instanceId,
version,
...newSlo
} = slo;
expect(mockClone).toBeCalledWith({
originalSloId: slo.id,
slo: {
...newSlo,
name: `[Copy] ${newSlo.name}`,
},
});
await waitFor(() => {
expect(mockNavigate).toBeCalledWith(paths.observability.slos);
expect(mockNavigate).toBeCalledWith(
paths.observability.sloCreateWithEncodedForm(
encode({ ...slo, name: `[Copy] ${slo.name}`, id: undefined })
)
);
});
});

View file

@ -18,8 +18,7 @@ import { i18n } from '@kbn/i18n';
import type { GetSLOResponse } from '@kbn/slo-schema';
import React, { useCallback, useEffect, useState } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { sloFeatureId } from '../../../../common';
import { SLO_BURN_RATE_RULE_TYPE_ID } from '../../../../common/constants';
import { BurnRateRuleFlyout } from '../../slos/components/common/burn_rate_rule_flyout';
import { paths } from '../../../../common/locators/paths';
import { useCreateSlo } from '../../../hooks/slo/use_create_slo';
import { useFetchRulesForSlo } from '../../../hooks/slo/use_fetch_rules_for_slo';
@ -54,7 +53,6 @@ export function SloEditForm({ slo }: Props) {
const {
application: { navigateToUrl },
http: { basePath },
triggersActionsUi: { getAddRuleFlyout: AddRuleFlyout },
} = useKibana().services;
const isEditMode = slo !== undefined;
@ -146,10 +144,6 @@ export function SloEditForm({ slo }: Props) {
setIsCreateRuleCheckboxChecked(!isCreateRuleCheckboxChecked);
};
const handleCloseRuleFlyout = async () => {
navigateToUrl(basePath.prepend(paths.observability.slos));
};
return (
<>
<FormProvider {...methods}>
@ -256,17 +250,11 @@ export function SloEditForm({ slo }: Props) {
</EuiFlexGroup>
</FormProvider>
{isAddRuleFlyoutOpen && slo ? (
<AddRuleFlyout
<BurnRateRuleFlyout
slo={slo as GetSLOResponse}
isAddRuleFlyoutOpen={isAddRuleFlyoutOpen}
canChangeTrigger={false}
consumer={sloFeatureId}
initialValues={{ name: `${watch('name')} burn rate rule`, params: { sloId: slo.id } }}
ruleTypeId={SLO_BURN_RATE_RULE_TYPE_ID}
onClose={handleCloseRuleFlyout}
onSave={handleCloseRuleFlyout}
useRuleProducer
/>
) : null}
</>
);
}

View file

@ -33,7 +33,7 @@ export function SloEditPage() {
const { sloId } = useParams<{ sloId: string | undefined }>();
const { hasAtLeast } = useLicense();
const hasRightLicense = hasAtLeast('platinum');
const { data: slo, isInitialLoading } = useFetchSloDetails({ sloId });
const { data: slo } = useFetchSloDetails({ sloId });
useBreadcrumbs([
{
@ -66,10 +66,6 @@ export function SloEditPage() {
navigateToUrl(basePath.prepend(paths.observability.slos));
}
if (sloId && isInitialLoading) {
return null;
}
return (
<ObservabilityPageTemplate
pageHeader={{

View file

@ -19,6 +19,7 @@ import { EuiIcon, EuiPanel, useEuiBackgroundColor } from '@elastic/eui';
import { ALL_VALUE, HistoricalSummaryResponse, SLOWithSummaryResponse } from '@kbn/slo-schema';
import { Rule } from '@kbn/triggers-actions-ui-plugin/public';
import { i18n } from '@kbn/i18n';
import { css } from '@emotion/react';
import { SloCardBadgesPortal } from './badges_portal';
import { useSloListActions } from '../../hooks/use_slo_list_actions';
import { BurnRateRuleFlyout } from '../common/burn_rate_rule_flyout';
@ -52,7 +53,7 @@ export const useSloCardColor = (status?: SLOWithSummaryResponse['summary']['stat
return colors[status ?? 'NO_DATA'];
};
const getSubTitle = (slo: SLOWithSummaryResponse, cardsPerRow: number) => {
const getSubTitle = (slo: SLOWithSummaryResponse) => {
return slo.groupBy && slo.groupBy !== ALL_VALUE ? `${slo.groupBy}: ${slo.instanceId}` : '';
};
@ -88,14 +89,14 @@ export function SloCardItem({ slo, rules, activeAlerts, historicalSummary, cards
}
}}
paddingSize="none"
style={{
height: '182px',
overflow: 'hidden',
position: 'relative',
}}
css={css`
height: 182px;
overflow: hidden;
position: relative;
`}
title={slo.summary.status}
>
<SloCardChart slo={slo} historicalSliData={historicalSliData} cardsPerRow={cardsPerRow} />
<SloCardChart slo={slo} historicalSliData={historicalSliData} />
{(isMouseOver || isActionsPopoverOpen) && (
<SloCardItemActions
slo={slo}
@ -135,11 +136,9 @@ export function SloCardItem({ slo, rules, activeAlerts, historicalSummary, cards
export function SloCardChart({
slo,
cardsPerRow,
historicalSliData,
}: {
slo: SLOWithSummaryResponse;
cardsPerRow: number;
historicalSliData?: Array<{ key?: number; value?: number }>;
}) {
const {
@ -147,7 +146,7 @@ export function SloCardChart({
} = useKibana().services;
const cardColor = useSloCardColor(slo.summary.status);
const subTitle = getSubTitle(slo, cardsPerRow);
const subTitle = getSubTitle(slo);
const { sliValue, sloTarget, sloDetailsUrl } = useSloFormattedSummary(slo);
return (

View file

@ -68,8 +68,10 @@ export function SloListCardView({
}
return (
<EuiFlexGrid columns={columns}>
{sloList.map((slo) => (
<EuiFlexGrid columns={columns} gutterSize="m">
{sloList
.filter((slo) => slo.summary)
.map((slo) => (
<EuiFlexItem key={`${slo.id}-${slo.instanceId ?? 'ALL_VALUE'}`}>
<SloCardItem
slo={slo}

View file

@ -8,6 +8,7 @@
import React from 'react';
import { SLOWithSummaryResponse } from '@kbn/slo-schema';
import { useQueryClient } from '@tanstack/react-query';
import { paths } from '../../../../../common/locators/paths';
import { useGetFilteredRuleTypes } from '../../../../hooks/use_get_filtered_rule_types';
import { sloKeys } from '../../../../hooks/slo/query_key_factory';
import { useKibana } from '../../../../utils/kibana_react';
@ -17,13 +18,17 @@ import { sloFeatureId } from '../../../../../common';
export function BurnRateRuleFlyout({
slo,
isAddRuleFlyoutOpen,
canChangeTrigger,
setIsAddRuleFlyoutOpen,
}: {
slo: SLOWithSummaryResponse;
slo?: SLOWithSummaryResponse;
isAddRuleFlyoutOpen: boolean;
setIsAddRuleFlyoutOpen: (value: boolean) => void;
canChangeTrigger?: boolean;
setIsAddRuleFlyoutOpen?: (value: boolean) => void;
}) {
const {
application: { navigateToUrl },
http: { basePath },
triggersActionsUi: { getAddRuleFlyout: AddRuleFlyout },
} = useKibana().services;
@ -32,19 +37,30 @@ export function BurnRateRuleFlyout({
const queryClient = useQueryClient();
const handleSavedRule = async () => {
if (setIsAddRuleFlyoutOpen) {
queryClient.invalidateQueries({ queryKey: sloKeys.rules(), exact: false });
} else {
navigateToUrl(basePath.prepend(paths.observability.slos));
}
};
return isAddRuleFlyoutOpen ? (
const handleCloseRuleFlyout = async () => {
if (setIsAddRuleFlyoutOpen) {
setIsAddRuleFlyoutOpen(false);
} else {
navigateToUrl(basePath.prepend(paths.observability.slos));
}
};
return isAddRuleFlyoutOpen && slo ? (
<AddRuleFlyout
canChangeTrigger={canChangeTrigger}
consumer={sloFeatureId}
filteredRuleTypes={filteredRuleTypes}
ruleTypeId={SLO_BURN_RATE_RULE_TYPE_ID}
initialValues={{ name: `${slo.name} Burn Rate rule`, params: { sloId: slo.id } }}
onSave={handleSavedRule}
onClose={() => {
setIsAddRuleFlyoutOpen(false);
}}
onClose={handleCloseRuleFlyout}
useRuleProducer
/>
) : null;

View file

@ -19,6 +19,7 @@ import { i18n } from '@kbn/i18n';
import { ALL_VALUE, SLOWithSummaryResponse } from '@kbn/slo-schema';
import { useQueryClient } from '@tanstack/react-query';
import React, { useState } from 'react';
import { useCloneSlo } from '../../../../hooks/slo/use_clone_slo';
import { rulesLocatorID, sloFeatureId } from '../../../../../common';
import { SLO_BURN_RATE_RULE_TYPE_ID } from '../../../../../common/constants';
import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n';
@ -28,7 +29,6 @@ import { SloStatusBadge } from '../../../../components/slo/slo_status_badge';
import { SloActiveAlertsBadge } from '../../../../components/slo/slo_status_badge/slo_active_alerts_badge';
import { sloKeys } from '../../../../hooks/slo/query_key_factory';
import { useCapabilities } from '../../../../hooks/slo/use_capabilities';
import { useCloneSlo } from '../../../../hooks/slo/use_clone_slo';
import { useDeleteSlo } from '../../../../hooks/slo/use_delete_slo';
import { useFetchActiveAlerts } from '../../../../hooks/slo/use_fetch_active_alerts';
import { useFetchHistoricalSummary } from '../../../../hooks/slo/use_fetch_historical_summary';
@ -37,10 +37,6 @@ import { useGetFilteredRuleTypes } from '../../../../hooks/use_get_filtered_rule
import { RulesParams } from '../../../../locators/rules';
import { useKibana } from '../../../../utils/kibana_react';
import { formatHistoricalData } from '../../../../utils/slo/chart_data_formatter';
import {
transformCreateSLOFormToCreateSLOInput,
transformSloResponseToCreateSloForm,
} from '../../../slo_edit/helpers/process_slo_form_values';
import { SloRulesBadge } from '../badges/slo_rules_badge';
import { SloListEmpty } from '../slo_list_empty';
import { SloListError } from '../slo_list_error';
@ -72,7 +68,6 @@ export function SloListCompactView({ sloList, loading, error }: Props) {
const filteredRuleTypes = useGetFilteredRuleTypes();
const queryClient = useQueryClient();
const { mutate: cloneSlo } = useCloneSlo();
const { mutate: deleteSlo } = useDeleteSlo();
const [sloToAddRule, setSloToAddRule] = useState<SLOWithSummaryResponse | undefined>(undefined);
@ -102,6 +97,8 @@ export function SloListCompactView({ sloList, loading, error }: Props) {
list: sloList.map((slo) => ({ sloId: slo.id, instanceId: slo.instanceId ?? ALL_VALUE })),
});
const navigateToClone = useCloneSlo();
const actions: Array<DefaultItemAction<SLOWithSummaryResponse>> = [
{
type: 'icon',
@ -180,11 +177,7 @@ export function SloListCompactView({ sloList, loading, error }: Props) {
'data-test-subj': 'sloActionsClone',
enabled: (_) => hasWriteCapabilities,
onClick: (slo: SLOWithSummaryResponse) => {
const newSlo = transformCreateSLOFormToCreateSLOInput(
transformSloResponseToCreateSloForm({ ...slo, name: `[Copy] ${slo.name}` })!
);
cloneSlo({ slo: newSlo, originalSloId: slo.id });
navigateToClone(slo);
},
},
{

View file

@ -17,16 +17,12 @@ import { i18n } from '@kbn/i18n';
import React from 'react';
import { ALL_VALUE, SLOWithSummaryResponse } from '@kbn/slo-schema';
import styled from 'styled-components';
import { useCapabilities } from '../../../hooks/slo/use_capabilities';
import { useCloneSlo } from '../../../hooks/slo/use_clone_slo';
import { useCapabilities } from '../../../hooks/slo/use_capabilities';
import { useKibana } from '../../../utils/kibana_react';
import { paths } from '../../../../common/locators/paths';
import { RulesParams } from '../../../locators/rules';
import { rulesLocatorID } from '../../../../common';
import {
transformCreateSLOFormToCreateSLOInput,
transformSloResponseToCreateSloForm,
} from '../../slo_edit/helpers/process_slo_form_values';
interface Props {
slo: SLOWithSummaryResponse;
@ -73,7 +69,6 @@ export function SloItemActions({
},
} = useKibana().services;
const { hasWriteCapabilities } = useCapabilities();
const { mutate: cloneSlo } = useCloneSlo();
const sloDetailsUrl = basePath.prepend(
paths.observability.sloDetails(
@ -94,20 +89,17 @@ export function SloItemActions({
navigateToUrl(basePath.prepend(paths.observability.sloEdit(slo.id)));
};
const navigateToClone = useCloneSlo();
const handleClone = () => {
navigateToClone(slo);
};
const handleNavigateToRules = async () => {
const locator = locators.get<RulesParams>(rulesLocatorID);
locator?.navigate({ params: { sloId: slo.id } }, { replace: false });
};
const handleClone = () => {
const newSlo = transformCreateSLOFormToCreateSLOInput(
transformSloResponseToCreateSloForm({ ...slo, name: `[Copy] ${slo.name}` })!
);
cloneSlo({ slo: newSlo, originalSloId: slo.id });
setIsActionsPopoverOpen(false);
};
const handleDelete = () => {
setDeleteConfirmationModalOpen(true);
setIsActionsPopoverOpen(false);

View file

@ -15,7 +15,6 @@ import { paths } from '../../../common/locators/paths';
import { historicalSummaryData } from '../../data/slo/historical_summary_data';
import { emptySloList, sloList } from '../../data/slo/slo';
import { useCapabilities } from '../../hooks/slo/use_capabilities';
import { useCloneSlo } from '../../hooks/slo/use_clone_slo';
import { useCreateSlo } from '../../hooks/slo/use_create_slo';
import { useDeleteSlo } from '../../hooks/slo/use_delete_slo';
import { useFetchHistoricalSummary } from '../../hooks/slo/use_fetch_historical_summary';
@ -24,6 +23,7 @@ import { useLicense } from '../../hooks/use_license';
import { useKibana } from '../../utils/kibana_react';
import { render } from '../../utils/test_helper';
import { SlosPage } from './slos';
import { encode } from '@kbn/rison';
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
@ -35,7 +35,6 @@ jest.mock('../../utils/kibana_react');
jest.mock('../../hooks/use_license');
jest.mock('../../hooks/slo/use_fetch_slo_list');
jest.mock('../../hooks/slo/use_create_slo');
jest.mock('../../hooks/slo/use_clone_slo');
jest.mock('../../hooks/slo/use_delete_slo');
jest.mock('../../hooks/slo/use_fetch_historical_summary');
jest.mock('../../hooks/slo/use_capabilities');
@ -44,17 +43,14 @@ const useKibanaMock = useKibana as jest.Mock;
const useLicenseMock = useLicense as jest.Mock;
const useFetchSloListMock = useFetchSloList as jest.Mock;
const useCreateSloMock = useCreateSlo as jest.Mock;
const useCloneSloMock = useCloneSlo as jest.Mock;
const useDeleteSloMock = useDeleteSlo as jest.Mock;
const useFetchHistoricalSummaryMock = useFetchHistoricalSummary as jest.Mock;
const useCapabilitiesMock = useCapabilities as jest.Mock;
const mockCreateSlo = jest.fn();
const mockCloneSlo = jest.fn();
const mockDeleteSlo = jest.fn();
useCreateSloMock.mockReturnValue({ mutate: mockCreateSlo });
useCloneSloMock.mockReturnValue({ mutate: mockCloneSlo });
useDeleteSloMock.mockReturnValue({ mutate: mockDeleteSlo });
const mockNavigate = jest.fn();
@ -358,7 +354,14 @@ describe('SLOs Page', () => {
button.click();
expect(mockCloneSlo).toBeCalled();
await waitFor(() => {
const slo = sloList.results.at(0);
expect(mockNavigate).toBeCalledWith(
paths.observability.sloCreateWithEncodedForm(
encode({ ...slo, name: `[Copy] ${slo!.name}`, id: undefined })
)
);
});
});
});
});

View file

@ -28923,8 +28923,6 @@
"xpack.observability.slo.alerting.burnRate.reasonForInstanceId": "{actionGroupName} : Le taux d'avancement pour le (les) dernier(s) {longWindowDuration} est de {longWindowBurnRate} et pour le (les) dernier(s) {shortWindowDuration} est de {shortWindowBurnRate} pour {instanceId}. Alerter si supérieur à {burnRateThreshold} pour les deux fenêtres",
"xpack.observability.slo.burnRate.breachedStatustSubtitle": "Au rythme actuel, le budget d'erreur sera épuisé en {hour} heures.",
"xpack.observability.slo.burnRate.threshold": "Le seuil est {threshold}x",
"xpack.observability.slo.clone.errorNotification": "Échec du clonage de {name}",
"xpack.observability.slo.clone.successNotification": "{name} créé avec succès",
"xpack.observability.slo.create.errorNotification": "Un problème est survenu lors de la création de {name}",
"xpack.observability.slo.create.successNotification": "{name} créé avec succès",
"xpack.observability.slo.deleteConfirmationModal.title": "Supprimer {name} ?",

View file

@ -28923,8 +28923,6 @@
"xpack.observability.slo.alerting.burnRate.reasonForInstanceId": "{actionGroupName}:過去{longWindowDuration}のバーンレートは{longWindowBurnRate}、{instanceId}の過去{shortWindowDuration}のバーンレートは{shortWindowBurnRate}です。両期間とも{burnRateThreshold}を超えたらアラート",
"xpack.observability.slo.burnRate.breachedStatustSubtitle": "現在のレートでは、エラー予算は{hour}時間後に使い果たされます。",
"xpack.observability.slo.burnRate.threshold": "しきい値は{threshold}xです",
"xpack.observability.slo.clone.errorNotification": "{name}を複製できませんでした",
"xpack.observability.slo.clone.successNotification": "{name}の作成が正常に完了しました",
"xpack.observability.slo.create.errorNotification": "{name}の作成中に問題が発生しました",
"xpack.observability.slo.create.successNotification": "{name}の作成が正常に完了しました",
"xpack.observability.slo.deleteConfirmationModal.title": "{name}を削除しますか?",

View file

@ -28920,8 +28920,6 @@
"xpack.observability.slo.alerting.burnRate.reasonForInstanceId": "{actionGroupName}:过去 {longWindowDuration} 的消耗速度为 {longWindowBurnRate},且对于 {instanceId},过去 {shortWindowDuration} 为 {shortWindowBurnRate}。两个窗口超出 {burnRateThreshold} 时告警",
"xpack.observability.slo.burnRate.breachedStatustSubtitle": "按照当前的速率,错误预算将在 {hour} 小时后耗尽。",
"xpack.observability.slo.burnRate.threshold": "阈值为 {threshold}x",
"xpack.observability.slo.clone.errorNotification": "无法克隆 {name}",
"xpack.observability.slo.clone.successNotification": "已成功创建 {name}",
"xpack.observability.slo.create.errorNotification": "创建 {name} 时出现问题",
"xpack.observability.slo.create.successNotification": "已成功创建 {name}",
"xpack.observability.slo.deleteConfirmationModal.title": "删除 {name}",