mirror of
https://github.com/grafana/grafana.git
synced 2025-02-20 11:48:34 -06:00
Alerting: allow creating/editing recording rules for Loki and Cortex (#38064)
This commit is contained in:
parent
309d263531
commit
9d8f61c738
@ -1,6 +1,6 @@
|
||||
import { Matcher, render, waitFor } from '@testing-library/react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { locationService, setDataSourceSrv } from '@grafana/runtime';
|
||||
import { BackendSrv, locationService, setBackendSrv, setDataSourceSrv } from '@grafana/runtime';
|
||||
import { configureStore } from 'app/store/configureStore';
|
||||
import RuleEditor from './RuleEditor';
|
||||
import { Route, Router } from 'react-router-dom';
|
||||
@ -19,6 +19,7 @@ import { DashboardSearchHit } from 'app/features/search/types';
|
||||
import { getDefaultQueries } from './utils/rule-form';
|
||||
import { ExpressionEditorProps } from './components/rule-editor/ExpressionEditor';
|
||||
import * as api from 'app/features/manage-dashboards/state/actions';
|
||||
import { GrafanaAlertStateDecision } from 'app/types/unified-alerting-dto';
|
||||
|
||||
jest.mock('./components/rule-editor/ExpressionEditor', () => ({
|
||||
// eslint-disable-next-line react/display-name
|
||||
@ -64,7 +65,7 @@ function renderRuleEditor(identifier?: string) {
|
||||
|
||||
const ui = {
|
||||
inputs: {
|
||||
name: byLabelText('Alert name'),
|
||||
name: byLabelText('Rule name'),
|
||||
alertType: byTestId('alert-type-picker'),
|
||||
dataSource: byTestId('datasource-picker'),
|
||||
folder: byTestId('folder-picker'),
|
||||
@ -228,6 +229,169 @@ describe('RuleEditor', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('can create a new cloud recording rule', async () => {
|
||||
const dataSources = {
|
||||
default: mockDataSource(
|
||||
{
|
||||
type: 'prometheus',
|
||||
name: 'Prom',
|
||||
isDefault: true,
|
||||
},
|
||||
{ alerting: true }
|
||||
),
|
||||
};
|
||||
|
||||
setDataSourceSrv(new MockDataSourceSrv(dataSources));
|
||||
mocks.getAllDataSources.mockReturnValue(Object.values(dataSources));
|
||||
mocks.api.setRulerRuleGroup.mockResolvedValue();
|
||||
mocks.api.fetchRulerRulesNamespace.mockResolvedValue([]);
|
||||
mocks.api.fetchRulerRulesGroup.mockResolvedValue({
|
||||
name: 'group2',
|
||||
rules: [],
|
||||
});
|
||||
mocks.api.fetchRulerRules.mockResolvedValue({
|
||||
namespace1: [
|
||||
{
|
||||
name: 'group1',
|
||||
rules: [],
|
||||
},
|
||||
],
|
||||
namespace2: [
|
||||
{
|
||||
name: 'group2',
|
||||
rules: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await renderRuleEditor();
|
||||
await userEvent.type(await ui.inputs.name.find(), 'my great new recording rule');
|
||||
await clickSelectOption(ui.inputs.alertType.get(), /Cortex\/Loki managed recording rule/);
|
||||
const dataSourceSelect = ui.inputs.dataSource.get();
|
||||
userEvent.click(byRole('textbox').get(dataSourceSelect));
|
||||
await clickSelectOption(dataSourceSelect, 'Prom (default)');
|
||||
await waitFor(() => expect(mocks.api.fetchRulerRules).toHaveBeenCalled());
|
||||
await clickSelectOption(ui.inputs.namespace.get(), 'namespace2');
|
||||
await clickSelectOption(ui.inputs.group.get(), 'group2');
|
||||
|
||||
await userEvent.type(ui.inputs.expr.get(), 'up == 1');
|
||||
|
||||
userEvent.click(ui.buttons.addLabel.get());
|
||||
|
||||
await userEvent.type(ui.inputs.labelKey(1).get(), 'team');
|
||||
await userEvent.type(ui.inputs.labelValue(1).get(), 'the a-team');
|
||||
|
||||
// save and check what was sent to backend
|
||||
userEvent.click(ui.buttons.save.get());
|
||||
await waitFor(() => expect(mocks.api.setRulerRuleGroup).toHaveBeenCalled());
|
||||
expect(mocks.api.setRulerRuleGroup).toHaveBeenCalledWith('Prom', 'namespace2', {
|
||||
name: 'group2',
|
||||
rules: [
|
||||
{
|
||||
record: 'my great new recording rule',
|
||||
labels: { team: 'the a-team' },
|
||||
expr: 'up == 1',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('can edit grafana managed rule', async () => {
|
||||
const uid = 'FOOBAR123';
|
||||
const folder = {
|
||||
title: 'Folder A',
|
||||
uid: 'abcd',
|
||||
id: 1,
|
||||
};
|
||||
jest.spyOn(api, 'searchFolders').mockResolvedValue([folder] as DashboardSearchHit[]);
|
||||
const getFolderByUid = jest.fn().mockResolvedValue({
|
||||
...folder,
|
||||
canSave: true,
|
||||
});
|
||||
const dataSources = {
|
||||
default: mockDataSource({
|
||||
type: 'prometheus',
|
||||
name: 'Prom',
|
||||
isDefault: true,
|
||||
}),
|
||||
};
|
||||
|
||||
const backendSrv = ({
|
||||
getFolderByUid,
|
||||
} as any) as BackendSrv;
|
||||
setBackendSrv(backendSrv);
|
||||
setDataSourceSrv(new MockDataSourceSrv(dataSources));
|
||||
mocks.api.setRulerRuleGroup.mockResolvedValue();
|
||||
mocks.api.fetchRulerRulesNamespace.mockResolvedValue([]);
|
||||
mocks.api.fetchRulerRules.mockResolvedValue({
|
||||
[folder.title]: [
|
||||
{
|
||||
interval: '1m',
|
||||
name: 'my great new rule',
|
||||
rules: [
|
||||
{
|
||||
annotations: { description: 'some description', summary: 'some summary' },
|
||||
labels: { severity: 'warn', team: 'the a-team' },
|
||||
for: '5m',
|
||||
grafana_alert: {
|
||||
uid,
|
||||
namespace_uid: 'abcd',
|
||||
namespace_id: 1,
|
||||
condition: 'B',
|
||||
data: getDefaultQueries(),
|
||||
exec_err_state: GrafanaAlertStateDecision.Alerting,
|
||||
no_data_state: GrafanaAlertStateDecision.NoData,
|
||||
title: 'my great new rule',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await renderRuleEditor(uid);
|
||||
|
||||
// check that it's filled in
|
||||
const nameInput = await ui.inputs.name.find();
|
||||
expect(nameInput).toHaveValue('my great new rule');
|
||||
expect(ui.inputs.folder.get()).toHaveTextContent(new RegExp(folder.title));
|
||||
expect(ui.inputs.annotationValue(0).get()).toHaveValue('some description');
|
||||
expect(ui.inputs.annotationValue(1).get()).toHaveValue('some summary');
|
||||
|
||||
// add an annotation
|
||||
await clickSelectOption(ui.inputs.annotationKey(2).get(), /Add new/);
|
||||
await userEvent.type(byRole('textbox').get(ui.inputs.annotationKey(2).get()), 'custom');
|
||||
await userEvent.type(ui.inputs.annotationValue(2).get(), 'value');
|
||||
|
||||
//add a label
|
||||
await userEvent.type(ui.inputs.labelKey(2).get(), 'custom');
|
||||
await userEvent.type(ui.inputs.labelValue(2).get(), 'value');
|
||||
|
||||
// save and check what was sent to backend
|
||||
userEvent.click(ui.buttons.save.get());
|
||||
await waitFor(() => expect(mocks.api.setRulerRuleGroup).toHaveBeenCalled());
|
||||
|
||||
expect(mocks.api.setRulerRuleGroup).toHaveBeenCalledWith(GRAFANA_RULES_SOURCE_NAME, 'Folder A', {
|
||||
interval: '1m',
|
||||
name: 'my great new rule',
|
||||
rules: [
|
||||
{
|
||||
annotations: { description: 'some description', summary: 'some summary', custom: 'value' },
|
||||
labels: { severity: 'warn', team: 'the a-team', custom: 'value' },
|
||||
for: '5m',
|
||||
grafana_alert: {
|
||||
uid,
|
||||
condition: 'B',
|
||||
data: getDefaultQueries(),
|
||||
exec_err_state: 'Alerting',
|
||||
no_data_state: 'NoData',
|
||||
title: 'my great new rule',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('for cloud alerts, should only allow to select editable rules sources', async () => {
|
||||
const dataSources: Record<string, DataSourceInstanceSettings<any>> = {
|
||||
// can edit rules
|
||||
|
@ -121,7 +121,7 @@ export const AlertRuleForm: FC<Props> = ({ existing }) => {
|
||||
{showStep2 && (
|
||||
<>
|
||||
<QueryStep />
|
||||
{type === RuleFormType.cloud ? <CloudConditionsStep /> : <GrafanaConditionsStep />}
|
||||
{type === RuleFormType.grafana ? <GrafanaConditionsStep /> : <CloudConditionsStep />}
|
||||
<DetailsStep />
|
||||
</>
|
||||
)}
|
||||
|
@ -40,19 +40,24 @@ export const AlertTypeStep: FC<Props> = ({ editingExistingRule }) => {
|
||||
if (contextSrv.isEditor) {
|
||||
result.push({
|
||||
label: 'Cortex/Loki managed alert',
|
||||
value: RuleFormType.cloud,
|
||||
value: RuleFormType.cloudAlerting,
|
||||
description: 'Alert based on a system or application behavior. Based on Prometheus.',
|
||||
});
|
||||
result.push({
|
||||
label: 'Cortex/Loki managed recording rule',
|
||||
value: RuleFormType.cloudRecording,
|
||||
description: 'Recording rule to pre-compute frequently needed or expensive calculations. Based on Prometheus.',
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<RuleEditorSection stepNo={1} title="Alert type">
|
||||
<RuleEditorSection stepNo={1} title="Rule type">
|
||||
<Field
|
||||
className={styles.formInput}
|
||||
label="Alert name"
|
||||
label="Rule name"
|
||||
error={errors?.name?.message}
|
||||
invalid={!!errors.name?.message}
|
||||
>
|
||||
@ -65,7 +70,7 @@ export const AlertTypeStep: FC<Props> = ({ editingExistingRule }) => {
|
||||
<div className={styles.flexRow}>
|
||||
<Field
|
||||
disabled={editingExistingRule}
|
||||
label="Alert type"
|
||||
label="Rule type"
|
||||
className={styles.formInput}
|
||||
error={errors.type?.message}
|
||||
invalid={!!errors.type?.message}
|
||||
@ -87,7 +92,7 @@ export const AlertTypeStep: FC<Props> = ({ editingExistingRule }) => {
|
||||
}}
|
||||
/>
|
||||
</Field>
|
||||
{ruleFormType === RuleFormType.cloud && (
|
||||
{(ruleFormType === RuleFormType.cloudRecording || ruleFormType === RuleFormType.cloudAlerting) && (
|
||||
<Field
|
||||
className={styles.formInput}
|
||||
label="Select data source"
|
||||
@ -115,9 +120,9 @@ export const AlertTypeStep: FC<Props> = ({ editingExistingRule }) => {
|
||||
</Field>
|
||||
)}
|
||||
</div>
|
||||
{ruleFormType === RuleFormType.cloud && dataSourceName && (
|
||||
<GroupAndNamespaceFields dataSourceName={dataSourceName} />
|
||||
)}
|
||||
{(ruleFormType === RuleFormType.cloudRecording || ruleFormType === RuleFormType.cloudAlerting) &&
|
||||
dataSourceName && <GroupAndNamespaceFields dataSourceName={dataSourceName} />}
|
||||
|
||||
{ruleFormType === RuleFormType.grafana && (
|
||||
<Field
|
||||
label="Folder"
|
||||
|
@ -3,7 +3,7 @@ import { css } from '@emotion/css';
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
import { Field, Input, InputControl, Select, useStyles } from '@grafana/ui';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { RuleFormValues } from '../../types/rule-form';
|
||||
import { RuleFormType, RuleFormValues } from '../../types/rule-form';
|
||||
import { timeOptions } from '../../utils/time';
|
||||
import { RuleEditorSection } from './RuleEditorSection';
|
||||
import { PreviewRule } from './PreviewRule';
|
||||
@ -13,9 +13,17 @@ export const CloudConditionsStep: FC = () => {
|
||||
const {
|
||||
register,
|
||||
control,
|
||||
watch,
|
||||
formState: { errors },
|
||||
} = useFormContext<RuleFormValues>();
|
||||
|
||||
const type = watch('type');
|
||||
|
||||
// cloud recording rules do not have alert conditions
|
||||
if (type === RuleFormType.cloudRecording) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<RuleEditorSection stepNo={3} title="Define alert conditions">
|
||||
<Field label="For" description="Expression has to be true for this long for the alert to be fired.">
|
||||
|
@ -2,15 +2,27 @@ import React, { FC } from 'react';
|
||||
import LabelsField from './LabelsField';
|
||||
import AnnotationsField from './AnnotationsField';
|
||||
import { RuleEditorSection } from './RuleEditorSection';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { RuleFormType, RuleFormValues } from '../../types/rule-form';
|
||||
|
||||
export const DetailsStep: FC = () => {
|
||||
const { watch } = useFormContext<RuleFormValues>();
|
||||
|
||||
const type = watch('type');
|
||||
|
||||
return (
|
||||
<RuleEditorSection
|
||||
stepNo={4}
|
||||
title="Add details for your alert"
|
||||
description="Write a summary and add labels to help you better manage your alerts"
|
||||
stepNo={type === RuleFormType.cloudRecording ? 3 : 4}
|
||||
title={
|
||||
type === RuleFormType.cloudRecording ? 'Add details for your recording rule' : 'Add details for your alert'
|
||||
}
|
||||
description={
|
||||
type === RuleFormType.cloudRecording
|
||||
? 'Add labels to help you better manage your rules'
|
||||
: 'Write a summary and add labels to help you better manage your alerts'
|
||||
}
|
||||
>
|
||||
<AnnotationsField />
|
||||
{type !== RuleFormType.cloudRecording && <AnnotationsField />}
|
||||
<LabelsField />
|
||||
</RuleEditorSection>
|
||||
);
|
||||
|
@ -18,7 +18,7 @@ export function PreviewRule(): React.ReactElement | null {
|
||||
const { getValues } = useFormContext();
|
||||
const [type] = getValues(fields);
|
||||
|
||||
if (type === RuleFormType.cloud) {
|
||||
if (type === RuleFormType.cloudRecording || type === RuleFormType.cloudAlerting) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -60,7 +60,7 @@ function createPreviewRequest(values: any[]): PreviewRuleRequest {
|
||||
const [type, dataSourceName, condition, queries, expression] = values;
|
||||
|
||||
switch (type) {
|
||||
case RuleFormType.cloud:
|
||||
case RuleFormType.cloudAlerting:
|
||||
return {
|
||||
dataSourceName,
|
||||
expr: expression,
|
||||
|
@ -15,8 +15,11 @@ export const QueryStep: FC = () => {
|
||||
const type = watch('type');
|
||||
const dataSourceName = watch('dataSourceName');
|
||||
return (
|
||||
<RuleEditorSection stepNo={2} title="Create a query to be alerted on">
|
||||
{type === RuleFormType.cloud && dataSourceName && (
|
||||
<RuleEditorSection
|
||||
stepNo={2}
|
||||
title={type === RuleFormType.cloudRecording ? 'Create a query to be recorded' : 'Create a query to be alerted on'}
|
||||
>
|
||||
{(type === RuleFormType.cloudRecording || type === RuleFormType.cloudAlerting) && dataSourceName && (
|
||||
<Field error={errors.expression?.message} invalid={!!errors.expression?.message}>
|
||||
<InputControl
|
||||
name="expression"
|
||||
|
@ -63,7 +63,7 @@ export function RuleListErrors(): ReactElement {
|
||||
|
||||
return (
|
||||
<>
|
||||
{errors.length && !closed && (
|
||||
{!!errors.length && !closed && (
|
||||
<Alert
|
||||
data-testid="cloud-rulessource-errors"
|
||||
title="Errors loading rules"
|
||||
|
@ -6,6 +6,7 @@ import { useUnifiedAlertingSelector } from './useUnifiedAlertingSelector';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useEffect } from 'react';
|
||||
import { checkIfLotexSupportsEditingRulesAction } from '../state/actions';
|
||||
import { GRAFANA_RULES_SOURCE_NAME } from '../utils/datasource';
|
||||
|
||||
interface ResultBag {
|
||||
isEditable?: boolean;
|
||||
@ -20,7 +21,7 @@ export function useIsRuleEditable(rulesSourceName: string, rule?: RulerRuleDTO):
|
||||
const { folder, loading } = useFolder(folderUID);
|
||||
|
||||
useEffect(() => {
|
||||
if (checkEditingRequests[rulesSourceName] === undefined) {
|
||||
if (checkEditingRequests[rulesSourceName] === undefined && rulesSourceName !== GRAFANA_RULES_SOURCE_NAME) {
|
||||
dispatch(checkIfLotexSupportsEditingRulesAction(rulesSourceName));
|
||||
}
|
||||
}, [rulesSourceName, checkEditingRequests, dispatch]);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { locationService } from '@grafana/runtime';
|
||||
import { getBackendSrv, locationService } from '@grafana/runtime';
|
||||
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||
import {
|
||||
AlertmanagerAlert,
|
||||
@ -41,7 +41,7 @@ import { RuleFormType, RuleFormValues } from '../types/rule-form';
|
||||
import { getAllRulesSourceNames, GRAFANA_RULES_SOURCE_NAME, isGrafanaRulesSource } from '../utils/datasource';
|
||||
import { makeAMLink, retryWhile } from '../utils/misc';
|
||||
import { isFetchError, withAppEvents, withSerializedError } from '../utils/redux';
|
||||
import { formValuesToRulerAlertingRuleDTO, formValuesToRulerGrafanaRuleDTO } from '../utils/rule-form';
|
||||
import { formValuesToRulerRuleDTO, formValuesToRulerGrafanaRuleDTO } from '../utils/rule-form';
|
||||
import {
|
||||
isCloudRuleIdentifier,
|
||||
isGrafanaRuleIdentifier,
|
||||
@ -50,7 +50,6 @@ import {
|
||||
isRulerNotSupportedResponse,
|
||||
} from '../utils/rules';
|
||||
import { addDefaultsToAlertmanagerConfig } from '../utils/alertmanager';
|
||||
import { backendSrv } from 'app/core/services/backend_srv';
|
||||
import * as ruleId from '../utils/rule-id';
|
||||
import { isEmpty } from 'lodash';
|
||||
import messageFromError from 'app/plugins/datasource/grafana-azure-monitor-datasource/utils/messageFromError';
|
||||
@ -251,7 +250,7 @@ export function deleteRuleAction(
|
||||
|
||||
async function saveLotexRule(values: RuleFormValues, existing?: RuleWithLocation): Promise<RuleIdentifier> {
|
||||
const { dataSourceName, group, namespace } = values;
|
||||
const formRule = formValuesToRulerAlertingRuleDTO(values);
|
||||
const formRule = formValuesToRulerRuleDTO(values);
|
||||
if (dataSourceName && group && namespace) {
|
||||
// if we're updating a rule...
|
||||
if (existing) {
|
||||
@ -373,7 +372,7 @@ export const saveRuleFormAction = createAsyncThunk(
|
||||
const { type } = values;
|
||||
// in case of system (cortex/loki)
|
||||
let identifier: RuleIdentifier;
|
||||
if (type === RuleFormType.cloud) {
|
||||
if (type === RuleFormType.cloudAlerting || type === RuleFormType.cloudRecording) {
|
||||
identifier = await saveLotexRule(values, existing);
|
||||
// in case of grafana managed
|
||||
} else if (type === RuleFormType.grafana) {
|
||||
@ -545,7 +544,7 @@ export const deleteTemplateAction = (templateName: string, alertManagerSourceNam
|
||||
|
||||
export const fetchFolderAction = createAsyncThunk(
|
||||
'unifiedalerting/fetchFolder',
|
||||
(uid: string): Promise<FolderDTO> => withSerializedError(backendSrv.getFolderByUid(uid))
|
||||
(uid: string): Promise<FolderDTO> => withSerializedError((getBackendSrv() as any).getFolderByUid(uid))
|
||||
);
|
||||
|
||||
export const fetchFolderIfNotFetchedAction = (uid: string): ThunkResult<void> => {
|
||||
|
@ -2,7 +2,8 @@ import { AlertQuery, GrafanaAlertStateDecision } from 'app/types/unified-alertin
|
||||
|
||||
export enum RuleFormType {
|
||||
grafana = 'grafana',
|
||||
cloud = 'cloud',
|
||||
cloudAlerting = 'cloud-alerting',
|
||||
cloudRecording = 'cloud-recording',
|
||||
}
|
||||
|
||||
export interface RuleFormValues {
|
||||
|
@ -20,14 +20,14 @@ import {
|
||||
AlertQuery,
|
||||
Labels,
|
||||
PostableRuleGrafanaRuleDTO,
|
||||
RulerAlertingRuleDTO,
|
||||
RulerRuleDTO,
|
||||
} from 'app/types/unified-alerting-dto';
|
||||
import { EvalFunction } from '../../state/alertDef';
|
||||
import { RuleFormType, RuleFormValues } from '../types/rule-form';
|
||||
import { Annotation } from './constants';
|
||||
import { isGrafanaRulesSource } from './datasource';
|
||||
import { arrayToRecord, recordToArray } from './misc';
|
||||
import { isAlertingRulerRule, isGrafanaRulerRule } from './rules';
|
||||
import { isAlertingRulerRule, isGrafanaRulerRule, isRecordingRulerRule } from './rules';
|
||||
import { parseInterval } from './time';
|
||||
|
||||
export const getDefaultFormValues = (): RuleFormValues =>
|
||||
@ -59,15 +59,24 @@ export const getDefaultFormValues = (): RuleFormValues =>
|
||||
forTimeUnit: 'm',
|
||||
});
|
||||
|
||||
export function formValuesToRulerAlertingRuleDTO(values: RuleFormValues): RulerAlertingRuleDTO {
|
||||
const { name, expression, forTime, forTimeUnit } = values;
|
||||
return {
|
||||
alert: name,
|
||||
for: `${forTime}${forTimeUnit}`,
|
||||
annotations: arrayToRecord(values.annotations || []),
|
||||
labels: arrayToRecord(values.labels || []),
|
||||
expr: expression,
|
||||
};
|
||||
export function formValuesToRulerRuleDTO(values: RuleFormValues): RulerRuleDTO {
|
||||
const { name, expression, forTime, forTimeUnit, type } = values;
|
||||
if (type === RuleFormType.cloudAlerting) {
|
||||
return {
|
||||
alert: name,
|
||||
for: `${forTime}${forTimeUnit}`,
|
||||
annotations: arrayToRecord(values.annotations || []),
|
||||
labels: arrayToRecord(values.labels || []),
|
||||
expr: expression,
|
||||
};
|
||||
} else if (type === RuleFormType.cloudRecording) {
|
||||
return {
|
||||
record: name,
|
||||
labels: arrayToRecord(values.labels || []),
|
||||
expr: expression,
|
||||
};
|
||||
}
|
||||
throw new Error(`unexpected rule type: ${type}`);
|
||||
}
|
||||
|
||||
function listifyLabelsOrAnnotations(item: Labels | Annotations | undefined): Array<{ key: string; value: string }> {
|
||||
@ -125,7 +134,7 @@ export function rulerRuleToFormValues(ruleWithLocation: RuleWithLocation): RuleF
|
||||
return {
|
||||
...defaultFormValues,
|
||||
name: rule.alert,
|
||||
type: RuleFormType.cloud,
|
||||
type: RuleFormType.cloudAlerting,
|
||||
dataSourceName: ruleSourceName,
|
||||
namespace,
|
||||
group: group.name,
|
||||
@ -135,8 +144,19 @@ export function rulerRuleToFormValues(ruleWithLocation: RuleWithLocation): RuleF
|
||||
annotations: listifyLabelsOrAnnotations(rule.annotations),
|
||||
labels: listifyLabelsOrAnnotations(rule.labels),
|
||||
};
|
||||
} else if (isRecordingRulerRule(rule)) {
|
||||
return {
|
||||
...defaultFormValues,
|
||||
name: rule.record,
|
||||
type: RuleFormType.cloudRecording,
|
||||
dataSourceName: ruleSourceName,
|
||||
namespace,
|
||||
group: group.name,
|
||||
expression: rule.expr,
|
||||
labels: listifyLabelsOrAnnotations(rule.labels),
|
||||
};
|
||||
} else {
|
||||
throw new Error('Editing recording rules not supported (yet)');
|
||||
throw new Error('Unexpected type of rule for cloud rules source');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user