Skip to content

Commit

Permalink
PMM-12741 Move creation of templated alerts to separate page
Browse files Browse the repository at this point in the history
  • Loading branch information
matejkubinec committed Feb 15, 2024
1 parent 7560424 commit a2f0113
Show file tree
Hide file tree
Showing 24 changed files with 176 additions and 142 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ import { useAppNotification } from 'app/core/copy/appNotification';
import { contextSrv } from 'app/core/core';
import { useCleanup } from 'app/core/hooks/useCleanup';
import { useQueryParams } from 'app/core/hooks/useQueryParams';
import { getPerconaSettings } from 'app/percona/shared/core/selectors';
import { useDispatch, useSelector } from 'app/types';
import { useDispatch } from 'app/types';
import { RuleWithLocation } from 'app/types/unified-alerting';

import { logInfo, LogMessages, trackNewAlerRuleFormError } from '../../../Analytics';
Expand All @@ -37,7 +36,6 @@ import { GrafanaEvaluationBehavior } from '../GrafanaEvaluationBehavior';
import { NotificationsStep } from '../NotificationsStep';
import { RecordingRulesNameSpaceAndGroupStep } from '../RecordingRulesNameSpaceAndGroupStep';
import { RuleInspector } from '../RuleInspector';
import { TemplateStep } from '../TemplateStep/TemplateStep';
import { QueryAndExpressionsStep } from '../query-and-alert-condition/QueryAndExpressionsStep';
import { translateRouteParamToRuleType } from '../util';

Expand All @@ -53,7 +51,6 @@ export const AlertRuleForm = ({ existing, prefill }: Props) => {
const [queryParams] = useQueryParams();
const [showEditYaml, setShowEditYaml] = useState(false);
const [evaluateEvery, setEvaluateEvery] = useState(existing?.group.interval ?? MINUTE);
const { result } = useSelector(getPerconaSettings);

const routeParams = useParams<{ type: string; id: string }>();
const ruleType = translateRouteParamToRuleType(routeParams.type);
Expand All @@ -75,23 +72,14 @@ export const AlertRuleForm = ({ existing, prefill }: Props) => {
return formValuesFromQueryParams(queryParams['defaults'], ruleType);
}

const type = ruleType
? ruleType
: result && !!result.alertingEnabled
? RuleFormType.templated
: RuleFormType.grafana;

return {
...getDefaultFormValues(),
condition: 'C',
queries: getDefaultQueries(),
type: ruleType || RuleFormType.grafana,
evaluateEvery: evaluateEvery,
// @PERCONA
// Set templated as default
type,
group: result && !!result.alertingEnabled ? 'default-alert-group' : '',
};
}, [existing, prefill, queryParams, evaluateEvery, ruleType, result]);
}, [existing, prefill, queryParams, evaluateEvery, ruleType]);

