mirror of
https://github.com/elastic/kibana.git
synced 2025-04-25 02:09:32 -04:00
# 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:
parent
dab8881b8f
commit
292b615297
15 changed files with 109 additions and 212 deletions
|
@ -112,7 +112,7 @@ export function SloOverview({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={containerRef} style={{ width: '100%' }}>
|
<div ref={containerRef} style={{ width: '100%' }}>
|
||||||
<SloCardChart slo={slo} historicalSliData={historicalSliData ?? []} cardsPerRow={4} />
|
<SloCardChart slo={slo} historicalSliData={historicalSliData ?? []} />
|
||||||
<SloCardBadgesPortal containerRef={containerRef}>
|
<SloCardBadgesPortal containerRef={containerRef}>
|
||||||
<SloCardItemBadges
|
<SloCardItemBadges
|
||||||
slo={slo}
|
slo={slo}
|
||||||
|
|
|
@ -5,83 +5,28 @@
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IHttpFetchError, ResponseErrorBody } from '@kbn/core/public';
|
import { encode } from '@kbn/rison';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { useCallback } from 'react';
|
||||||
import type { CreateSLOInput, CreateSLOResponse, FindSLOResponse } from '@kbn/slo-schema';
|
import { SLOWithSummaryResponse } from '@kbn/slo-schema';
|
||||||
import { QueryKey, useMutation, useQueryClient } from '@tanstack/react-query';
|
import { paths } from '../../../common/locators/paths';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
|
||||||
import { useKibana } from '../../utils/kibana_react';
|
import { useKibana } from '../../utils/kibana_react';
|
||||||
import { sloKeys } from './query_key_factory';
|
|
||||||
|
|
||||||
type ServerError = IHttpFetchError<ResponseErrorBody>;
|
|
||||||
|
|
||||||
export function useCloneSlo() {
|
export function useCloneSlo() {
|
||||||
const {
|
const {
|
||||||
http,
|
http: { basePath },
|
||||||
notifications: { toasts },
|
application: { navigateToUrl },
|
||||||
} = useKibana().services;
|
} = useKibana().services;
|
||||||
const queryClient = useQueryClient();
|
|
||||||
|
|
||||||
return useMutation<
|
return useCallback(
|
||||||
CreateSLOResponse,
|
(slo: SLOWithSummaryResponse) => {
|
||||||
ServerError,
|
navigateToUrl(
|
||||||
{ slo: CreateSLOInput; originalSloId?: string },
|
basePath.prepend(
|
||||||
{ previousData?: FindSLOResponse; queryKey?: QueryKey }
|
paths.observability.sloCreateWithEncodedForm(
|
||||||
>(
|
encode({ ...slo, name: `[Copy] ${slo.name}`, id: undefined })
|
||||||
['cloneSlo'],
|
)
|
||||||
({ slo }: { slo: CreateSLOInput; originalSloId?: string }) => {
|
)
|
||||||
const body = JSON.stringify(slo);
|
);
|
||||||
return http.post<CreateSLOResponse>(`/api/observability/slos`, { body });
|
|
||||||
},
|
},
|
||||||
{
|
[navigateToUrl, basePath]
|
||||||
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 },
|
|
||||||
})
|
|
||||||
);
|
|
||||||
queryClient.invalidateQueries({ queryKey: sloKeys.lists(), exact: false });
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,20 +10,16 @@ import { i18n } from '@kbn/i18n';
|
||||||
import { EuiButton, EuiContextMenuItem, EuiContextMenuPanel, EuiPopover } from '@elastic/eui';
|
import { EuiButton, EuiContextMenuItem, EuiContextMenuPanel, EuiPopover } from '@elastic/eui';
|
||||||
import { SLOWithSummaryResponse } from '@kbn/slo-schema';
|
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 { SloDeleteConfirmationModal } from '../../../components/slo/delete_confirmation_modal/slo_delete_confirmation_modal';
|
||||||
import { useCapabilities } from '../../../hooks/slo/use_capabilities';
|
import { useCapabilities } from '../../../hooks/slo/use_capabilities';
|
||||||
import { useKibana } from '../../../utils/kibana_react';
|
import { useKibana } from '../../../utils/kibana_react';
|
||||||
import { useCloneSlo } from '../../../hooks/slo/use_clone_slo';
|
|
||||||
import { useDeleteSlo } from '../../../hooks/slo/use_delete_slo';
|
import { useDeleteSlo } from '../../../hooks/slo/use_delete_slo';
|
||||||
import { isApmIndicatorType } from '../../../utils/slo/indicator';
|
import { isApmIndicatorType } from '../../../utils/slo/indicator';
|
||||||
import { convertSliApmParamsToApmAppDeeplinkUrl } from '../../../utils/slo/convert_sli_apm_params_to_apm_app_deeplink_url';
|
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 { SLO_BURN_RATE_RULE_TYPE_ID } from '../../../../common/constants';
|
||||||
import { rulesLocatorID, sloFeatureId } from '../../../../common';
|
import { rulesLocatorID, sloFeatureId } from '../../../../common';
|
||||||
import { paths } from '../../../../common/locators/paths';
|
import { paths } from '../../../../common/locators/paths';
|
||||||
import {
|
|
||||||
transformSloResponseToCreateSloForm,
|
|
||||||
transformCreateSLOFormToCreateSLOInput,
|
|
||||||
} from '../../slo_edit/helpers/process_slo_form_values';
|
|
||||||
import type { RulesParams } from '../../../locators/rules';
|
import type { RulesParams } from '../../../locators/rules';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
|
@ -47,7 +43,6 @@ export function HeaderControl({ isLoading, slo }: Props) {
|
||||||
const [isRuleFlyoutVisible, setRuleFlyoutVisibility] = useState<boolean>(false);
|
const [isRuleFlyoutVisible, setRuleFlyoutVisibility] = useState<boolean>(false);
|
||||||
const [isDeleteConfirmationModalOpen, setDeleteConfirmationModalOpen] = useState(false);
|
const [isDeleteConfirmationModalOpen, setDeleteConfirmationModalOpen] = useState(false);
|
||||||
|
|
||||||
const { mutate: cloneSlo } = useCloneSlo();
|
|
||||||
const { mutate: deleteSlo } = useDeleteSlo();
|
const { mutate: deleteSlo } = useDeleteSlo();
|
||||||
|
|
||||||
const handleActionsClick = () => setIsPopoverOpen((value) => !value);
|
const handleActionsClick = () => setIsPopoverOpen((value) => !value);
|
||||||
|
@ -101,17 +96,12 @@ export function HeaderControl({ isLoading, slo }: Props) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const navigateToClone = useCloneSlo();
|
||||||
|
|
||||||
const handleClone = async () => {
|
const handleClone = async () => {
|
||||||
if (slo) {
|
if (slo) {
|
||||||
setIsPopoverOpen(false);
|
setIsPopoverOpen(false);
|
||||||
|
navigateToClone(slo);
|
||||||
const newSlo = transformCreateSLOFormToCreateSLOInput(
|
|
||||||
transformSloResponseToCreateSloForm({ ...slo, name: `[Copy] ${slo.name}` })!
|
|
||||||
);
|
|
||||||
|
|
||||||
cloneSlo({ slo: newSlo, originalSloId: slo.id });
|
|
||||||
|
|
||||||
navigate(basePath.prepend(paths.observability.slos));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@ import { useFetchSloDetails } from '../../hooks/slo/use_fetch_slo_details';
|
||||||
import { useFetchHistoricalSummary } from '../../hooks/slo/use_fetch_historical_summary';
|
import { useFetchHistoricalSummary } from '../../hooks/slo/use_fetch_historical_summary';
|
||||||
import { useFetchActiveAlerts } from '../../hooks/slo/use_fetch_active_alerts';
|
import { useFetchActiveAlerts } from '../../hooks/slo/use_fetch_active_alerts';
|
||||||
import { ActiveAlerts } from '../../hooks/slo/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 { useDeleteSlo } from '../../hooks/slo/use_delete_slo';
|
||||||
import { render } from '../../utils/test_helper';
|
import { render } from '../../utils/test_helper';
|
||||||
import { SloDetailsPage } from './slo_details';
|
import { SloDetailsPage } from './slo_details';
|
||||||
|
@ -30,6 +29,7 @@ import {
|
||||||
import { chartPluginMock } from '@kbn/charts-plugin/public/mocks';
|
import { chartPluginMock } from '@kbn/charts-plugin/public/mocks';
|
||||||
import { buildApmAvailabilityIndicator } from '../../data/slo/indicator';
|
import { buildApmAvailabilityIndicator } from '../../data/slo/indicator';
|
||||||
import { ALL_VALUE } from '@kbn/slo-schema';
|
import { ALL_VALUE } from '@kbn/slo-schema';
|
||||||
|
import { encode } from '@kbn/rison';
|
||||||
|
|
||||||
jest.mock('react-router-dom', () => ({
|
jest.mock('react-router-dom', () => ({
|
||||||
...jest.requireActual('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_active_alerts');
|
||||||
jest.mock('../../hooks/slo/use_fetch_slo_details');
|
jest.mock('../../hooks/slo/use_fetch_slo_details');
|
||||||
jest.mock('../../hooks/slo/use_fetch_historical_summary');
|
jest.mock('../../hooks/slo/use_fetch_historical_summary');
|
||||||
jest.mock('../../hooks/slo/use_clone_slo');
|
|
||||||
jest.mock('../../hooks/slo/use_delete_slo');
|
jest.mock('../../hooks/slo/use_delete_slo');
|
||||||
|
|
||||||
const useKibanaMock = useKibana as jest.Mock;
|
const useKibanaMock = useKibana as jest.Mock;
|
||||||
|
@ -55,12 +54,10 @@ const useCapabilitiesMock = useCapabilities as jest.Mock;
|
||||||
const useFetchActiveAlertsMock = useFetchActiveAlerts as jest.Mock;
|
const useFetchActiveAlertsMock = useFetchActiveAlerts as jest.Mock;
|
||||||
const useFetchSloDetailsMock = useFetchSloDetails as jest.Mock;
|
const useFetchSloDetailsMock = useFetchSloDetails as jest.Mock;
|
||||||
const useFetchHistoricalSummaryMock = useFetchHistoricalSummary as jest.Mock;
|
const useFetchHistoricalSummaryMock = useFetchHistoricalSummary as jest.Mock;
|
||||||
const useCloneSloMock = useCloneSlo as jest.Mock;
|
|
||||||
const useDeleteSloMock = useDeleteSlo as jest.Mock;
|
const useDeleteSloMock = useDeleteSlo as jest.Mock;
|
||||||
|
|
||||||
const mockNavigate = jest.fn();
|
const mockNavigate = jest.fn();
|
||||||
const mockLocator = jest.fn();
|
const mockLocator = jest.fn();
|
||||||
const mockClone = jest.fn();
|
|
||||||
const mockDelete = jest.fn();
|
const mockDelete = jest.fn();
|
||||||
const mockCapabilities = {
|
const mockCapabilities = {
|
||||||
apm: { show: true },
|
apm: { show: true },
|
||||||
|
@ -120,7 +117,6 @@ describe('SLO Details Page', () => {
|
||||||
data: historicalSummaryData,
|
data: historicalSummaryData,
|
||||||
});
|
});
|
||||||
useFetchActiveAlertsMock.mockReturnValue({ isLoading: false, data: new ActiveAlerts() });
|
useFetchActiveAlertsMock.mockReturnValue({ isLoading: false, data: new ActiveAlerts() });
|
||||||
useCloneSloMock.mockReturnValue({ mutate: mockClone });
|
|
||||||
useDeleteSloMock.mockReturnValue({ mutate: mockDelete });
|
useDeleteSloMock.mockReturnValue({ mutate: mockDelete });
|
||||||
useLocationMock.mockReturnValue({ search: '' });
|
useLocationMock.mockReturnValue({ search: '' });
|
||||||
});
|
});
|
||||||
|
@ -248,29 +244,12 @@ describe('SLO Details Page', () => {
|
||||||
|
|
||||||
fireEvent.click(button!);
|
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(() => {
|
await waitFor(() => {
|
||||||
expect(mockNavigate).toBeCalledWith(paths.observability.slos);
|
expect(mockNavigate).toBeCalledWith(
|
||||||
|
paths.observability.sloCreateWithEncodedForm(
|
||||||
|
encode({ ...slo, name: `[Copy] ${slo.name}`, id: undefined })
|
||||||
|
)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,7 @@ import { i18n } from '@kbn/i18n';
|
||||||
import type { GetSLOResponse } from '@kbn/slo-schema';
|
import type { GetSLOResponse } from '@kbn/slo-schema';
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import { FormProvider, useForm } from 'react-hook-form';
|
import { FormProvider, useForm } from 'react-hook-form';
|
||||||
import { sloFeatureId } from '../../../../common';
|
import { BurnRateRuleFlyout } from '../../slos/components/common/burn_rate_rule_flyout';
|
||||||
import { SLO_BURN_RATE_RULE_TYPE_ID } from '../../../../common/constants';
|
|
||||||
import { paths } from '../../../../common/locators/paths';
|
import { paths } from '../../../../common/locators/paths';
|
||||||
import { useCreateSlo } from '../../../hooks/slo/use_create_slo';
|
import { useCreateSlo } from '../../../hooks/slo/use_create_slo';
|
||||||
import { useFetchRulesForSlo } from '../../../hooks/slo/use_fetch_rules_for_slo';
|
import { useFetchRulesForSlo } from '../../../hooks/slo/use_fetch_rules_for_slo';
|
||||||
|
@ -54,7 +53,6 @@ export function SloEditForm({ slo }: Props) {
|
||||||
const {
|
const {
|
||||||
application: { navigateToUrl },
|
application: { navigateToUrl },
|
||||||
http: { basePath },
|
http: { basePath },
|
||||||
triggersActionsUi: { getAddRuleFlyout: AddRuleFlyout },
|
|
||||||
} = useKibana().services;
|
} = useKibana().services;
|
||||||
|
|
||||||
const isEditMode = slo !== undefined;
|
const isEditMode = slo !== undefined;
|
||||||
|
@ -146,10 +144,6 @@ export function SloEditForm({ slo }: Props) {
|
||||||
setIsCreateRuleCheckboxChecked(!isCreateRuleCheckboxChecked);
|
setIsCreateRuleCheckboxChecked(!isCreateRuleCheckboxChecked);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCloseRuleFlyout = async () => {
|
|
||||||
navigateToUrl(basePath.prepend(paths.observability.slos));
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FormProvider {...methods}>
|
<FormProvider {...methods}>
|
||||||
|
@ -256,17 +250,11 @@ export function SloEditForm({ slo }: Props) {
|
||||||
</EuiFlexGroup>
|
</EuiFlexGroup>
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
|
|
||||||
{isAddRuleFlyoutOpen && slo ? (
|
<BurnRateRuleFlyout
|
||||||
<AddRuleFlyout
|
slo={slo as GetSLOResponse}
|
||||||
canChangeTrigger={false}
|
isAddRuleFlyoutOpen={isAddRuleFlyoutOpen}
|
||||||
consumer={sloFeatureId}
|
canChangeTrigger={false}
|
||||||
initialValues={{ name: `${watch('name')} burn rate rule`, params: { sloId: slo.id } }}
|
/>
|
||||||
ruleTypeId={SLO_BURN_RATE_RULE_TYPE_ID}
|
|
||||||
onClose={handleCloseRuleFlyout}
|
|
||||||
onSave={handleCloseRuleFlyout}
|
|
||||||
useRuleProducer
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ export function SloEditPage() {
|
||||||
const { sloId } = useParams<{ sloId: string | undefined }>();
|
const { sloId } = useParams<{ sloId: string | undefined }>();
|
||||||
const { hasAtLeast } = useLicense();
|
const { hasAtLeast } = useLicense();
|
||||||
const hasRightLicense = hasAtLeast('platinum');
|
const hasRightLicense = hasAtLeast('platinum');
|
||||||
const { data: slo, isInitialLoading } = useFetchSloDetails({ sloId });
|
const { data: slo } = useFetchSloDetails({ sloId });
|
||||||
|
|
||||||
useBreadcrumbs([
|
useBreadcrumbs([
|
||||||
{
|
{
|
||||||
|
@ -66,10 +66,6 @@ export function SloEditPage() {
|
||||||
navigateToUrl(basePath.prepend(paths.observability.slos));
|
navigateToUrl(basePath.prepend(paths.observability.slos));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sloId && isInitialLoading) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ObservabilityPageTemplate
|
<ObservabilityPageTemplate
|
||||||
pageHeader={{
|
pageHeader={{
|
||||||
|
|
|
@ -19,6 +19,7 @@ import { EuiIcon, EuiPanel, useEuiBackgroundColor } from '@elastic/eui';
|
||||||
import { ALL_VALUE, HistoricalSummaryResponse, SLOWithSummaryResponse } from '@kbn/slo-schema';
|
import { ALL_VALUE, HistoricalSummaryResponse, SLOWithSummaryResponse } from '@kbn/slo-schema';
|
||||||
import { Rule } from '@kbn/triggers-actions-ui-plugin/public';
|
import { Rule } from '@kbn/triggers-actions-ui-plugin/public';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
|
import { css } from '@emotion/react';
|
||||||
import { SloCardBadgesPortal } from './badges_portal';
|
import { SloCardBadgesPortal } from './badges_portal';
|
||||||
import { useSloListActions } from '../../hooks/use_slo_list_actions';
|
import { useSloListActions } from '../../hooks/use_slo_list_actions';
|
||||||
import { BurnRateRuleFlyout } from '../common/burn_rate_rule_flyout';
|
import { BurnRateRuleFlyout } from '../common/burn_rate_rule_flyout';
|
||||||
|
@ -52,7 +53,7 @@ export const useSloCardColor = (status?: SLOWithSummaryResponse['summary']['stat
|
||||||
return colors[status ?? 'NO_DATA'];
|
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}` : '';
|
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"
|
paddingSize="none"
|
||||||
style={{
|
css={css`
|
||||||
height: '182px',
|
height: 182px;
|
||||||
overflow: 'hidden',
|
overflow: hidden;
|
||||||
position: 'relative',
|
position: relative;
|
||||||
}}
|
`}
|
||||||
title={slo.summary.status}
|
title={slo.summary.status}
|
||||||
>
|
>
|
||||||
<SloCardChart slo={slo} historicalSliData={historicalSliData} cardsPerRow={cardsPerRow} />
|
<SloCardChart slo={slo} historicalSliData={historicalSliData} />
|
||||||
{(isMouseOver || isActionsPopoverOpen) && (
|
{(isMouseOver || isActionsPopoverOpen) && (
|
||||||
<SloCardItemActions
|
<SloCardItemActions
|
||||||
slo={slo}
|
slo={slo}
|
||||||
|
@ -135,11 +136,9 @@ export function SloCardItem({ slo, rules, activeAlerts, historicalSummary, cards
|
||||||
|
|
||||||
export function SloCardChart({
|
export function SloCardChart({
|
||||||
slo,
|
slo,
|
||||||
cardsPerRow,
|
|
||||||
historicalSliData,
|
historicalSliData,
|
||||||
}: {
|
}: {
|
||||||
slo: SLOWithSummaryResponse;
|
slo: SLOWithSummaryResponse;
|
||||||
cardsPerRow: number;
|
|
||||||
historicalSliData?: Array<{ key?: number; value?: number }>;
|
historicalSliData?: Array<{ key?: number; value?: number }>;
|
||||||
}) {
|
}) {
|
||||||
const {
|
const {
|
||||||
|
@ -147,7 +146,7 @@ export function SloCardChart({
|
||||||
} = useKibana().services;
|
} = useKibana().services;
|
||||||
|
|
||||||
const cardColor = useSloCardColor(slo.summary.status);
|
const cardColor = useSloCardColor(slo.summary.status);
|
||||||
const subTitle = getSubTitle(slo, cardsPerRow);
|
const subTitle = getSubTitle(slo);
|
||||||
const { sliValue, sloTarget, sloDetailsUrl } = useSloFormattedSummary(slo);
|
const { sliValue, sloTarget, sloDetailsUrl } = useSloFormattedSummary(slo);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -68,27 +68,29 @@ export function SloListCardView({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EuiFlexGrid columns={columns}>
|
<EuiFlexGrid columns={columns} gutterSize="m">
|
||||||
{sloList.map((slo) => (
|
{sloList
|
||||||
<EuiFlexItem key={`${slo.id}-${slo.instanceId ?? 'ALL_VALUE'}`}>
|
.filter((slo) => slo.summary)
|
||||||
<SloCardItem
|
.map((slo) => (
|
||||||
slo={slo}
|
<EuiFlexItem key={`${slo.id}-${slo.instanceId ?? 'ALL_VALUE'}`}>
|
||||||
loading={loading}
|
<SloCardItem
|
||||||
error={error}
|
slo={slo}
|
||||||
activeAlerts={activeAlertsBySlo.get(slo)}
|
loading={loading}
|
||||||
rules={rulesBySlo?.[slo.id]}
|
error={error}
|
||||||
historicalSummary={
|
activeAlerts={activeAlertsBySlo.get(slo)}
|
||||||
historicalSummaries.find(
|
rules={rulesBySlo?.[slo.id]}
|
||||||
(historicalSummary) =>
|
historicalSummary={
|
||||||
historicalSummary.sloId === slo.id &&
|
historicalSummaries.find(
|
||||||
historicalSummary.instanceId === (slo.instanceId ?? ALL_VALUE)
|
(historicalSummary) =>
|
||||||
)?.data
|
historicalSummary.sloId === slo.id &&
|
||||||
}
|
historicalSummary.instanceId === (slo.instanceId ?? ALL_VALUE)
|
||||||
historicalSummaryLoading={historicalSummaryLoading}
|
)?.data
|
||||||
cardsPerRow={Number(cardsPerRow)}
|
}
|
||||||
/>
|
historicalSummaryLoading={historicalSummaryLoading}
|
||||||
</EuiFlexItem>
|
cardsPerRow={Number(cardsPerRow)}
|
||||||
))}
|
/>
|
||||||
|
</EuiFlexItem>
|
||||||
|
))}
|
||||||
</EuiFlexGrid>
|
</EuiFlexGrid>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { SLOWithSummaryResponse } from '@kbn/slo-schema';
|
import { SLOWithSummaryResponse } from '@kbn/slo-schema';
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
|
import { paths } from '../../../../../common/locators/paths';
|
||||||
import { useGetFilteredRuleTypes } from '../../../../hooks/use_get_filtered_rule_types';
|
import { useGetFilteredRuleTypes } from '../../../../hooks/use_get_filtered_rule_types';
|
||||||
import { sloKeys } from '../../../../hooks/slo/query_key_factory';
|
import { sloKeys } from '../../../../hooks/slo/query_key_factory';
|
||||||
import { useKibana } from '../../../../utils/kibana_react';
|
import { useKibana } from '../../../../utils/kibana_react';
|
||||||
|
@ -17,13 +18,17 @@ import { sloFeatureId } from '../../../../../common';
|
||||||
export function BurnRateRuleFlyout({
|
export function BurnRateRuleFlyout({
|
||||||
slo,
|
slo,
|
||||||
isAddRuleFlyoutOpen,
|
isAddRuleFlyoutOpen,
|
||||||
|
canChangeTrigger,
|
||||||
setIsAddRuleFlyoutOpen,
|
setIsAddRuleFlyoutOpen,
|
||||||
}: {
|
}: {
|
||||||
slo: SLOWithSummaryResponse;
|
slo?: SLOWithSummaryResponse;
|
||||||
isAddRuleFlyoutOpen: boolean;
|
isAddRuleFlyoutOpen: boolean;
|
||||||
setIsAddRuleFlyoutOpen: (value: boolean) => void;
|
canChangeTrigger?: boolean;
|
||||||
|
setIsAddRuleFlyoutOpen?: (value: boolean) => void;
|
||||||
}) {
|
}) {
|
||||||
const {
|
const {
|
||||||
|
application: { navigateToUrl },
|
||||||
|
http: { basePath },
|
||||||
triggersActionsUi: { getAddRuleFlyout: AddRuleFlyout },
|
triggersActionsUi: { getAddRuleFlyout: AddRuleFlyout },
|
||||||
} = useKibana().services;
|
} = useKibana().services;
|
||||||
|
|
||||||
|
@ -32,19 +37,30 @@ export function BurnRateRuleFlyout({
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
const handleSavedRule = async () => {
|
const handleSavedRule = async () => {
|
||||||
queryClient.invalidateQueries({ queryKey: sloKeys.rules(), exact: false });
|
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
|
<AddRuleFlyout
|
||||||
|
canChangeTrigger={canChangeTrigger}
|
||||||
consumer={sloFeatureId}
|
consumer={sloFeatureId}
|
||||||
filteredRuleTypes={filteredRuleTypes}
|
filteredRuleTypes={filteredRuleTypes}
|
||||||
ruleTypeId={SLO_BURN_RATE_RULE_TYPE_ID}
|
ruleTypeId={SLO_BURN_RATE_RULE_TYPE_ID}
|
||||||
initialValues={{ name: `${slo.name} Burn Rate rule`, params: { sloId: slo.id } }}
|
initialValues={{ name: `${slo.name} Burn Rate rule`, params: { sloId: slo.id } }}
|
||||||
onSave={handleSavedRule}
|
onSave={handleSavedRule}
|
||||||
onClose={() => {
|
onClose={handleCloseRuleFlyout}
|
||||||
setIsAddRuleFlyoutOpen(false);
|
|
||||||
}}
|
|
||||||
useRuleProducer
|
useRuleProducer
|
||||||
/>
|
/>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
|
@ -19,6 +19,7 @@ import { i18n } from '@kbn/i18n';
|
||||||
import { ALL_VALUE, SLOWithSummaryResponse } from '@kbn/slo-schema';
|
import { ALL_VALUE, SLOWithSummaryResponse } from '@kbn/slo-schema';
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
|
import { useCloneSlo } from '../../../../hooks/slo/use_clone_slo';
|
||||||
import { rulesLocatorID, sloFeatureId } from '../../../../../common';
|
import { rulesLocatorID, sloFeatureId } from '../../../../../common';
|
||||||
import { SLO_BURN_RATE_RULE_TYPE_ID } from '../../../../../common/constants';
|
import { SLO_BURN_RATE_RULE_TYPE_ID } from '../../../../../common/constants';
|
||||||
import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n';
|
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 { SloActiveAlertsBadge } from '../../../../components/slo/slo_status_badge/slo_active_alerts_badge';
|
||||||
import { sloKeys } from '../../../../hooks/slo/query_key_factory';
|
import { sloKeys } from '../../../../hooks/slo/query_key_factory';
|
||||||
import { useCapabilities } from '../../../../hooks/slo/use_capabilities';
|
import { useCapabilities } from '../../../../hooks/slo/use_capabilities';
|
||||||
import { useCloneSlo } from '../../../../hooks/slo/use_clone_slo';
|
|
||||||
import { useDeleteSlo } from '../../../../hooks/slo/use_delete_slo';
|
import { useDeleteSlo } from '../../../../hooks/slo/use_delete_slo';
|
||||||
import { useFetchActiveAlerts } from '../../../../hooks/slo/use_fetch_active_alerts';
|
import { useFetchActiveAlerts } from '../../../../hooks/slo/use_fetch_active_alerts';
|
||||||
import { useFetchHistoricalSummary } from '../../../../hooks/slo/use_fetch_historical_summary';
|
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 { RulesParams } from '../../../../locators/rules';
|
||||||
import { useKibana } from '../../../../utils/kibana_react';
|
import { useKibana } from '../../../../utils/kibana_react';
|
||||||
import { formatHistoricalData } from '../../../../utils/slo/chart_data_formatter';
|
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 { SloRulesBadge } from '../badges/slo_rules_badge';
|
||||||
import { SloListEmpty } from '../slo_list_empty';
|
import { SloListEmpty } from '../slo_list_empty';
|
||||||
import { SloListError } from '../slo_list_error';
|
import { SloListError } from '../slo_list_error';
|
||||||
|
@ -72,7 +68,6 @@ export function SloListCompactView({ sloList, loading, error }: Props) {
|
||||||
const filteredRuleTypes = useGetFilteredRuleTypes();
|
const filteredRuleTypes = useGetFilteredRuleTypes();
|
||||||
|
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { mutate: cloneSlo } = useCloneSlo();
|
|
||||||
const { mutate: deleteSlo } = useDeleteSlo();
|
const { mutate: deleteSlo } = useDeleteSlo();
|
||||||
|
|
||||||
const [sloToAddRule, setSloToAddRule] = useState<SLOWithSummaryResponse | undefined>(undefined);
|
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 })),
|
list: sloList.map((slo) => ({ sloId: slo.id, instanceId: slo.instanceId ?? ALL_VALUE })),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const navigateToClone = useCloneSlo();
|
||||||
|
|
||||||
const actions: Array<DefaultItemAction<SLOWithSummaryResponse>> = [
|
const actions: Array<DefaultItemAction<SLOWithSummaryResponse>> = [
|
||||||
{
|
{
|
||||||
type: 'icon',
|
type: 'icon',
|
||||||
|
@ -180,11 +177,7 @@ export function SloListCompactView({ sloList, loading, error }: Props) {
|
||||||
'data-test-subj': 'sloActionsClone',
|
'data-test-subj': 'sloActionsClone',
|
||||||
enabled: (_) => hasWriteCapabilities,
|
enabled: (_) => hasWriteCapabilities,
|
||||||
onClick: (slo: SLOWithSummaryResponse) => {
|
onClick: (slo: SLOWithSummaryResponse) => {
|
||||||
const newSlo = transformCreateSLOFormToCreateSLOInput(
|
navigateToClone(slo);
|
||||||
transformSloResponseToCreateSloForm({ ...slo, name: `[Copy] ${slo.name}` })!
|
|
||||||
);
|
|
||||||
|
|
||||||
cloneSlo({ slo: newSlo, originalSloId: slo.id });
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -17,16 +17,12 @@ import { i18n } from '@kbn/i18n';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ALL_VALUE, SLOWithSummaryResponse } from '@kbn/slo-schema';
|
import { ALL_VALUE, SLOWithSummaryResponse } from '@kbn/slo-schema';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { useCapabilities } from '../../../hooks/slo/use_capabilities';
|
|
||||||
import { useCloneSlo } from '../../../hooks/slo/use_clone_slo';
|
import { useCloneSlo } from '../../../hooks/slo/use_clone_slo';
|
||||||
|
import { useCapabilities } from '../../../hooks/slo/use_capabilities';
|
||||||
import { useKibana } from '../../../utils/kibana_react';
|
import { useKibana } from '../../../utils/kibana_react';
|
||||||
import { paths } from '../../../../common/locators/paths';
|
import { paths } from '../../../../common/locators/paths';
|
||||||
import { RulesParams } from '../../../locators/rules';
|
import { RulesParams } from '../../../locators/rules';
|
||||||
import { rulesLocatorID } from '../../../../common';
|
import { rulesLocatorID } from '../../../../common';
|
||||||
import {
|
|
||||||
transformCreateSLOFormToCreateSLOInput,
|
|
||||||
transformSloResponseToCreateSloForm,
|
|
||||||
} from '../../slo_edit/helpers/process_slo_form_values';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
slo: SLOWithSummaryResponse;
|
slo: SLOWithSummaryResponse;
|
||||||
|
@ -73,7 +69,6 @@ export function SloItemActions({
|
||||||
},
|
},
|
||||||
} = useKibana().services;
|
} = useKibana().services;
|
||||||
const { hasWriteCapabilities } = useCapabilities();
|
const { hasWriteCapabilities } = useCapabilities();
|
||||||
const { mutate: cloneSlo } = useCloneSlo();
|
|
||||||
|
|
||||||
const sloDetailsUrl = basePath.prepend(
|
const sloDetailsUrl = basePath.prepend(
|
||||||
paths.observability.sloDetails(
|
paths.observability.sloDetails(
|
||||||
|
@ -94,20 +89,17 @@ export function SloItemActions({
|
||||||
navigateToUrl(basePath.prepend(paths.observability.sloEdit(slo.id)));
|
navigateToUrl(basePath.prepend(paths.observability.sloEdit(slo.id)));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const navigateToClone = useCloneSlo();
|
||||||
|
|
||||||
|
const handleClone = () => {
|
||||||
|
navigateToClone(slo);
|
||||||
|
};
|
||||||
|
|
||||||
const handleNavigateToRules = async () => {
|
const handleNavigateToRules = async () => {
|
||||||
const locator = locators.get<RulesParams>(rulesLocatorID);
|
const locator = locators.get<RulesParams>(rulesLocatorID);
|
||||||
locator?.navigate({ params: { sloId: slo.id } }, { replace: false });
|
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 = () => {
|
const handleDelete = () => {
|
||||||
setDeleteConfirmationModalOpen(true);
|
setDeleteConfirmationModalOpen(true);
|
||||||
setIsActionsPopoverOpen(false);
|
setIsActionsPopoverOpen(false);
|
||||||
|
|
|
@ -15,7 +15,6 @@ import { paths } from '../../../common/locators/paths';
|
||||||
import { historicalSummaryData } from '../../data/slo/historical_summary_data';
|
import { historicalSummaryData } from '../../data/slo/historical_summary_data';
|
||||||
import { emptySloList, sloList } from '../../data/slo/slo';
|
import { emptySloList, sloList } from '../../data/slo/slo';
|
||||||
import { useCapabilities } from '../../hooks/slo/use_capabilities';
|
import { useCapabilities } from '../../hooks/slo/use_capabilities';
|
||||||
import { useCloneSlo } from '../../hooks/slo/use_clone_slo';
|
|
||||||
import { useCreateSlo } from '../../hooks/slo/use_create_slo';
|
import { useCreateSlo } from '../../hooks/slo/use_create_slo';
|
||||||
import { useDeleteSlo } from '../../hooks/slo/use_delete_slo';
|
import { useDeleteSlo } from '../../hooks/slo/use_delete_slo';
|
||||||
import { useFetchHistoricalSummary } from '../../hooks/slo/use_fetch_historical_summary';
|
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 { useKibana } from '../../utils/kibana_react';
|
||||||
import { render } from '../../utils/test_helper';
|
import { render } from '../../utils/test_helper';
|
||||||
import { SlosPage } from './slos';
|
import { SlosPage } from './slos';
|
||||||
|
import { encode } from '@kbn/rison';
|
||||||
|
|
||||||
jest.mock('react-router-dom', () => ({
|
jest.mock('react-router-dom', () => ({
|
||||||
...jest.requireActual('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/use_license');
|
||||||
jest.mock('../../hooks/slo/use_fetch_slo_list');
|
jest.mock('../../hooks/slo/use_fetch_slo_list');
|
||||||
jest.mock('../../hooks/slo/use_create_slo');
|
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_delete_slo');
|
||||||
jest.mock('../../hooks/slo/use_fetch_historical_summary');
|
jest.mock('../../hooks/slo/use_fetch_historical_summary');
|
||||||
jest.mock('../../hooks/slo/use_capabilities');
|
jest.mock('../../hooks/slo/use_capabilities');
|
||||||
|
@ -44,17 +43,14 @@ const useKibanaMock = useKibana as jest.Mock;
|
||||||
const useLicenseMock = useLicense as jest.Mock;
|
const useLicenseMock = useLicense as jest.Mock;
|
||||||
const useFetchSloListMock = useFetchSloList as jest.Mock;
|
const useFetchSloListMock = useFetchSloList as jest.Mock;
|
||||||
const useCreateSloMock = useCreateSlo as jest.Mock;
|
const useCreateSloMock = useCreateSlo as jest.Mock;
|
||||||
const useCloneSloMock = useCloneSlo as jest.Mock;
|
|
||||||
const useDeleteSloMock = useDeleteSlo as jest.Mock;
|
const useDeleteSloMock = useDeleteSlo as jest.Mock;
|
||||||
const useFetchHistoricalSummaryMock = useFetchHistoricalSummary as jest.Mock;
|
const useFetchHistoricalSummaryMock = useFetchHistoricalSummary as jest.Mock;
|
||||||
const useCapabilitiesMock = useCapabilities as jest.Mock;
|
const useCapabilitiesMock = useCapabilities as jest.Mock;
|
||||||
|
|
||||||
const mockCreateSlo = jest.fn();
|
const mockCreateSlo = jest.fn();
|
||||||
const mockCloneSlo = jest.fn();
|
|
||||||
const mockDeleteSlo = jest.fn();
|
const mockDeleteSlo = jest.fn();
|
||||||
|
|
||||||
useCreateSloMock.mockReturnValue({ mutate: mockCreateSlo });
|
useCreateSloMock.mockReturnValue({ mutate: mockCreateSlo });
|
||||||
useCloneSloMock.mockReturnValue({ mutate: mockCloneSlo });
|
|
||||||
useDeleteSloMock.mockReturnValue({ mutate: mockDeleteSlo });
|
useDeleteSloMock.mockReturnValue({ mutate: mockDeleteSlo });
|
||||||
|
|
||||||
const mockNavigate = jest.fn();
|
const mockNavigate = jest.fn();
|
||||||
|
@ -358,7 +354,14 @@ describe('SLOs Page', () => {
|
||||||
|
|
||||||
button.click();
|
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 })
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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.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.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.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.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.create.successNotification": "{name} créé avec succès",
|
||||||
"xpack.observability.slo.deleteConfirmationModal.title": "Supprimer {name} ?",
|
"xpack.observability.slo.deleteConfirmationModal.title": "Supprimer {name} ?",
|
||||||
|
|
|
@ -28923,8 +28923,6 @@
|
||||||
"xpack.observability.slo.alerting.burnRate.reasonForInstanceId": "{actionGroupName}:過去{longWindowDuration}のバーンレートは{longWindowBurnRate}、{instanceId}の過去{shortWindowDuration}のバーンレートは{shortWindowBurnRate}です。両期間とも{burnRateThreshold}を超えたらアラート",
|
"xpack.observability.slo.alerting.burnRate.reasonForInstanceId": "{actionGroupName}:過去{longWindowDuration}のバーンレートは{longWindowBurnRate}、{instanceId}の過去{shortWindowDuration}のバーンレートは{shortWindowBurnRate}です。両期間とも{burnRateThreshold}を超えたらアラート",
|
||||||
"xpack.observability.slo.burnRate.breachedStatustSubtitle": "現在のレートでは、エラー予算は{hour}時間後に使い果たされます。",
|
"xpack.observability.slo.burnRate.breachedStatustSubtitle": "現在のレートでは、エラー予算は{hour}時間後に使い果たされます。",
|
||||||
"xpack.observability.slo.burnRate.threshold": "しきい値は{threshold}xです",
|
"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.errorNotification": "{name}の作成中に問題が発生しました",
|
||||||
"xpack.observability.slo.create.successNotification": "{name}の作成が正常に完了しました",
|
"xpack.observability.slo.create.successNotification": "{name}の作成が正常に完了しました",
|
||||||
"xpack.observability.slo.deleteConfirmationModal.title": "{name}を削除しますか?",
|
"xpack.observability.slo.deleteConfirmationModal.title": "{name}を削除しますか?",
|
||||||
|
|
|
@ -28920,8 +28920,6 @@
|
||||||
"xpack.observability.slo.alerting.burnRate.reasonForInstanceId": "{actionGroupName}:过去 {longWindowDuration} 的消耗速度为 {longWindowBurnRate},且对于 {instanceId},过去 {shortWindowDuration} 为 {shortWindowBurnRate}。两个窗口超出 {burnRateThreshold} 时告警",
|
"xpack.observability.slo.alerting.burnRate.reasonForInstanceId": "{actionGroupName}:过去 {longWindowDuration} 的消耗速度为 {longWindowBurnRate},且对于 {instanceId},过去 {shortWindowDuration} 为 {shortWindowBurnRate}。两个窗口超出 {burnRateThreshold} 时告警",
|
||||||
"xpack.observability.slo.burnRate.breachedStatustSubtitle": "按照当前的速率,错误预算将在 {hour} 小时后耗尽。",
|
"xpack.observability.slo.burnRate.breachedStatustSubtitle": "按照当前的速率,错误预算将在 {hour} 小时后耗尽。",
|
||||||
"xpack.observability.slo.burnRate.threshold": "阈值为 {threshold}x",
|
"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.errorNotification": "创建 {name} 时出现问题",
|
||||||
"xpack.observability.slo.create.successNotification": "已成功创建 {name}",
|
"xpack.observability.slo.create.successNotification": "已成功创建 {name}",
|
||||||
"xpack.observability.slo.deleteConfirmationModal.title": "删除 {name}?",
|
"xpack.observability.slo.deleteConfirmationModal.title": "删除 {name}?",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue