feat(slo): Add enable/disable actions (#205518)

This commit is contained in:
Kevin Delemme 2025-01-07 12:25:22 -05:00 committed by GitHub
parent a8579bb41f
commit 0ed641ef4f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 755 additions and 87 deletions

View file

@ -0,0 +1,45 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { EuiConfirmModal } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { SLODefinitionResponse, SLOWithSummaryResponse } from '@kbn/slo-schema';
import React from 'react';
export interface Props {
slo: SLOWithSummaryResponse | SLODefinitionResponse;
onCancel: () => void;
onConfirm: () => void;
isLoading?: boolean;
}
export function SloDisableConfirmationModal({ slo, onCancel, onConfirm, isLoading }: Props) {
const { name } = slo;
return (
<EuiConfirmModal
buttonColor="primary"
data-test-subj="sloDisableConfirmationModal"
title={i18n.translate('xpack.slo.disableConfirmationModal.title', {
defaultMessage: 'Disable {name}?',
values: { name },
})}
cancelButtonText={i18n.translate('xpack.slo.disableConfirmationModal.cancelButtonLabel', {
defaultMessage: 'Cancel',
})}
confirmButtonText={i18n.translate('xpack.slo.disableConfirmationModal.disableButtonLabel', {
defaultMessage: 'Disable',
})}
onCancel={onCancel}
onConfirm={onConfirm}
isLoading={isLoading}
>
{i18n.translate('xpack.slo.disableConfirmationModal.descriptionText', {
defaultMessage: 'Disabling this SLO will stop generating data until it is enabled again.',
})}
</EuiConfirmModal>
);
}

View file

@ -0,0 +1,45 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { EuiConfirmModal } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { SLODefinitionResponse, SLOWithSummaryResponse } from '@kbn/slo-schema';
import React from 'react';
export interface Props {
slo: SLOWithSummaryResponse | SLODefinitionResponse;
onCancel: () => void;
onConfirm: () => void;
isLoading?: boolean;
}
export function SloEnableConfirmationModal({ slo, onCancel, onConfirm, isLoading }: Props) {
const { name } = slo;
return (
<EuiConfirmModal
buttonColor="primary"
data-test-subj="sloEnableConfirmationModal"
title={i18n.translate('xpack.slo.enableConfirmationModal.title', {
defaultMessage: 'Enable {name}?',
values: { name },
})}
cancelButtonText={i18n.translate('xpack.slo.enableConfirmationModal.cancelButtonLabel', {
defaultMessage: 'Cancel',
})}
confirmButtonText={i18n.translate('xpack.slo.enableConfirmationModal.enableButtonLabel', {
defaultMessage: 'Enable',
})}
onCancel={onCancel}
onConfirm={onConfirm}
isLoading={isLoading}
>
{i18n.translate('xpack.slo.enableConfirmationModal.descriptionText', {
defaultMessage: 'Enabling this SLO will generate the missing data since it was disabled.',
})}
</EuiConfirmModal>
);
}

View file

@ -6,3 +6,5 @@
*/
export { SloStatusBadge } from './slo_status_badge';
export { SloActiveAlertsBadge } from './slo_active_alerts_badge';
export { SloStateBadge } from './slo_state_badge';

View file

@ -0,0 +1,39 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React from 'react';
import { EuiBadge, EuiFlexItem, EuiToolTip } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { SLOWithSummaryResponse } from '@kbn/slo-schema';
export interface Props {
slo: SLOWithSummaryResponse;
}
export function SloStateBadge({ slo }: Props) {
const isEnabled = slo.enabled;
if (isEnabled) {
return null;
}
return (
<EuiFlexItem grow={false}>
<EuiToolTip
position="top"
content={i18n.translate('xpack.slo.sloStateBadge.disabled.tooltip', {
defaultMessage: 'This SLO is disabled. Enable it to start processing data.',
})}
>
<EuiBadge color="default">
{i18n.translate('xpack.slo.sloStateBadge.disabled.label', {
defaultMessage: 'Disabled',
})}
</EuiBadge>
</EuiToolTip>
</EuiFlexItem>
);
}

View file

@ -95,8 +95,6 @@ export function SloOverview({ sloId, sloInstanceId, remoteName, reloadSubject }:
const rules = rulesBySlo?.[slo?.id];
const activeAlerts = activeAlertsBySlo.get(slo);
const hasGroupBy = Boolean(slo.groupBy && slo.groupBy !== ALL_VALUE);
const historicalSummary = historicalSummaries.find(
(histSummary) =>
histSummary.sloId === slo.id && histSummary.instanceId === (slo.instanceId ?? ALL_VALUE)
@ -112,14 +110,7 @@ export function SloOverview({ sloId, sloInstanceId, remoteName, reloadSubject }:
onClick={() => {
setSelectedSlo(slo);
}}
badges={
<SloCardItemBadges
slo={slo}
rules={rules}
activeAlerts={activeAlerts}
hasGroupBy={hasGroupBy}
/>
}
badges={<SloCardItemBadges slo={slo} rules={rules} activeAlerts={activeAlerts} />}
/>
<SloOverviewDetails slo={selectedSlo} setSelectedSlo={setSelectedSlo} />
</div>

View file

@ -125,7 +125,6 @@ export function SloCardChartList({ sloId }: { sloId: string }) {
const rules = rulesBySlo?.[slo?.id];
const activeAlerts = activeAlertsBySlo.get(slo);
const hasGroupBy = Boolean(slo.groupBy && slo.groupBy !== ALL_VALUE);
const data = getSloChartData({
slo,
@ -141,7 +140,6 @@ export function SloCardChartList({ sloId }: { sloId: string }) {
rules={rules}
activeAlerts={activeAlerts}
handleCreateRule={() => {}}
hasGroupBy={hasGroupBy}
/>
);
chartsData[chartsData.length - 1].push(data);

View file

@ -0,0 +1,57 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { IHttpFetchError, ResponseErrorBody } from '@kbn/core/public';
import { i18n } from '@kbn/i18n';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useKibana } from './use_kibana';
import { sloKeys } from './query_key_factory';
import { usePluginContext } from './use_plugin_context';
type ServerError = IHttpFetchError<ResponseErrorBody>;
export function useDisableSlo() {
const {
notifications: { toasts },
} = useKibana().services;
const { sloClient } = usePluginContext();
const queryClient = useQueryClient();
return useMutation<void, ServerError, { id: string; name: string }>(
['disableSlo'],
({ id }) => {
try {
return sloClient.fetch(`POST /api/observability/slos/{id}/disable 2023-10-31`, {
params: { path: { id } },
});
} catch (error) {
return Promise.reject(`Something went wrong: ${String(error)}`);
}
},
{
onError: (error, { name }) => {
toasts.addError(new Error(error.body?.message ?? error.message), {
title: i18n.translate('xpack.slo.disable.errorNotification', {
defaultMessage: 'Failed to disable {name}',
values: { name },
}),
});
},
onSuccess: (_data, { name }) => {
queryClient.invalidateQueries({ queryKey: sloKeys.lists(), exact: false });
queryClient.invalidateQueries({ queryKey: sloKeys.details(), exact: false });
toasts.addSuccess(
i18n.translate('xpack.slo.disable.successNotification', {
defaultMessage: 'Disabled {name}',
values: { name },
})
);
},
}
);
}

View file

@ -0,0 +1,57 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { IHttpFetchError, ResponseErrorBody } from '@kbn/core/public';
import { i18n } from '@kbn/i18n';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useKibana } from './use_kibana';
import { sloKeys } from './query_key_factory';
import { usePluginContext } from './use_plugin_context';
type ServerError = IHttpFetchError<ResponseErrorBody>;
export function useEnableSlo() {
const {
notifications: { toasts },
} = useKibana().services;
const { sloClient } = usePluginContext();
const queryClient = useQueryClient();
return useMutation<void, ServerError, { id: string; name: string }>(
['enableSlo'],
({ id }) => {
try {
return sloClient.fetch(`POST /api/observability/slos/{id}/enable 2023-10-31`, {
params: { path: { id } },
});
} catch (error) {
return Promise.reject(`Something went wrong: ${String(error)}`);
}
},
{
onError: (error, { name }) => {
toasts.addError(new Error(error.body?.message ?? error.message), {
title: i18n.translate('xpack.slo.enable.errorNotification', {
defaultMessage: 'Failed to enable {name}',
values: { name },
}),
});
},
onSuccess: (_data, { name }) => {
queryClient.invalidateQueries({ queryKey: sloKeys.lists(), exact: false });
queryClient.invalidateQueries({ queryKey: sloKeys.details(), exact: false });
toasts.addSuccess(
i18n.translate('xpack.slo.enable.successNotification', {
defaultMessage: 'Enabled {name}',
values: { name },
})
);
},
}
);
}

View file

@ -30,6 +30,10 @@ import { isApmIndicatorType } from '../../../utils/slo/indicator';
import { EditBurnRateRuleFlyout } from '../../slos/components/common/edit_burn_rate_rule_flyout';
import { useGetQueryParams } from '../hooks/use_get_query_params';
import { useSloActions } from '../hooks/use_slo_actions';
import { SloDisableConfirmationModal } from '../../../components/slo/disable_confirmation_modal/slo_disable_confirmation_modal';
import { SloEnableConfirmationModal } from '../../../components/slo/enable_confirmation_modal/slo_enable_confirmation_modal';
import { useDisableSlo } from '../../../hooks/use_disable_slo';
import { useEnableSlo } from '../../../hooks/use_enable_slo';
export interface Props {
slo: SLOWithSummaryResponse;
@ -45,16 +49,28 @@ export function HeaderControl({ slo }: Props) {
const hasApmReadCapabilities = capabilities.apm.show;
const { data: permissions } = usePermissions();
const { isDeletingSlo, isResettingSlo, removeDeleteQueryParam, removeResetQueryParam } =
useGetQueryParams();
const {
isDeletingSlo,
isResettingSlo,
isEnablingSlo,
isDisablingSlo,
removeDeleteQueryParam,
removeResetQueryParam,
removeEnableQueryParam,
removeDisableQueryParam,
} = useGetQueryParams();
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
const [isRuleFlyoutVisible, setRuleFlyoutVisibility] = useState<boolean>(false);
const [isEditRuleFlyoutOpen, setIsEditRuleFlyoutOpen] = useState(false);
const [isDeleteConfirmationModalOpen, setDeleteConfirmationModalOpen] = useState(false);
const [isResetConfirmationModalOpen, setResetConfirmationModalOpen] = useState(false);
const [isEnableConfirmationModalOpen, setEnableConfirmationModalOpen] = useState(false);
const [isDisableConfirmationModalOpen, setDisableConfirmationModalOpen] = useState(false);
const { mutateAsync: resetSlo, isLoading: isResetLoading } = useResetSlo();
const { mutate: resetSlo, isLoading: isResetLoading } = useResetSlo();
const { mutate: enableSlo, isLoading: isEnableLoading } = useEnableSlo();
const { mutate: disableSlo, isLoading: isDisableLoading } = useDisableSlo();
const { data: rulesBySlo, refetchRules } = useFetchRulesForSlo({
sloIds: [slo.id],
@ -72,7 +88,13 @@ export function HeaderControl({ slo }: Props) {
if (isResettingSlo) {
setResetConfirmationModalOpen(true);
}
}, [isDeletingSlo, isResettingSlo]);
if (isEnablingSlo) {
setEnableConfirmationModalOpen(true);
}
if (isDisablingSlo) {
setDisableConfirmationModalOpen(true);
}
}, [isDeletingSlo, isResettingSlo, isEnablingSlo, isDisablingSlo]);
const onCloseRuleFlyout = () => {
setRuleFlyoutVisibility(false);
@ -83,7 +105,14 @@ export function HeaderControl({ slo }: Props) {
setRuleFlyoutVisibility(true);
};
const { handleNavigateToRules, sloEditUrl, remoteDeleteUrl, remoteResetUrl } = useSloActions({
const {
handleNavigateToRules,
sloEditUrl,
remoteDeleteUrl,
remoteResetUrl,
remoteEnableUrl,
remoteDisableUrl,
} = useSloActions({
slo,
rules,
setIsEditRuleFlyoutOpen,
@ -129,11 +158,12 @@ export function HeaderControl({ slo }: Props) {
window.open(remoteResetUrl, '_blank');
} else {
setResetConfirmationModalOpen(true);
setIsPopoverOpen(false);
}
};
const handleResetConfirm = async () => {
await resetSlo({ id: slo.id, name: slo.name });
const handleResetConfirm = () => {
resetSlo({ id: slo.id, name: slo.name });
removeResetQueryParam();
setResetConfirmationModalOpen(false);
};
@ -143,6 +173,42 @@ export function HeaderControl({ slo }: Props) {
setResetConfirmationModalOpen(false);
};
const handleEnable = () => {
if (!!remoteEnableUrl) {
window.open(remoteEnableUrl, '_blank');
} else {
setEnableConfirmationModalOpen(true);
setIsPopoverOpen(false);
}
};
const handleEnableCancel = () => {
removeEnableQueryParam();
setEnableConfirmationModalOpen(false);
};
const handleEnableConfirm = () => {
enableSlo({ id: slo.id, name: slo.name });
removeEnableQueryParam();
setEnableConfirmationModalOpen(false);
};
const handleDisable = () => {
if (!!remoteDisableUrl) {
window.open(remoteDisableUrl, '_blank');
} else {
setDisableConfirmationModalOpen(true);
setIsPopoverOpen(false);
}
};
const handleDisableCancel = () => {
removeDisableQueryParam();
setDisableConfirmationModalOpen(false);
};
const handleDisableConfirm = () => {
disableSlo({ id: slo.id, name: slo.name });
removeDisableQueryParam();
setDisableConfirmationModalOpen(false);
};
const navigate = useCallback(
(url: string) => setTimeout(() => navigateToUrl(url)),
[navigateToUrl]
@ -249,6 +315,35 @@ export function HeaderControl({ slo }: Props) {
)
)
.concat(
slo.enabled ? (
<EuiContextMenuItem
key="disable"
icon="stop"
disabled={!permissions?.hasAllWriteRequested || hasUndefinedRemoteKibanaUrl}
onClick={handleDisable}
toolTipContent={
hasUndefinedRemoteKibanaUrl ? NOT_AVAILABLE_FOR_UNDEFINED_REMOTE_KIBANA_URL : ''
}
data-test-subj="sloActionsDisable"
>
{i18n.translate('xpack.slo.item.actions.disable', { defaultMessage: 'Disable' })}
{showRemoteLinkIcon}
</EuiContextMenuItem>
) : (
<EuiContextMenuItem
key="enable"
icon="play"
disabled={!permissions?.hasAllWriteRequested || hasUndefinedRemoteKibanaUrl}
onClick={handleEnable}
toolTipContent={
hasUndefinedRemoteKibanaUrl ? NOT_AVAILABLE_FOR_UNDEFINED_REMOTE_KIBANA_URL : ''
}
data-test-subj="sloActionsEnable"
>
{i18n.translate('xpack.slo.item.actions.enable', { defaultMessage: 'Enable' })}
{showRemoteLinkIcon}
</EuiContextMenuItem>
),
<EuiContextMenuItem
key="clone"
disabled={!permissions?.hasAllWriteRequested || hasUndefinedRemoteKibanaUrl}
@ -327,6 +422,24 @@ export function HeaderControl({ slo }: Props) {
isLoading={isResetLoading}
/>
) : null}
{isEnableConfirmationModalOpen ? (
<SloEnableConfirmationModal
slo={slo}
onCancel={handleEnableCancel}
onConfirm={handleEnableConfirm}
isLoading={isEnableLoading}
/>
) : null}
{isDisableConfirmationModalOpen ? (
<SloDisableConfirmationModal
slo={slo}
onCancel={handleDisableCancel}
onConfirm={handleDisableConfirm}
isLoading={isDisableLoading}
/>
) : null}
</>
);
}

View file

@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n';
import { SLOWithSummaryResponse } from '@kbn/slo-schema';
import moment from 'moment';
import React from 'react';
import { SloStatusBadge } from '../../../components/slo/slo_status_badge';
import { SloStateBadge, SloStatusBadge } from '../../../components/slo/slo_badges';
import { SloRemoteBadge } from '../../slos/components/badges/slo_remote_badge';
import { SLOGroupings } from './groupings/slo_groupings';
@ -35,6 +35,7 @@ export function HeaderTitle({ isLoading, slo }: Props) {
wrap={true}
>
<SloStatusBadge slo={slo} />
<SloStateBadge slo={slo} />
<SloRemoteBadge slo={slo} />
<EuiFlexItem grow={false}>
<EuiText color="subdued" size="xs">

View file

@ -13,6 +13,8 @@ export const INSTANCE_SEARCH_PARAM = 'instanceId';
export const REMOTE_NAME_PARAM = 'remoteName';
export const DELETE_SLO = 'delete';
export const RESET_SLO = 'reset';
export const ENABLE_SLO = 'enable';
export const DISABLE_SLO = 'disable';
export function useGetQueryParams() {
const { search, pathname } = useLocation();
@ -23,6 +25,8 @@ export function useGetQueryParams() {
const remoteName = searchParams.get(REMOTE_NAME_PARAM);
const deleteSlo = searchParams.get(DELETE_SLO);
const resetSlo = searchParams.get(RESET_SLO);
const enableSlo = searchParams.get(ENABLE_SLO);
const disableSlo = searchParams.get(DISABLE_SLO);
const removeDeleteQueryParam = useCallback(() => {
const qParams = new URLSearchParams(search);
@ -50,6 +54,32 @@ export function useGetQueryParams() {
}
}, [resetSlo, history, pathname, search]);
const removeEnableQueryParam = useCallback(() => {
const qParams = new URLSearchParams(search);
// remote enable param from url after initial load
if (enableSlo === 'true') {
qParams.delete(ENABLE_SLO);
history.replace({
pathname,
search: qParams.toString(),
});
}
}, [enableSlo, history, pathname, search]);
const removeDisableQueryParam = useCallback(() => {
const qParams = new URLSearchParams(search);
// remote disable param from url after initial load
if (disableSlo === 'true') {
qParams.delete(DISABLE_SLO);
history.replace({
pathname,
search: qParams.toString(),
});
}
}, [disableSlo, history, pathname, search]);
return {
instanceId: !!instanceId && instanceId !== ALL_VALUE ? instanceId : undefined,
remoteName: remoteName !== null ? remoteName : undefined,
@ -57,5 +87,9 @@ export function useGetQueryParams() {
removeDeleteQueryParam,
isResettingSlo: resetSlo === 'true',
removeResetQueryParam,
isEnablingSlo: enableSlo === 'true',
removeEnableQueryParam,
isDisablingSlo: disableSlo === 'true',
removeDisableQueryParam,
};
}

View file

@ -15,21 +15,25 @@ import { BurnRateRuleParams } from '../../../typings';
import { useKibana } from '../../../hooks/use_kibana';
import {
createRemoteSloDeleteUrl,
createRemoteSloDisableUrl,
createRemoteSloEditUrl,
createRemoteSloEnableUrl,
createRemoteSloResetUrl,
} from '../../../utils/slo/remote_slo_urls';
interface Props {
slo?: SLOWithSummaryResponse;
rules?: Array<Rule<BurnRateRuleParams>>;
setIsEditRuleFlyoutOpen: (val: boolean) => void;
setIsActionsPopoverOpen: (val: boolean) => void;
}
export const useSloActions = ({
slo,
rules,
setIsEditRuleFlyoutOpen,
setIsActionsPopoverOpen,
}: {
slo?: SLOWithSummaryResponse;
rules?: Array<Rule<BurnRateRuleParams>>;
setIsEditRuleFlyoutOpen: (val: boolean) => void;
setIsActionsPopoverOpen: (val: boolean) => void;
}) => {
}: Props) => {
const {
share: {
url: { locators },
@ -44,6 +48,8 @@ export const useSloActions = ({
handleNavigateToRules: () => {},
remoteDeleteUrl: undefined,
remoteResetUrl: undefined,
remoteEnableUrl: undefined,
remoteDisableUrl: undefined,
sloDetailsUrl: '',
};
}
@ -79,6 +85,8 @@ export const useSloActions = ({
const remoteDeleteUrl = createRemoteSloDeleteUrl(slo, spaceId);
const remoteResetUrl = createRemoteSloResetUrl(slo, spaceId);
const remoteEnableUrl = createRemoteSloEnableUrl(slo, spaceId);
const remoteDisableUrl = createRemoteSloDisableUrl(slo, spaceId);
const sloEditUrl = slo.remote
? createRemoteSloEditUrl(slo, spaceId)
@ -89,6 +97,8 @@ export const useSloActions = ({
handleNavigateToRules,
remoteDeleteUrl,
remoteResetUrl,
remoteEnableUrl,
remoteDisableUrl,
sloDetailsUrl: http.basePath.prepend(detailsUrl),
};
};

View file

@ -9,8 +9,8 @@ import { EuiFlexGroup, EuiSkeletonRectangle } from '@elastic/eui';
import { SLOWithSummaryResponse } from '@kbn/slo-schema';
import { Rule } from '@kbn/triggers-actions-ui-plugin/public';
import React from 'react';
import { SloStatusBadge } from '../../../../components/slo/slo_status_badge';
import { SloActiveAlertsBadge } from '../../../../components/slo/slo_status_badge/slo_active_alerts_badge';
import { SloStateBadge, SloStatusBadge } from '../../../../components/slo/slo_badges';
import { SloActiveAlertsBadge } from '../../../../components/slo/slo_badges/slo_active_alerts_badge';
import { BurnRateRuleParams } from '../../../../typings';
import { SloTagsList } from '../common/slo_tags_list';
import { SloIndicatorTypeBadge } from './slo_indicator_type_badge';
@ -42,6 +42,7 @@ export function SloBadges({
) : (
<>
<SloStatusBadge slo={slo} />
<SloStateBadge slo={slo} />
<SloActiveAlertsBadge slo={slo} activeAlerts={activeAlerts} />
<SloIndicatorTypeBadge slo={slo} />
<SloTimeWindowBadge slo={slo} />

View file

@ -19,7 +19,11 @@ import { Rule } from '@kbn/triggers-actions-ui-plugin/public';
import moment from 'moment';
import React, { useState } from 'react';
import { SloDeleteModal } from '../../../../components/slo/delete_confirmation_modal/slo_delete_confirmation_modal';
import { SloDisableConfirmationModal } from '../../../../components/slo/disable_confirmation_modal/slo_disable_confirmation_modal';
import { SloEnableConfirmationModal } from '../../../../components/slo/enable_confirmation_modal/slo_enable_confirmation_modal';
import { SloResetConfirmationModal } from '../../../../components/slo/reset_confirmation_modal/slo_reset_confirmation_modal';
import { useDisableSlo } from '../../../../hooks/use_disable_slo';
import { useEnableSlo } from '../../../../hooks/use_enable_slo';
import { useKibana } from '../../../../hooks/use_kibana';
import { useResetSlo } from '../../../../hooks/use_reset_slo';
import { BurnRateRuleParams } from '../../../../typings';
@ -73,6 +77,8 @@ export function SloCardItem({ slo, rules, activeAlerts, historicalSummary, refet
const [isEditRuleFlyoutOpen, setIsEditRuleFlyoutOpen] = useState(false);
const [isDeleteConfirmationModalOpen, setDeleteConfirmationModalOpen] = useState(false);
const [isResetConfirmationModalOpen, setResetConfirmationModalOpen] = useState(false);
const [isEnableConfirmationModalOpen, setEnableConfirmationModalOpen] = useState(false);
const [isDisableConfirmationModalOpen, setDisableConfirmationModalOpen] = useState(false);
const [isDashboardAttachmentReady, setDashboardAttachmentReady] = useState(false);
const historicalSliData = formatHistoricalData(historicalSummary, 'sli_value');
@ -87,10 +93,12 @@ export function SloCardItem({ slo, rules, activeAlerts, historicalSummary, refet
setDeleteConfirmationModalOpen(false);
};
const { mutateAsync: resetSlo, isLoading: isResetLoading } = useResetSlo();
const { mutate: resetSlo, isLoading: isResetLoading } = useResetSlo();
const { mutate: enableSlo, isLoading: isEnableLoading } = useEnableSlo();
const { mutate: disableSlo, isLoading: isDisableLoading } = useDisableSlo();
const handleResetConfirm = async () => {
await resetSlo({ id: slo.id, name: slo.name });
const handleResetConfirm = () => {
resetSlo({ id: slo.id, name: slo.name });
setResetConfirmationModalOpen(false);
};
@ -98,6 +106,22 @@ export function SloCardItem({ slo, rules, activeAlerts, historicalSummary, refet
setResetConfirmationModalOpen(false);
};
const handleEnableCancel = () => {
setEnableConfirmationModalOpen(false);
};
const handleEnableConfirm = () => {
enableSlo({ id: slo.id, name: slo.name });
setEnableConfirmationModalOpen(false);
};
const handleDisableCancel = () => {
setDisableConfirmationModalOpen(false);
};
const handleDisableConfirm = () => {
disableSlo({ id: slo.id, name: slo.name });
setDisableConfirmationModalOpen(false);
};
return (
<>
<EuiPanel
@ -144,7 +168,6 @@ export function SloCardItem({ slo, rules, activeAlerts, historicalSummary, refet
rules={rules}
activeAlerts={activeAlerts}
handleCreateRule={handleCreateRule}
hasGroupBy={Boolean(slo.groupBy && slo.groupBy !== ALL_VALUE)}
/>
}
/>
@ -159,6 +182,8 @@ export function SloCardItem({ slo, rules, activeAlerts, historicalSummary, refet
setIsEditRuleFlyoutOpen={setIsEditRuleFlyoutOpen}
setDashboardAttachmentReady={setDashboardAttachmentReady}
setResetConfirmationModalOpen={setResetConfirmationModalOpen}
setEnableConfirmationModalOpen={setEnableConfirmationModalOpen}
setDisableConfirmationModalOpen={setDisableConfirmationModalOpen}
/>
</div>
</EuiPanel>
@ -189,6 +214,24 @@ export function SloCardItem({ slo, rules, activeAlerts, historicalSummary, refet
/>
) : null}
{isEnableConfirmationModalOpen ? (
<SloEnableConfirmationModal
slo={slo}
onCancel={handleEnableCancel}
onConfirm={handleEnableConfirm}
isLoading={isEnableLoading}
/>
) : null}
{isDisableConfirmationModalOpen ? (
<SloDisableConfirmationModal
slo={slo}
onCancel={handleDisableCancel}
onConfirm={handleDisableConfirm}
isLoading={isDisableLoading}
/>
) : null}
{isDashboardAttachmentReady ? (
<SavedObjectSaveModalDashboard
objectType={i18n.translate('xpack.slo.item.actions.addToDashboard.objectTypeLabel', {

View file

@ -18,6 +18,8 @@ interface Props {
setIsActionsPopoverOpen: (value: boolean) => void;
setDeleteConfirmationModalOpen: (value: boolean) => void;
setResetConfirmationModalOpen: (value: boolean) => void;
setEnableConfirmationModalOpen: (value: boolean) => void;
setDisableConfirmationModalOpen: (value: boolean) => void;
setIsAddRuleFlyoutOpen: (value: boolean) => void;
setIsEditRuleFlyoutOpen: (value: boolean) => void;
setDashboardAttachmentReady: (value: boolean) => void;

View file

@ -6,23 +6,21 @@
*/
import { EuiFlexGroup } from '@elastic/eui';
import { css } from '@emotion/react';
import { SLOWithSummaryResponse } from '@kbn/slo-schema';
import { Rule } from '@kbn/triggers-actions-ui-plugin/public';
import React, { useCallback } from 'react';
import { css } from '@emotion/react';
import { SloIndicatorTypeBadge } from '../badges/slo_indicator_type_badge';
import { SloActiveAlertsBadge } from '../../../../components/slo/slo_status_badge/slo_active_alerts_badge';
import { SloStateBadge } from '../../../../components/slo/slo_badges';
import { SloActiveAlertsBadge } from '../../../../components/slo/slo_badges/slo_active_alerts_badge';
import { BurnRateRuleParams } from '../../../../typings';
import { useUrlSearchState } from '../../hooks/use_url_search_state';
import { LoadingBadges } from '../badges/slo_badges';
import { SloRemoteBadge } from '../badges/slo_remote_badge';
import { SloRulesBadge } from '../badges/slo_rules_badge';
import { SloTimeWindowBadge } from '../badges/slo_time_window_badge';
import { SloTagsList } from '../common/slo_tags_list';
import { SLOCardItemInstanceBadge } from './slo_card_item_instance_badge';
interface Props {
hasGroupBy: boolean;
activeAlerts?: number;
slo: SLOWithSummaryResponse;
rules: Array<Rule<BurnRateRuleParams>> | undefined;
@ -62,11 +60,10 @@ export function SloCardItemBadges({ slo, activeAlerts, rules, handleCreateRule }
<LoadingBadges />
) : (
<>
<SloStateBadge slo={slo} />
<SloActiveAlertsBadge slo={slo} activeAlerts={activeAlerts} viewMode="compact" />
<SloIndicatorTypeBadge slo={slo} color="default" />
<SLOCardItemInstanceBadge slo={slo} />
<SloRulesBadge rules={rules} onClick={handleCreateRule} isRemote={!!slo.remote} />
<SloTimeWindowBadge slo={slo} color="default" />
<SloRemoteBadge slo={slo} />
<SloTagsList
tags={slo.tags}

View file

@ -4,7 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React, { useState } from 'react';
import React, { useState, MouseEvent } from 'react';
import { EuiBadge, EuiFlexItem, EuiPopover } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { SLOWithSummaryResponse } from '@kbn/slo-schema';
@ -32,6 +32,9 @@ export function SLOCardItemInstanceBadge({ slo }: Props) {
onClick={() => {
setIsPopoverOpen(!isPopoverOpen);
}}
onMouseDown={(e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation(); // stops propagation of metric onElementClick
}}
onClickAriaLabel={i18n.translate('xpack.slo.instances.seeAllBadge', {
defaultMessage: 'see all instance ids',
})}
@ -46,7 +49,9 @@ export function SLOCardItemInstanceBadge({ slo }: Props) {
</EuiBadge>
}
>
<SLOGroupings slo={slo} direction="column" truncate={false} />
<div onMouseDownCapture={(e: MouseEvent<HTMLDivElement>) => e.stopPropagation()}>
<SLOGroupings slo={slo} direction="column" truncate={false} />
</div>
</EuiPopover>
</EuiFlexItem>
);

View file

@ -24,23 +24,29 @@ import React, { useState } from 'react';
import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n';
import { paths } from '../../../../../common/locators/paths';
import { SloDeleteModal } from '../../../../components/slo/delete_confirmation_modal/slo_delete_confirmation_modal';
import { SloDisableConfirmationModal } from '../../../../components/slo/disable_confirmation_modal/slo_disable_confirmation_modal';
import { SloEnableConfirmationModal } from '../../../../components/slo/enable_confirmation_modal/slo_enable_confirmation_modal';
import { SloResetConfirmationModal } from '../../../../components/slo/reset_confirmation_modal/slo_reset_confirmation_modal';
import { SloStatusBadge } from '../../../../components/slo/slo_status_badge';
import { SloActiveAlertsBadge } from '../../../../components/slo/slo_status_badge/slo_active_alerts_badge';
import { SloStateBadge, SloStatusBadge } from '../../../../components/slo/slo_badges';
import { SloActiveAlertsBadge } from '../../../../components/slo/slo_badges/slo_active_alerts_badge';
import { sloKeys } from '../../../../hooks/query_key_factory';
import { useCloneSlo } from '../../../../hooks/use_clone_slo';
import { useDisableSlo } from '../../../../hooks/use_disable_slo';
import { useEnableSlo } from '../../../../hooks/use_enable_slo';
import { useFetchActiveAlerts } from '../../../../hooks/use_fetch_active_alerts';
import { useFetchHistoricalSummary } from '../../../../hooks/use_fetch_historical_summary';
import { useFetchRulesForSlo } from '../../../../hooks/use_fetch_rules_for_slo';
import { useGetFilteredRuleTypes } from '../../../../hooks/use_get_filtered_rule_types';
import { useKibana } from '../../../../hooks/use_kibana';
import { usePermissions } from '../../../../hooks/use_permissions';
import { useResetSlo } from '../../../../hooks/use_reset_slo';
import { useSpace } from '../../../../hooks/use_space';
import { useKibana } from '../../../../hooks/use_kibana';
import { formatHistoricalData } from '../../../../utils/slo/chart_data_formatter';
import {
createRemoteSloDeleteUrl,
createRemoteSloDisableUrl,
createRemoteSloEditUrl,
createRemoteSloEnableUrl,
createRemoteSloResetUrl,
} from '../../../../utils/slo/remote_slo_urls';
import { SloRemoteBadge } from '../badges/slo_remote_badge';
@ -78,11 +84,15 @@ export function SloListCompactView({ sloList, loading, error }: Props) {
const filteredRuleTypes = useGetFilteredRuleTypes();
const queryClient = useQueryClient();
const { mutateAsync: resetSlo, isLoading: isResetLoading } = useResetSlo();
const { mutate: resetSlo, isLoading: isResetLoading } = useResetSlo();
const { mutate: enableSlo, isLoading: isEnableLoading } = useEnableSlo();
const { mutate: disableSlo, isLoading: isDisableLoading } = useDisableSlo();
const [sloToAddRule, setSloToAddRule] = useState<SLOWithSummaryResponse | undefined>(undefined);
const [sloToDelete, setSloToDelete] = useState<SLOWithSummaryResponse | undefined>(undefined);
const [sloToReset, setSloToReset] = useState<SLOWithSummaryResponse | undefined>(undefined);
const [sloToEnable, setSloToEnable] = useState<SLOWithSummaryResponse | undefined>(undefined);
const [sloToDisable, setSloToDisable] = useState<SLOWithSummaryResponse | undefined>(undefined);
const handleDeleteConfirm = () => {
setSloToDelete(undefined);
@ -92,9 +102,9 @@ export function SloListCompactView({ sloList, loading, error }: Props) {
setSloToDelete(undefined);
};
const handleResetConfirm = async () => {
const handleResetConfirm = () => {
if (sloToReset) {
await resetSlo({ id: sloToReset.id, name: sloToReset.name });
resetSlo({ id: sloToReset.id, name: sloToReset.name });
setSloToReset(undefined);
}
};
@ -103,6 +113,28 @@ export function SloListCompactView({ sloList, loading, error }: Props) {
setSloToReset(undefined);
};
const handleEnableConfirm = async () => {
if (sloToEnable) {
enableSlo({ id: sloToEnable.id, name: sloToEnable.name });
setSloToEnable(undefined);
}
};
const handleEnableCancel = () => {
setSloToEnable(undefined);
};
const handleDisableConfirm = async () => {
if (sloToDisable) {
disableSlo({ id: sloToDisable.id, name: sloToDisable.name });
setSloToDisable(undefined);
}
};
const handleDisableCancel = () => {
setSloToDisable(undefined);
};
const handleSavedRule = async () => {
queryClient.invalidateQueries({ queryKey: sloKeys.rules(), exact: false });
};
@ -217,6 +249,46 @@ export function SloListCompactView({ sloList, loading, error }: Props) {
locator?.navigate({ params: { sloId: slo.id } }, { replace: false });
},
},
{
type: 'icon',
icon: (slo: SLOWithSummaryResponse) => (slo.enabled ? 'stop' : 'play'),
name: (slo: SLOWithSummaryResponse) =>
buildActionName(
slo.enabled
? i18n.translate('xpack.slo.item.actions.disable', {
defaultMessage: 'Disable',
})
: i18n.translate('xpack.slo.item.actions.enable', {
defaultMessage: 'Enable',
})
)(slo),
description: (slo: SLOWithSummaryResponse) =>
slo.enabled
? i18n.translate('xpack.slo.item.actions.disable', {
defaultMessage: 'Disable',
})
: i18n.translate('xpack.slo.item.actions.enable', {
defaultMessage: 'Enable',
}),
'data-test-subj': 'sloActionsManage',
enabled: (slo: SLOWithSummaryResponse) =>
(permissions?.hasAllWriteRequested && !isRemote(slo)) || hasRemoteKibanaUrl(slo),
onClick: (slo: SLOWithSummaryResponse) => {
const isEnabled = slo.enabled;
const remoteUrl = isEnabled
? createRemoteSloDisableUrl(slo, spaceId)
: createRemoteSloEnableUrl(slo, spaceId);
if (!!remoteUrl) {
window.open(remoteUrl, '_blank');
} else {
if (isEnabled) {
setSloToDisable(slo);
} else {
setSloToEnable(slo);
}
}
},
},
{
type: 'icon',
icon: 'copy',
@ -290,6 +362,7 @@ export function SloListCompactView({ sloList, loading, error }: Props) {
render: (_, slo: SLOWithSummaryResponse) => (
<EuiFlexGroup direction="row" gutterSize="s">
<SloStatusBadge slo={slo} />
<SloStateBadge slo={slo} />
<SloRemoteBadge slo={slo} />
</EuiFlexGroup>
),
@ -489,6 +562,24 @@ export function SloListCompactView({ sloList, loading, error }: Props) {
isLoading={isResetLoading}
/>
) : null}
{sloToEnable ? (
<SloEnableConfirmationModal
slo={sloToEnable}
onCancel={handleEnableCancel}
onConfirm={handleEnableConfirm}
isLoading={isEnableLoading}
/>
) : null}
{sloToDisable ? (
<SloDisableConfirmationModal
slo={sloToDisable}
onCancel={handleDisableCancel}
onConfirm={handleDisableConfirm}
isLoading={isDisableLoading}
/>
) : null}
</>
);
}

View file

@ -31,6 +31,8 @@ interface Props {
setIsActionsPopoverOpen: (value: boolean) => void;
setDeleteConfirmationModalOpen: (value: boolean) => void;
setResetConfirmationModalOpen: (value: boolean) => void;
setEnableConfirmationModalOpen: (value: boolean) => void;
setDisableConfirmationModalOpen: (value: boolean) => void;
setIsAddRuleFlyoutOpen: (value: boolean) => void;
setIsEditRuleFlyoutOpen: (value: boolean) => void;
setDashboardAttachmentReady?: (value: boolean) => void;
@ -65,6 +67,8 @@ export function SloItemActions({
setIsEditRuleFlyoutOpen,
setDeleteConfirmationModalOpen,
setResetConfirmationModalOpen,
setEnableConfirmationModalOpen,
setDisableConfirmationModalOpen,
setDashboardAttachmentReady,
btnProps,
}: Props) {
@ -77,13 +81,20 @@ export function SloItemActions({
const { data: permissions } = usePermissions();
const navigateToClone = useCloneSlo();
const { handleNavigateToRules, sloEditUrl, remoteDeleteUrl, remoteResetUrl, sloDetailsUrl } =
useSloActions({
slo,
rules,
setIsEditRuleFlyoutOpen,
setIsActionsPopoverOpen,
});
const {
handleNavigateToRules,
sloEditUrl,
remoteDeleteUrl,
remoteResetUrl,
remoteEnableUrl,
remoteDisableUrl,
sloDetailsUrl,
} = useSloActions({
slo,
rules,
setIsEditRuleFlyoutOpen,
setIsActionsPopoverOpen,
});
const handleClickActions = () => {
setIsActionsPopoverOpen(!isActionsPopoverOpen);
@ -115,6 +126,24 @@ export function SloItemActions({
}
};
const handleEnable = () => {
if (!!remoteEnableUrl) {
window.open(remoteEnableUrl, '_blank');
} else {
setEnableConfirmationModalOpen(true);
setIsActionsPopoverOpen(false);
}
};
const handleDisable = () => {
if (!!remoteDisableUrl) {
window.open(remoteDisableUrl, '_blank');
} else {
setDisableConfirmationModalOpen(true);
setIsActionsPopoverOpen(false);
}
};
const handleCreateRule = () => {
setIsActionsPopoverOpen(false);
setIsAddRuleFlyoutOpen(true);
@ -221,6 +250,35 @@ export function SloItemActions({
})}
{showRemoteLinkIcon}
</EuiContextMenuItem>,
slo.enabled ? (
<EuiContextMenuItem
key="disable"
icon="stop"
disabled={!permissions?.hasAllWriteRequested || hasUndefinedRemoteKibanaUrl}
onClick={handleDisable}
toolTipContent={
hasUndefinedRemoteKibanaUrl ? NOT_AVAILABLE_FOR_UNDEFINED_REMOTE_KIBANA_URL : ''
}
data-test-subj="sloActionsDisable"
>
{i18n.translate('xpack.slo.item.actions.disable', { defaultMessage: 'Disable' })}
{showRemoteLinkIcon}
</EuiContextMenuItem>
) : (
<EuiContextMenuItem
key="enable"
icon="play"
disabled={!permissions?.hasAllWriteRequested || hasUndefinedRemoteKibanaUrl}
onClick={handleEnable}
toolTipContent={
hasUndefinedRemoteKibanaUrl ? NOT_AVAILABLE_FOR_UNDEFINED_REMOTE_KIBANA_URL : ''
}
data-test-subj="sloActionsEnable"
>
{i18n.translate('xpack.slo.item.actions.enable', { defaultMessage: 'Enable' })}
{showRemoteLinkIcon}
</EuiContextMenuItem>
),
<EuiContextMenuItem
key="clone"
disabled={!permissions?.hasAllWriteRequested || hasUndefinedRemoteKibanaUrl}

View file

@ -10,7 +10,11 @@ import { HistoricalSummaryResponse, SLOWithSummaryResponse } from '@kbn/slo-sche
import type { Rule } from '@kbn/triggers-actions-ui-plugin/public';
import React, { useState } from 'react';
import { SloDeleteModal } from '../../../../components/slo/delete_confirmation_modal/slo_delete_confirmation_modal';
import { SloDisableConfirmationModal } from '../../../../components/slo/disable_confirmation_modal/slo_disable_confirmation_modal';
import { SloEnableConfirmationModal } from '../../../../components/slo/enable_confirmation_modal/slo_enable_confirmation_modal';
import { SloResetConfirmationModal } from '../../../../components/slo/reset_confirmation_modal/slo_reset_confirmation_modal';
import { useDisableSlo } from '../../../../hooks/use_disable_slo';
import { useEnableSlo } from '../../../../hooks/use_enable_slo';
import { useResetSlo } from '../../../../hooks/use_reset_slo';
import { BurnRateRuleParams } from '../../../../typings';
import { useSloListActions } from '../../hooks/use_slo_list_actions';
@ -44,8 +48,13 @@ export function SloListItem({
const [isEditRuleFlyoutOpen, setIsEditRuleFlyoutOpen] = useState(false);
const [isDeleteConfirmationModalOpen, setDeleteConfirmationModalOpen] = useState(false);
const [isResetConfirmationModalOpen, setResetConfirmationModalOpen] = useState(false);
const [isEnableConfirmationModalOpen, setEnableConfirmationModalOpen] = useState(false);
const [isDisableConfirmationModalOpen, setDisableConfirmationModalOpen] = useState(false);
const { mutate: resetSlo, isLoading: isResetLoading } = useResetSlo();
const { mutate: enableSlo, isLoading: isEnableLoading } = useEnableSlo();
const { mutate: disableSlo, isLoading: isDisableLoading } = useDisableSlo();
const { mutateAsync: resetSlo, isLoading: isResetLoading } = useResetSlo();
const { sloDetailsUrl } = useSloFormattedSummary(slo);
const { handleCreateRule } = useSloListActions({
@ -58,8 +67,8 @@ export function SloListItem({
setDeleteConfirmationModalOpen(false);
};
const handleResetConfirm = async () => {
await resetSlo({ id: slo.id, name: slo.name });
const handleResetConfirm = () => {
resetSlo({ id: slo.id, name: slo.name });
setResetConfirmationModalOpen(false);
};
@ -67,6 +76,22 @@ export function SloListItem({
setResetConfirmationModalOpen(false);
};
const handleEnableCancel = () => {
setEnableConfirmationModalOpen(false);
};
const handleEnableConfirm = () => {
enableSlo({ id: slo.id, name: slo.name });
setEnableConfirmationModalOpen(false);
};
const handleDisableCancel = () => {
setDisableConfirmationModalOpen(false);
};
const handleDisableConfirm = () => {
disableSlo({ id: slo.id, name: slo.name });
setDisableConfirmationModalOpen(false);
};
return (
<EuiPanel data-test-subj="sloItem" hasBorder hasShadow={false}>
<EuiFlexGroup responsive={false} alignItems="center">
@ -116,6 +141,8 @@ export function SloListItem({
setIsActionsPopoverOpen={setIsActionsPopoverOpen}
setDeleteConfirmationModalOpen={setDeleteConfirmationModalOpen}
setResetConfirmationModalOpen={setResetConfirmationModalOpen}
setEnableConfirmationModalOpen={setEnableConfirmationModalOpen}
setDisableConfirmationModalOpen={setDisableConfirmationModalOpen}
/>
</EuiFlexItem>
</EuiFlexGroup>
@ -144,6 +171,24 @@ export function SloListItem({
isLoading={isResetLoading}
/>
) : null}
{isEnableConfirmationModalOpen ? (
<SloEnableConfirmationModal
slo={slo}
onCancel={handleEnableCancel}
onConfirm={handleEnableConfirm}
isLoading={isEnableLoading}
/>
) : null}
{isDisableConfirmationModalOpen ? (
<SloDisableConfirmationModal
slo={slo}
onCancel={handleDisableCancel}
onConfirm={handleDisableConfirm}
isLoading={isDisableLoading}
/>
) : null}
</EuiPanel>
);
}

View file

@ -8,7 +8,7 @@
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { SLOWithSummaryResponse } from '@kbn/slo-schema';
import React from 'react';
import { SloListCardView } from './card_view/slos_card_view';
import { SloListCardView } from './card_view/slo_list_card_view';
import { SloListCompactView } from './compact_view/slo_list_compact_view';
import { HealthCallout } from './health_callout/health_callout';
import { SloListEmpty } from './slo_list_empty';

View file

@ -10,7 +10,9 @@ import {
createRemoteSloCloneUrl,
createRemoteSloDeleteUrl,
createRemoteSloDetailsUrl,
createRemoteSloDisableUrl,
createRemoteSloEditUrl,
createRemoteSloEnableUrl,
} from './remote_slo_urls';
describe('remote SLO URLs Utils', () => {
@ -19,6 +21,8 @@ describe('remote SLO URLs Utils', () => {
expect(createRemoteSloDetailsUrl(localSlo)).toMatchInlineSnapshot(`undefined`);
expect(createRemoteSloDeleteUrl(localSlo)).toMatchInlineSnapshot(`undefined`);
expect(createRemoteSloEnableUrl(localSlo)).toMatchInlineSnapshot(`undefined`);
expect(createRemoteSloDisableUrl(localSlo)).toMatchInlineSnapshot(`undefined`);
expect(createRemoteSloEditUrl(localSlo)).toMatchInlineSnapshot(`undefined`);
expect(createRemoteSloCloneUrl(localSlo)).toMatchInlineSnapshot(`undefined`);
});
@ -31,6 +35,8 @@ describe('remote SLO URLs Utils', () => {
expect(createRemoteSloDetailsUrl(remoteSloWithoutKibanaUrl)).toMatchInlineSnapshot(`undefined`);
expect(createRemoteSloDeleteUrl(remoteSloWithoutKibanaUrl)).toMatchInlineSnapshot(`undefined`);
expect(createRemoteSloEnableUrl(remoteSloWithoutKibanaUrl)).toMatchInlineSnapshot(`undefined`);
expect(createRemoteSloDisableUrl(remoteSloWithoutKibanaUrl)).toMatchInlineSnapshot(`undefined`);
expect(createRemoteSloEditUrl(remoteSloWithoutKibanaUrl)).toMatchInlineSnapshot(`undefined`);
expect(createRemoteSloCloneUrl(remoteSloWithoutKibanaUrl)).toMatchInlineSnapshot(`undefined`);
});
@ -47,6 +53,12 @@ describe('remote SLO URLs Utils', () => {
expect(createRemoteSloDeleteUrl(remoteSlo)).toMatchInlineSnapshot(
`"https://cloud.elast.co/app/slos/fixed-id?delete=true"`
);
expect(createRemoteSloEnableUrl(remoteSlo)).toMatchInlineSnapshot(
`"https://cloud.elast.co/app/slos/fixed-id?enable=true"`
);
expect(createRemoteSloDisableUrl(remoteSlo)).toMatchInlineSnapshot(
`"https://cloud.elast.co/app/slos/fixed-id?disable=true"`
);
expect(createRemoteSloEditUrl(remoteSlo)).toMatchInlineSnapshot(
`"https://cloud.elast.co/app/slos/edit/fixed-id"`
);
@ -67,6 +79,12 @@ describe('remote SLO URLs Utils', () => {
expect(createRemoteSloDeleteUrl(remoteSlo, 'my-custom-space')).toMatchInlineSnapshot(
`"https://cloud.elast.co/s/my-custom-space/app/slos/fixed-id?delete=true"`
);
expect(createRemoteSloEnableUrl(remoteSlo, 'my-custom-space')).toMatchInlineSnapshot(
`"https://cloud.elast.co/s/my-custom-space/app/slos/fixed-id?enable=true"`
);
expect(createRemoteSloDisableUrl(remoteSlo, 'my-custom-space')).toMatchInlineSnapshot(
`"https://cloud.elast.co/s/my-custom-space/app/slos/fixed-id?disable=true"`
);
expect(createRemoteSloEditUrl(remoteSlo, 'my-custom-space')).toMatchInlineSnapshot(
`"https://cloud.elast.co/s/my-custom-space/app/slos/edit/fixed-id"`
);

View file

@ -10,55 +10,71 @@ import { ALL_VALUE, SLOWithSummaryResponse } from '@kbn/slo-schema';
import path from 'path';
import { paths } from '../../../common/locators/paths';
function createBaseRemoteSloDetailsUrl(
slo: SLOWithSummaryResponse,
spaceId: string = 'default'
): URL | undefined {
if (!slo.remote || slo.remote.kibanaUrl === '') {
return undefined;
}
const spacePath = spaceId !== 'default' ? `/s/${spaceId}` : '';
const detailsPath = paths.sloDetails(
slo.id,
![slo.groupBy].flat().includes(ALL_VALUE) && slo.instanceId ? slo.instanceId : undefined
);
const remoteUrl = new URL(path.join(spacePath, detailsPath), slo.remote.kibanaUrl);
return remoteUrl;
}
export function createRemoteSloDetailsUrl(
slo: SLOWithSummaryResponse,
spaceId: string = 'default'
) {
if (!slo.remote || slo.remote.kibanaUrl === '') {
return undefined;
}
const spacePath = spaceId !== 'default' ? `/s/${spaceId}` : '';
const detailsPath = paths.sloDetails(
slo.id,
![slo.groupBy].flat().includes(ALL_VALUE) && slo.instanceId ? slo.instanceId : undefined
);
const remoteUrl = new URL(path.join(spacePath, detailsPath), slo.remote.kibanaUrl);
return remoteUrl.toString();
return createBaseRemoteSloDetailsUrl(slo, spaceId)?.toString();
}
export function createRemoteSloDeleteUrl(slo: SLOWithSummaryResponse, spaceId: string = 'default') {
if (!slo.remote || slo.remote.kibanaUrl === '') {
const remoteUrl = createBaseRemoteSloDetailsUrl(slo, spaceId);
if (!remoteUrl) {
return undefined;
}
const spacePath = spaceId !== 'default' ? `/s/${spaceId}` : '';
const detailsPath = paths.sloDetails(
slo.id,
![slo.groupBy].flat().includes(ALL_VALUE) && slo.instanceId ? slo.instanceId : undefined
);
const remoteUrl = new URL(path.join(spacePath, detailsPath), slo.remote.kibanaUrl);
remoteUrl.searchParams.append('delete', 'true');
return remoteUrl.toString();
}
export function createRemoteSloResetUrl(slo: SLOWithSummaryResponse, spaceId: string = 'default') {
if (!slo.remote || slo.remote.kibanaUrl === '') {
const remoteUrl = createBaseRemoteSloDetailsUrl(slo, spaceId);
if (!remoteUrl) {
return undefined;
}
const spacePath = spaceId !== 'default' ? `/s/${spaceId}` : '';
const detailsPath = paths.sloDetails(
slo.id,
![slo.groupBy].flat().includes(ALL_VALUE) && slo.instanceId ? slo.instanceId : undefined
);
const remoteUrl = new URL(path.join(spacePath, detailsPath), slo.remote.kibanaUrl);
remoteUrl.searchParams.append('reset', 'true');
return remoteUrl.toString();
}
export function createRemoteSloEnableUrl(slo: SLOWithSummaryResponse, spaceId: string = 'default') {
const remoteUrl = createBaseRemoteSloDetailsUrl(slo, spaceId);
if (!remoteUrl) {
return undefined;
}
remoteUrl.searchParams.append('enable', 'true');
return remoteUrl.toString();
}
export function createRemoteSloDisableUrl(
slo: SLOWithSummaryResponse,
spaceId: string = 'default'
) {
const remoteUrl = createBaseRemoteSloDetailsUrl(slo, spaceId);
if (!remoteUrl) {
return undefined;
}
remoteUrl.searchParams.append('disable', 'true');
return remoteUrl.toString();
}