const formAPI = useForm<RuleFormValues>({
mode: 'onSubmit',
Expand All @@ -105,8 +93,6 @@ export const AlertRuleForm = ({ existing, prefill }: Props) => {
const dataSourceName = watch('dataSourceName');

const showDataSourceDependantStep = Boolean(type && (type === RuleFormType.grafana || !!dataSourceName));
// @PERCONA
const showTemplateStep = type === RuleFormType.templated;

const submitState = useUnifiedAlertingSelector((state) => state.ruleForm.saveRule) || initialAsyncRequestState;
useCleanup((state) => (state.unifiedAlerting.ruleForm.saveRule = initialAsyncRequestState));
Expand Down Expand Up @@ -236,8 +222,6 @@ export const AlertRuleForm = ({ existing, prefill }: Props) => {
<AlertRuleNameInput />
{/* Step 2 */}
<QueryAndExpressionsStep editingExistingRule={!!existing} onDataChange={checkAlertCondition} />
{/* @PERCONA */}
{showTemplateStep && <TemplateStep />}
{/* Step 3-4-5 */}
{showDataSourceDependantStep && (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -335,19 +335,12 @@ export const QueryAndExpressionsStep = ({ editingExistingRule, onDataChange }: P

const onClickSwitch = useCallback(() => {
const typeInForm = getValues('type');

console.log({ typeInForm });

if (typeInForm === RuleFormType.cloudAlerting) {
setValue('type', RuleFormType.grafana);
setValue('dataSourceName', null); // set data source name back to "null"

prevExpressions.length > 0 && restoreExpressionsInQueries();
prevCondition && setValue('condition', prevCondition);

// @PERCONA
} else if (typeInForm === RuleFormType.templated) {
setValue('type', RuleFormType.templated);
} else {
setValue('type', RuleFormType.cloudAlerting);
// dataSourceName is used only by Mimir/Loki alerting and recording rules
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,6 @@ export function SmartAlertTypeDetector({
const [ruleFormType] = getValues(['type']);
const canSwitch = getCanSwitch({ queries, ruleFormType, rulesSourcesWithRuler });

console.log(ruleFormType);

const options = [
{ label: 'Grafana-managed', value: RuleFormType.grafana },
{ label: 'Data source-managed', value: RuleFormType.cloudAlerting },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import { RuleFormType } from '../../../types/rule-form';

import { GrafanaManagedRuleType } from './GrafanaManagedAlert';
import { MimirFlavoredType } from './MimirOrLokiAlert';
import { TemplatedAlertRuleType } from './TemplatedAlert';

interface RuleTypePickerProps {
onChange: (value: RuleFormType) => void;
selected: RuleFormType;
Expand All @@ -23,10 +21,6 @@ interface RuleTypePickerProps {
const RuleTypePicker = ({ selected, onChange, enabledTypes }: RuleTypePickerProps) => {
const rulesSourcesWithRuler = useRulesSourcesWithRuler();
const hasLotexDatasources = !isEmpty(rulesSourcesWithRuler);
// @PERCONA
// Simplified conditions by adding these two consts below
const showTemplateRuleDisclaimer = enabledTypes.includes(RuleFormType.templated);
const showGrafanaManagedRuleDisclaimer = !showTemplateRuleDisclaimer && enabledTypes.includes(RuleFormType.grafana);

useEffect(() => {
dispatch(fetchAllPromBuildInfoAction());
Expand All @@ -41,10 +35,6 @@ const RuleTypePicker = ({ selected, onChange, enabledTypes }: RuleTypePickerProp
return (
<>
<Stack direction="row" gap={2}>
{/* @PERCONA */}
{enabledTypes.includes(RuleFormType.templated) && (
<TemplatedAlertRuleType selected={selected === RuleFormType.templated} onClick={onChange} />
)}
{enabledTypes.includes(RuleFormType.grafana) && (
<GrafanaManagedRuleType selected={selected === RuleFormType.grafana} onClick={handleChange} />
)}
Expand All @@ -56,11 +46,7 @@ const RuleTypePicker = ({ selected, onChange, enabledTypes }: RuleTypePickerProp
/>
)}
</Stack>
{showTemplateRuleDisclaimer && (
<small className={styles.meta}>Select &ldquo;Percona templated&rdquo; for an easier alert rule setup.</small>
)}
{/* First condition shouldn't occur, just a safety measure */}
{!showTemplateRuleDisclaimer && showGrafanaManagedRuleDisclaimer && (
{enabledTypes.includes(RuleFormType.grafana) && (
<small className={styles.meta}>
Select &ldquo;Grafana managed&rdquo; unless you have a Mimir, Loki or Cortex data source with the Ruler API
enabled.
Expand Down

This file was deleted.

2 changes: 1 addition & 1 deletion public/app/features/alerting/unified/state/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { AsyncThunk, createAsyncThunk } from '@reduxjs/toolkit';
import { isEmpty } from 'lodash';

import { locationService } from '@grafana/runtime';
import { formatCreateAPIPayload } from 'app/percona/integrated-alerting/components/TemplateStep/TemplateStep.utils';
import { AlertRulesService } from 'app/percona/shared/services/AlertRules/AlertRules.service';
import {
AlertmanagerAlert,
Expand Down Expand Up @@ -66,7 +67,6 @@ import {
FetchRulerRulesFilter,
setRulerRuleGroup,
} from '../api/ruler';
import { formatCreateAPIPayload } from '../components/rule-editor/TemplateStep/TemplateStep.utils';
import { RuleFormType, RuleFormValues } from '../types/rule-form';
import { addDefaultsToAlertmanagerConfig, removeMuteTimingFromRoute } from '../utils/alertmanager';
import {
Expand Down
2 changes: 1 addition & 1 deletion public/app/features/alerting/unified/types/rule-form.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Template } from 'app/percona/integrated-alerting/components/AlertRuleTemplate/AlertRuleTemplate.types';
import { FiltersForm } from 'app/percona/integrated-alerting/components/TemplateStep/TemplateStep.types';
import { Severity } from 'app/percona/shared/core';
import { AlertQuery, GrafanaAlertStateDecision } from 'app/types/unified-alerting-dto';

import { Folder } from '../components/rule-editor/RuleFolderPicker';
import { FiltersForm } from '../components/rule-editor/TemplateStep/TemplateStep.types';

export enum RuleFormType {
grafana = 'grafana',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { css } from '@emotion/css';

import { GrafanaTheme2 } from '@grafana/data';

export const getStyles = (theme: GrafanaTheme2) => ({
buttonSpinner: css`
margin-right: ${theme.spacing(1)};
`,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import React, { FC, useMemo } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { Link } from 'react-router-dom';

import { Button, HorizontalGroup, Spinner, useStyles2 } from '@grafana/ui';
import { AppChromeUpdate } from 'app/core/components/AppChrome/AppChromeUpdate';
import { Page } from 'app/core/components/Page/Page';
import { useAppNotification } from 'app/core/copy/appNotification';
import { useQueryParams } from 'app/core/hooks/useQueryParams';
import { useUnifiedAlertingSelector } from 'app/features/alerting/unified/hooks/useUnifiedAlertingSelector';
import { saveRuleFormAction } from 'app/features/alerting/unified/state/actions';
import { RuleFormType, RuleFormValues } from 'app/features/alerting/unified/types/rule-form';
import { initialAsyncRequestState } from 'app/features/alerting/unified/utils/redux';
import { getDefaultFormValues, getDefaultQueries, MINUTE } from 'app/features/alerting/unified/utils/rule-form';
import { useDispatch } from 'app/types';

import { TemplateStep } from '../TemplateStep/TemplateStep';

import { getStyles } from './AlertRuleFromTemplate.styles';

export const AlertRuleFromTemplate: FC = () => {
const dispatch = useDispatch();
const notifyApp = useAppNotification();
const [queryParams] = useQueryParams();
const evaluateEvery = MINUTE;
const returnTo = !queryParams['returnTo'] ? '/alerting/list' : String(queryParams['returnTo']);
const defaultValues: RuleFormValues = useMemo(() => {
return {
...getDefaultFormValues(),
condition: 'C',
queries: getDefaultQueries(),
evaluateEvery: evaluateEvery,
type: RuleFormType.templated,
};
}, [evaluateEvery]);
const methods = useForm({
mode: 'onSubmit',
defaultValues,
shouldFocusError: true,
});
const styles = useStyles2(getStyles);
const submitState = useUnifiedAlertingSelector((state) => state.ruleForm.saveRule) || initialAsyncRequestState;

const submit = (values: RuleFormValues) => {
dispatch(
saveRuleFormAction({
values: {
...defaultValues,
...values,
annotations:
values.annotations
?.map(({ key, value }) => ({ key: key.trim(), value: value.trim() }))
.filter(({ key, value }) => !!key && !!value) ?? [],
labels:
values.labels
?.map(({ key, value }) => ({ key: key.trim(), value: value.trim() }))
.filter(({ key }) => !!key) ?? [],
},
redirectOnSave: returnTo,
initialAlertRuleName: defaultValues.name,
evaluateEvery: evaluateEvery,
})
);
};

const onInvalid = () => {
notifyApp.error('There are errors in the form. Please correct them and try again!');
};

const actionButtons = (
<HorizontalGroup height="auto" justify="flex-end">
<Button
variant="primary"
type="button"
size="sm"
onClick={methods.handleSubmit((values) => submit(values), onInvalid)}
disabled={submitState.loading}
>
{submitState.loading && <Spinner className={styles.buttonSpinner} inline={true} />}
Save rule and exit
</Button>
<Link to={returnTo}>
<Button variant="secondary" disabled={submitState.loading} type="button" size="sm">
Cancel
</Button>
</Link>
</HorizontalGroup>
);

return (
<FormProvider {...methods}>
<AppChromeUpdate actions={actionButtons} />
<Page navId="integrated-alerting-new-from-template">
<TemplateStep />
</Page>
</FormProvider>
);
};

export default AlertRuleFromTemplate;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './AlertRuleFromTemplate';
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,11 @@ export const getStyles = ({ v1: { spacing, typography, colors } }: GrafanaTheme2
font-size: ${typography.size.xs};
color: ${colors.textFaint};
`,
tableWrapper: css`
/* Actions Column */
table thead tr th:last-child {
max-width: fit-content;
width: 275px;
}
`,
});
Original file line number Diff line number Diff line change
Expand Up @@ -136,25 +136,27 @@ export const AlertRuleTemplate: FC = () => {
setVisible={setAddModalVisible}
getAlertRuleTemplates={getAlertRuleTemplates}
/>
<Table
showPagination
totalItems={totalItems}
totalPages={totalPages}
pageSize={pageSize}
pageIndex={pageIndex}
onPaginationChanged={handlePaginationChanged}
data={data}
columns={columns}
pendingRequest={pendingRequest}
emptyMessage={
<EmptyListCTA
title={Messages.alertRuleTemplate.table.noCreated}
buttonIcon="bell"
buttonTitle={Messages.alertRuleTemplate.table.newAlertRuleTemplate}
onClick={handleAddButton}
/>
}
/>
<div className={styles.tableWrapper}>
<Table
showPagination
totalItems={totalItems}
totalPages={totalPages}
pageSize={pageSize}
pageIndex={pageIndex}
onPaginationChanged={handlePaginationChanged}
data={data}
columns={columns}
pendingRequest={pendingRequest}
emptyMessage={
<EmptyListCTA
title={Messages.alertRuleTemplate.table.noCreated}
buttonIcon="bell"
buttonTitle={Messages.alertRuleTemplate.table.newAlertRuleTemplate}
onClick={handleAddButton}
/>
}
/>
</div>
</FeatureLoader>
</Page.Contents>
</Page>
Expand Down
Loading

0 comments on commit a2f0113

Please sign in to comment.