Skip to content

Commit

Permalink
[SLO] Open burn rule editing in flyout (#179373)
Browse files Browse the repository at this point in the history
## Summary

Open burn rule editing in flyout !!

Also handled mouse on click for badges in card view !!


https://github.com/elastic/kibana/assets/3505601/2c39ee6a-c011-439c-b268-3b02eda77cc0
  • Loading branch information
shahzad31 authored Mar 27, 2024
1 parent cb40ad3 commit f320d56
Show file tree
Hide file tree
Showing 13 changed files with 175 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
* 2.0.
*/

import { EuiBadge, EuiFlexItem } from '@elastic/eui';
import { EuiBadge, EuiFlexItem, EuiToolTip } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
import React, { MouseEvent } from 'react';
import { SLOWithSummaryResponse } from '@kbn/slo-schema';
import { observabilityPaths } from '@kbn/observability-plugin/common';
import { useKibana } from '../../../utils/kibana_react';
Expand Down Expand Up @@ -43,22 +43,36 @@ export function SloActiveAlertsBadge({ slo, activeAlerts, viewMode = 'default' }

return (
<EuiFlexItem grow={false}>
<EuiBadge
iconType="warning"
color="danger"
onClick={handleActiveAlertsClick}
onClickAriaLabel={i18n.translate('xpack.slo.slo.activeAlertsBadge.ariaLabel', {
defaultMessage: 'active alerts badge',
<EuiToolTip
position="top"
content={i18n.translate('xpack.slo.slo.activeAlertsBadge.tooltip', {
defaultMessage:
'{count, plural, one {# burn rate alert} other {# burn rate alerts}}, click to view.',
values: { count: activeAlerts },
})}
data-test-subj="o11ySloActiveAlertsBadge"
display="block"
>
{viewMode !== 'default'
? activeAlerts
: i18n.translate('xpack.slo.slo.activeAlertsBadge.label', {
defaultMessage: '{count, plural, one {# alert} other {# alerts}}',
values: { count: activeAlerts },
})}
</EuiBadge>
<EuiBadge
iconType="warning"
color="danger"
onClick={handleActiveAlertsClick}
onClickAriaLabel={i18n.translate('xpack.slo.slo.activeAlertsBadge.ariaLabel', {
defaultMessage: 'active alerts badge',
})}
data-test-subj="o11ySloActiveAlertsBadge"
onMouseDown={(e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation(); // stops propagation of metric onElementClick
}}
css={{ cursor: 'pointer' }}
>
{viewMode !== 'default'
? activeAlerts
: i18n.translate('xpack.slo.slo.activeAlertsBadge.label', {
defaultMessage: '{count, plural, one {# alert} other {# alerts}}',
values: { count: activeAlerts },
})}
</EuiBadge>
</EuiToolTip>
</EuiFlexItem>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,52 +5,47 @@
* 2.0.
*/

import type { Rule } from '@kbn/triggers-actions-ui-plugin/public';
import type { Rule, AsApiContract } from '@kbn/triggers-actions-ui-plugin/public';
import { transformRule } from '@kbn/triggers-actions-ui-plugin/public';
import { useQuery } from '@tanstack/react-query';
import { BurnRateRuleParams } from '../typings';
import { useKibana } from '../utils/kibana_react';
import { sloKeys } from './query_key_factory';

type SloId = string;

interface Params {
sloIds?: SloId[];
sloIds?: string[];
}

interface RuleApiResponse {
page: number;
total: number;
per_page: number;
data: Array<Rule<BurnRateRuleParams>>;
}

export interface UseFetchRulesForSloResponse {
isLoading: boolean;
isSuccess: boolean;
isError: boolean;
data: Record<string, Array<Rule<BurnRateRuleParams>>> | undefined;
data: Array<AsApiContract<Rule<BurnRateRuleParams>>>;
}

export function useFetchRulesForSlo({ sloIds = [] }: Params): UseFetchRulesForSloResponse {
export function useFetchRulesForSlo({ sloIds = [] }: Params) {
const { http } = useKibana().services;

const { isLoading, isError, isSuccess, data } = useQuery({
const { isLoading, isError, isSuccess, data, refetch } = useQuery({
queryKey: sloKeys.rule(sloIds),
queryFn: async () => {
try {
const body = JSON.stringify({
filter: sloIds.map((sloId) => `alert.attributes.params.sloId:${sloId}`).join(' or '),
fields: ['params', 'name'],
per_page: 1000,
});

const response = await http.post<RuleApiResponse>(`/internal/alerting/rules/_find`, {
body,
});

const rules = response.data.map((rule) => transformRule(rule)) as Array<
Rule<BurnRateRuleParams>
>;

const init = sloIds.reduce((acc, sloId) => ({ ...acc, [sloId]: [] }), {});

return response.data.reduce(
return rules.reduce(
(acc, rule) => ({
...acc,
[rule.params.sloId]: acc[rule.params.sloId].concat(rule),
Expand All @@ -66,10 +61,13 @@ export function useFetchRulesForSlo({ sloIds = [] }: Params): UseFetchRulesForSl
keepPreviousData: true,
});

const refetchRules = refetch as () => void;

return {
data,
isLoading,
isSuccess,
isError,
refetchRules,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import type { RulesParams } from '@kbn/observability-plugin/public';
import { rulesLocatorID } from '@kbn/observability-plugin/common';
import { SLO_BURN_RATE_RULE_TYPE_ID } from '@kbn/rule-data-utils';
import { sloFeatureId } from '@kbn/observability-plugin/common';
import { EditBurnRateRuleFlyout } from '../../slos/components/common/edit_burn_rate_rule_flyout';
import { useFetchRulesForSlo } from '../../../hooks/use_fetch_rules_for_slo';
import { useKibana } from '../../../utils/kibana_react';
import { paths } from '../../../../common/locators/paths';
import { SloDeleteConfirmationModal } from '../../../components/slo/delete_confirmation_modal/slo_delete_confirmation_modal';
Expand All @@ -24,7 +26,7 @@ import { convertSliApmParamsToApmAppDeeplinkUrl } from '../../../utils/slo/conve
import { isApmIndicatorType } from '../../../utils/slo/indicator';

export interface Props {
slo: SLOWithSummaryResponse | undefined;
slo?: SLOWithSummaryResponse;
isLoading: boolean;
}

Expand All @@ -42,10 +44,18 @@ export function HeaderControl({ isLoading, slo }: Props) {

const [isPopoverOpen, setIsPopoverOpen] = useState(false);
const [isRuleFlyoutVisible, setRuleFlyoutVisibility] = useState<boolean>(false);
const [isEditRuleFlyoutOpen, setIsEditRuleFlyoutOpen] = useState(false);

const [isDeleteConfirmationModalOpen, setDeleteConfirmationModalOpen] = useState(false);

const { mutate: deleteSlo } = useDeleteSlo();

const { data: rulesBySlo, refetchRules } = useFetchRulesForSlo({
sloIds: slo ? [slo.id] : undefined,
});

const rules = slo ? rulesBySlo?.[slo?.id] ?? [] : [];

const handleActionsClick = () => setIsPopoverOpen((value) => !value);
const closePopover = () => setIsPopoverOpen(false);

Expand All @@ -65,10 +75,15 @@ export function HeaderControl({ isLoading, slo }: Props) {
};

const handleNavigateToRules = async () => {
const locator = locators.get<RulesParams>(rulesLocatorID);
if (rules.length === 1) {
setIsEditRuleFlyoutOpen(true);
setIsPopoverOpen(false);
} else {
const locator = locators.get<RulesParams>(rulesLocatorID);

if (slo?.id && locator) {
locator.navigate({ params: { sloId: slo.id } }, { replace: false });
if (slo?.id && locator) {
locator.navigate({ params: { sloId: slo.id } }, { replace: false });
}
}
};

Expand Down Expand Up @@ -168,7 +183,8 @@ export function HeaderControl({ isLoading, slo }: Props) {
data-test-subj="sloDetailsHeaderControlPopoverManageRules"
>
{i18n.translate('xpack.slo.sloDetails.headerControl.manageRules', {
defaultMessage: 'Manage rules',
defaultMessage: 'Manage burn rate {count, plural, one {rule} other {rules}}',
values: { count: rules.length },
})}
</EuiContextMenuItem>,
]
Expand Down Expand Up @@ -215,6 +231,12 @@ export function HeaderControl({ isLoading, slo }: Props) {
)}
/>
</EuiPopover>
<EditBurnRateRuleFlyout
rule={rules?.[0]}
isEditRuleFlyoutOpen={isEditRuleFlyoutOpen}
setIsEditRuleFlyoutOpen={setIsEditRuleFlyoutOpen}
refetchRules={refetchRules}
/>

{slo && isRuleFlyoutVisible ? (
<AddRuleFlyout
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@ import { SLOGroupings } from '../../slos/components/common/slo_groupings';
import { SloStatusBadge } from '../../../components/slo/slo_status_badge';

export interface Props {
slo: SLOWithSummaryResponse | undefined;
slo?: SLOWithSummaryResponse;
isLoading: boolean;
showTitle?: boolean;
}

export function HeaderTitle({ isLoading, slo }: Props) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import React from 'react';
import React, { MouseEvent } from 'react';
import { EuiBadge, EuiToolTip } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { Rule } from '@kbn/triggers-actions-ui-plugin/public';
Expand All @@ -22,12 +22,19 @@ export function SloRulesBadge({ rules, onClick }: Props) {
position="top"
content={i18n.translate('xpack.slo.slo.rulesBadge.popover', {
defaultMessage:
'There are no rules configured for this SLO yet. You will not receive alerts when SLO is breached.',
'There are no rules configured for this SLO yet. You will not receive alerts when SLO is breached. Click to create a rule.',
})}
display="block"
>
<span onClick={onClick} onKeyDown={onClick}>
<EuiBadge color="text" iconType="alert" css={{ cursor: 'pointer' }} />
<EuiBadge
color="text"
iconType="alert"
css={{ cursor: 'pointer' }}
onMouseDown={(e: MouseEvent<HTMLButtonElement>) => {
e.stopPropagation(); // stops propagation of metric onElementClick
}}
/>
</span>
</EuiToolTip>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
import { ALL_VALUE, HistoricalSummaryResponse, SLOWithSummaryResponse } from '@kbn/slo-schema';
import { Rule } from '@kbn/triggers-actions-ui-plugin/public';
import React, { useState } from 'react';
import { EditBurnRateRuleFlyout } from '../common/edit_burn_rate_rule_flyout';
import { SloDeleteConfirmationModal } from '../../../../components/slo/delete_confirmation_modal/slo_delete_confirmation_modal';
import { BurnRateRuleParams } from '../../../../typings';
import { useKibana } from '../../../../utils/kibana_react';
Expand All @@ -43,7 +44,7 @@ export interface Props {
activeAlerts?: number;
loading: boolean;
error: boolean;
cardsPerRow: number;
refetchRules: () => void;
}

export const useSloCardColor = (status?: SLOWithSummaryResponse['summary']['status']) => {
Expand All @@ -67,12 +68,13 @@ const getFirstGroupBy = (slo: SLOWithSummaryResponse) => {
return slo.groupBy && ![slo.groupBy].flat().includes(ALL_VALUE) ? firstGroupBy : '';
};

export function SloCardItem({ slo, rules, activeAlerts, historicalSummary, cardsPerRow }: Props) {
export function SloCardItem({ slo, rules, activeAlerts, historicalSummary, refetchRules }: Props) {
const containerRef = React.useRef<HTMLDivElement>(null);

const [isMouseOver, setIsMouseOver] = useState(false);
const [isActionsPopoverOpen, setIsActionsPopoverOpen] = useState(false);
const [isAddRuleFlyoutOpen, setIsAddRuleFlyoutOpen] = useState(false);
const [isEditRuleFlyoutOpen, setIsEditRuleFlyoutOpen] = useState(false);
const [isDeleteConfirmationModalOpen, setDeleteConfirmationModalOpen] = useState(false);
const [isDashboardAttachmentReady, setDashboardAttachmentReady] = useState(false);
const historicalSliData = formatHistoricalData(historicalSummary, 'sli_value');
Expand Down Expand Up @@ -124,10 +126,12 @@ export function SloCardItem({ slo, rules, activeAlerts, historicalSummary, cards
{(isMouseOver || isActionsPopoverOpen) && (
<SloCardItemActions
slo={slo}
rules={rules}
isActionsPopoverOpen={isActionsPopoverOpen}
setIsActionsPopoverOpen={setIsActionsPopoverOpen}
setIsAddRuleFlyoutOpen={setIsAddRuleFlyoutOpen}
setDeleteConfirmationModalOpen={setDeleteConfirmationModalOpen}
setIsEditRuleFlyoutOpen={setIsEditRuleFlyoutOpen}
setDashboardAttachmentReady={setDashboardAttachmentReady}
/>
)}
Expand All @@ -139,6 +143,13 @@ export function SloCardItem({ slo, rules, activeAlerts, historicalSummary, cards
setIsAddRuleFlyoutOpen={setIsAddRuleFlyoutOpen}
/>

<EditBurnRateRuleFlyout
rule={rules?.[0]}
isEditRuleFlyoutOpen={isEditRuleFlyoutOpen}
setIsEditRuleFlyoutOpen={setIsEditRuleFlyoutOpen}
refetchRules={refetchRules}
/>

{isDeleteConfirmationModalOpen ? (
<SloDeleteConfirmationModal
slo={slo}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import React from 'react';
import { SLOWithSummaryResponse } from '@kbn/slo-schema';
import styled from 'styled-components';
import { useEuiShadow } from '@elastic/eui';
import { Rule } from '@kbn/triggers-actions-ui-plugin/public';
import { BurnRateRuleParams } from '../../../../typings';
import { SloItemActions } from '../slo_item_actions';

type PopoverPosition = 'relative' | 'default';
Expand Down Expand Up @@ -41,7 +43,9 @@ interface Props {
setIsActionsPopoverOpen: (value: boolean) => void;
setDeleteConfirmationModalOpen: (value: boolean) => void;
setIsAddRuleFlyoutOpen: (value: boolean) => void;
setIsEditRuleFlyoutOpen: (value: boolean) => void;
setDashboardAttachmentReady: (value: boolean) => void;
rules?: Array<Rule<BurnRateRuleParams>>;
}

export function SloCardItemActions(props: Props) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ export function SloCardItemBadges({ slo, activeAlerts, rules, handleCreateRule }
<>
<SloActiveAlertsBadge slo={slo} activeAlerts={activeAlerts} viewMode="compact" />
<SLOCardItemInstanceBadge slo={slo} />
<SloTimeWindowBadge slo={slo} color="default" />
<SloRulesBadge rules={rules} onClick={handleCreateRule} />
<SloTimeWindowBadge slo={slo} color="default" />
<SloTagsList
tags={slo.tags}
numberOfTagsToDisplay={1}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export function SloListCardView({ sloList, loading, error }: Props) {
(slo) => [slo.id, slo.instanceId ?? ALL_VALUE] as [string, string]
);
const { data: activeAlertsBySlo } = useFetchActiveAlerts({ sloIdsAndInstanceIds });
const { data: rulesBySlo } = useFetchRulesForSlo({
const { data: rulesBySlo, refetchRules } = useFetchRulesForSlo({
sloIds: sloIdsAndInstanceIds.map((item) => item[0]),
});
const { isLoading: historicalSummaryLoading, data: historicalSummaries = [] } =
Expand Down Expand Up @@ -82,7 +82,7 @@ export function SloListCardView({ sloList, loading, error }: Props) {
)?.data
}
historicalSummaryLoading={historicalSummaryLoading}
cardsPerRow={Number(columns)}
refetchRules={refetchRules}
/>
</EuiFlexItem>
))}
Expand Down
Loading

0 comments on commit f320d56

Please sign in to comment.