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 (
|
||||
<div ref={containerRef} style={{ width: '100%' }}>
|
||||
<SloCardChart slo={slo} historicalSliData={historicalSliData ?? []} cardsPerRow={4} />
|
||||
<SloCardChart slo={slo} historicalSliData={historicalSliData ?? []} />
|
||||
<SloCardBadgesPortal containerRef={containerRef}>
|
||||
<SloCardItemBadges
|
||||
slo={slo}
|
||||
|
|
|
@ -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 });
|
||||
return useCallback(
|
||||
(slo: SLOWithSummaryResponse) => {
|
||||
navigateToUrl(
|
||||
basePath.prepend(
|
||||
paths.observability.sloCreateWithEncodedForm(
|
||||
encode({ ...slo, name: `[Copy] ${slo.name}`, id: undefined })
|
||||
)
|
||||
)
|
||||
);
|
||||
},
|
||||
{
|
||||
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 });
|
||||
},
|
||||
}
|
||||
[navigateToUrl, basePath]
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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 })
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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
|
||||
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}
|
||||
<BurnRateRuleFlyout
|
||||
slo={slo as GetSLOResponse}
|
||||
isAddRuleFlyoutOpen={isAddRuleFlyoutOpen}
|
||||
canChangeTrigger={false}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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={{
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -68,27 +68,29 @@ export function SloListCardView({
|
|||
}
|
||||
|
||||
return (
|
||||
<EuiFlexGrid columns={columns}>
|
||||
{sloList.map((slo) => (
|
||||
<EuiFlexItem key={`${slo.id}-${slo.instanceId ?? 'ALL_VALUE'}`}>
|
||||
<SloCardItem
|
||||
slo={slo}
|
||||
loading={loading}
|
||||
error={error}
|
||||
activeAlerts={activeAlertsBySlo.get(slo)}
|
||||
rules={rulesBySlo?.[slo.id]}
|
||||
historicalSummary={
|
||||
historicalSummaries.find(
|
||||
(historicalSummary) =>
|
||||
historicalSummary.sloId === slo.id &&
|
||||
historicalSummary.instanceId === (slo.instanceId ?? ALL_VALUE)
|
||||
)?.data
|
||||
}
|
||||
historicalSummaryLoading={historicalSummaryLoading}
|
||||
cardsPerRow={Number(cardsPerRow)}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
))}
|
||||
<EuiFlexGrid columns={columns} gutterSize="m">
|
||||
{sloList
|
||||
.filter((slo) => slo.summary)
|
||||
.map((slo) => (
|
||||
<EuiFlexItem key={`${slo.id}-${slo.instanceId ?? 'ALL_VALUE'}`}>
|
||||
<SloCardItem
|
||||
slo={slo}
|
||||
loading={loading}
|
||||
error={error}
|
||||
activeAlerts={activeAlertsBySlo.get(slo)}
|
||||
rules={rulesBySlo?.[slo.id]}
|
||||
historicalSummary={
|
||||
historicalSummaries.find(
|
||||
(historicalSummary) =>
|
||||
historicalSummary.sloId === slo.id &&
|
||||
historicalSummary.instanceId === (slo.instanceId ?? ALL_VALUE)
|
||||
)?.data
|
||||
}
|
||||
historicalSummaryLoading={historicalSummaryLoading}
|
||||
cardsPerRow={Number(cardsPerRow)}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
))}
|
||||
</EuiFlexGrid>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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 () => {
|
||||
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
|
||||
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;
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 })
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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} ?",
|
||||
|
|
|
@ -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}を削除しますか?",
|
||||
|
|
|
@ -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}?",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue