[Synthetics] Create SLO from Synthetics app for monitors !! (#188835)

## Summary
Fixes https://github.com/elastic/kibana/issues/178449
Create SLO from Synthetics app  for monitors !! 

<img width="1446" alt="image"
src="https://github.com/user-attachments/assets/c8224faf-cae3-4163-8a26-2c2db27e35f3">


https://github.com/user-attachments/assets/4eaf9777-e031-43cb-a41b-6aa67c41268c

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Shahzad 2024-07-25 15:24:14 +02:00 committed by GitHub
parent 91d64f0481
commit 67b8d98619
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 137 additions and 45 deletions

View file

@ -35,14 +35,14 @@ export function useCreateRule<Params extends RuleTypeParams = never>() {
body,
});
} catch (e) {
throw new Error(`Unable to create rule: ${e}`);
throw new Error(`Unable to create burn rate rule: ${e}`);
}
},
{
onError: (_err) => {
toasts.addDanger(
i18n.translate('xpack.slo.rules.createRule.errorNotification.descriptionText', {
defaultMessage: 'Failed to create rule',
defaultMessage: 'Failed to create burn rate rule.',
})
);
},
@ -50,7 +50,7 @@ export function useCreateRule<Params extends RuleTypeParams = never>() {
onSuccess: () => {
toasts.addSuccess(
i18n.translate('xpack.slo.rules.createRule.successNotification.descriptionText', {
defaultMessage: 'Rule created',
defaultMessage: 'Burn rate rule created successfully.',
})
);
},

View file

@ -40,6 +40,7 @@
"cloud",
"data",
"fleet",
"slo",
"home",
"ml",
"serverless",

View file

@ -0,0 +1,60 @@
/*
* 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 { useKibana } from '@kbn/kibana-react-plugin/public';
import { useState } from 'react';
import { SYNTHETICS_INDEX_PATTERN } from '../../../../../../common/constants';
import { ClientPluginsStart } from '../../../../../plugin';
export function useCreateSLO({
configId,
label,
tags,
}: {
configId: string;
label: string;
tags: string[];
}) {
const { slo } = useKibana<ClientPluginsStart>().services;
const [isSLOFlyoutOpen, setIsSLOFlyoutOpen] = useState(false);
const CreateSLOFlyout = slo?.getCreateSLOFlyout({
initialValues: {
name: `SLO for monitor ${label}`,
indicator: {
type: 'sli.synthetics.availability',
params: {
index: SYNTHETICS_INDEX_PATTERN,
filter: '',
monitorIds: [
{
value: configId,
label,
},
],
projects: [],
tags: [],
},
},
budgetingMethod: 'occurrences',
objective: {
target: 0.99,
},
tags: tags || [],
groupBy: ['monitor.name', 'observer.geo.name', 'monitor.id'],
},
onClose: () => {
setIsSLOFlyoutOpen(false);
},
});
return {
CreateSLOFlyout: isSLOFlyoutOpen ? CreateSLOFlyout : null,
setIsSLOFlyoutOpen,
};
}

View file

@ -20,6 +20,7 @@ import { FETCH_STATUS } from '@kbn/observability-shared-plugin/public';
import { useDispatch, useSelector } from 'react-redux';
import styled from 'styled-components';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import { useCreateSLO } from '../../hooks/use_create_slo';
import { TEST_SCHEDULED_LABEL } from '../../../monitor_add_edit/form/run_test_btn';
import { useCanUsePublicLocById } from '../../hooks/use_can_use_public_loc_id';
import { toggleStatusAlert } from '../../../../../../../common/runtime_types/monitor_management/alert_config';
@ -136,6 +137,11 @@ export function ActionsPopover({
});
const { alertStatus, updateAlertEnabledState } = useMonitorAlertEnable();
const { CreateSLOFlyout, setIsSLOFlyoutOpen } = useCreateSLO({
configId: monitor.configId,
label: monitor.name,
tags: monitor.tags,
});
const [enableLabel, setEnableLabel] = useState(
monitor.isEnabled ? disableMonitorLabel : enableMonitorLabel
@ -220,6 +226,20 @@ export function ActionsPopover({
href: http?.basePath.prepend(`synthetics/add-monitor?cloneId=${monitor.configId}`),
'data-test-subj': 'cloneMonitorLink',
},
{
name: (
<NoPermissionsTooltip canEditSynthetics={canEditSynthetics}>
{CREATE_SLO}
</NoPermissionsTooltip>
),
icon: 'visGauge',
disabled: !canEditSynthetics || !isServiceAllowed,
onClick: () => {
setIsPopoverOpen(false);
setIsSLOFlyoutOpen(true);
},
'data-test-subj': 'createSLOBtn',
},
{
name: (
<NoPermissionsTooltip
@ -274,40 +294,43 @@ export function ActionsPopover({
if (isInspectView) popoverItems = popoverItems.filter((i) => i !== quickInspectPopoverItem);
return (
<Container boxShadow={euiShadow} position={position}>
<EuiPopover
button={
<IconPanel hasPanel={iconHasPanel}>
<EuiButtonIcon
data-test-subj="syntheticsActionsPopoverButton"
aria-label={openActionsMenuAria}
iconType="boxesHorizontal"
color="primary"
size={iconSize}
display="empty"
onClick={() => setIsPopoverOpen((b: boolean) => !b)}
title={openActionsMenuAria}
/>
</IconPanel>
}
color="lightestShade"
isOpen={isPopoverOpen}
closePopover={() => setIsPopoverOpen(false)}
anchorPosition="rightUp"
panelPaddingSize="none"
>
<EuiContextMenu
initialPanelId={0}
panels={[
{
id: '0',
title: actionsMenuTitle,
items: popoverItems,
},
]}
/>
</EuiPopover>
</Container>
<>
<Container boxShadow={euiShadow} position={position}>
<EuiPopover
button={
<IconPanel hasPanel={iconHasPanel}>
<EuiButtonIcon
data-test-subj="syntheticsActionsPopoverButton"
aria-label={openActionsMenuAria}
iconType="boxesHorizontal"
color="primary"
size={iconSize}
display="empty"
onClick={() => setIsPopoverOpen((b: boolean) => !b)}
title={openActionsMenuAria}
/>
</IconPanel>
}
color="lightestShade"
isOpen={isPopoverOpen}
closePopover={() => setIsPopoverOpen(false)}
anchorPosition="rightUp"
panelPaddingSize="none"
>
<EuiContextMenu
initialPanelId={0}
panels={[
{
id: '0',
title: actionsMenuTitle,
items: popoverItems,
},
]}
/>
</EuiPopover>
</Container>
{CreateSLOFlyout}
</>
);
}
@ -356,6 +379,10 @@ const actionsMenuCloneMonitorName = i18n.translate(
}
);
const CREATE_SLO = i18n.translate('xpack.synthetics.overview.actions.createSlo.name', {
defaultMessage: 'Create SLO',
});
const loadingLabel = (isEnabled: boolean) =>
isEnabled
? i18n.translate('xpack.synthetics.overview.actions.disablingLabel', {

View file

@ -41,6 +41,7 @@ export const SyntheticsSharedContext: React.FC<SyntheticsAppProps> = ({
share: startPlugins.share,
unifiedSearch: startPlugins.unifiedSearch,
embeddable: startPlugins.embeddable,
slo: startPlugins.slo,
}}
>
<EuiThemeProvider darkMode={darkMode}>

View file

@ -12,7 +12,6 @@ import {
EncryptedSyntheticsMonitor,
FetchMonitorManagementListQueryArgs,
MonitorManagementListResult,
MonitorManagementListResultCodec,
SyntheticsMonitor,
MonitorFiltersResult,
} from '../../../../../common/runtime_types';
@ -44,11 +43,10 @@ export const fetchMonitorManagementList = async (
): Promise<MonitorManagementListResult> => {
const params = toMonitorManagementListQueryArgs(pageState);
return await apiService.get(
SYNTHETICS_API_URLS.SYNTHETICS_MONITORS,
{ ...params, version: INITIAL_REST_VERSION },
MonitorManagementListResultCodec
);
return await apiService.get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS, {
...params,
version: INITIAL_REST_VERSION,
});
};
export const fetchDeleteMonitor = async ({ configId }: { configId: string }): Promise<void> => {

View file

@ -60,6 +60,7 @@ import { ServerlessPluginSetup, ServerlessPluginStart } from '@kbn/serverless/pu
import type { UiActionsSetup } from '@kbn/ui-actions-plugin/public';
import type { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public';
import { DashboardStart, DashboardSetup } from '@kbn/dashboard-plugin/public';
import { SloPublicStart } from '@kbn/slo-plugin/public';
import { registerSyntheticsEmbeddables } from './apps/embeddables/register_embeddables';
import { kibanaService } from './utils/kibana_service';
import { PLUGIN } from '../common/constants/plugin';
@ -111,6 +112,7 @@ export interface ClientPluginsStart {
usageCollection: UsageCollectionStart;
serverless: ServerlessPluginStart;
licenseManagement?: LicenseManagementUIPluginSetup;
slo?: SloPublicStart;
presentationUtil: PresentationUtilPluginStart;
dashboard: DashboardStart;
}

View file

@ -50,7 +50,9 @@ class ApiService {
} else {
// eslint-disable-next-line no-console
console.error(
`API $s is not returning expected response, ${formatErrors(decoded.left)} for response`,
`API ${apiUrl} is not returning expected response, ${formatErrors(
decoded.left
)} for response`,
apiUrl,
response
);

View file

@ -97,7 +97,8 @@
"@kbn/ui-actions-plugin",
"@kbn/presentation-util-plugin",
"@kbn/core-application-browser",
"@kbn/dashboard-plugin"
"@kbn/dashboard-plugin",
"@kbn/slo-plugin"
],
"exclude": ["target/**/*"]
}