mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
feat(slo): Add enable/disable actions (#205518)
This commit is contained in:
parent
a8579bb41f
commit
0ed641ef4f
28 changed files with 755 additions and 87 deletions
|
@ -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>
|
||||
);
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
}
|
|
@ -6,3 +6,5 @@
|
|||
*/
|
||||
|
||||
export { SloStatusBadge } from './slo_status_badge';
|
||||
export { SloActiveAlertsBadge } from './slo_active_alerts_badge';
|
||||
export { SloStateBadge } from './slo_state_badge';
|
|
@ -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>
|
||||
);
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 },
|
||||
})
|
||||
);
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
|
@ -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 },
|
||||
})
|
||||
);
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
|
@ -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}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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} />
|
||||
|
|
|
@ -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', {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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"`
|
||||
);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue