mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Add new button for exporting new alert rule in HCL format (#96785)
* add new button for exporting new alert rule * Fix test * allow only HCL format for exporting new alert rule * fix initial tab * Update public/app/features/alerting/unified/components/rule-editor/alert-rule-form/ModifyExportRuleForm.tsx Co-authored-by: Konrad Lalik <konradlalik@gmail.com> * Update public/app/features/alerting/unified/components/rule-editor/alert-rule-form/ModifyExportRuleForm.tsx Co-authored-by: Konrad Lalik <konradlalik@gmail.com> * address review comments * update translations --------- Co-authored-by: Konrad Lalik <konradlalik@gmail.com>
This commit is contained in:
parent
2736fe0568
commit
624f44fdb5
@ -227,6 +227,17 @@ export function getAlertingRoutes(cfg = config): RouteDescriptor[] {
|
|||||||
)
|
)
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/alerting/export-new-rule',
|
||||||
|
pageClass: 'page-alerting',
|
||||||
|
roles: evaluateAccess([AccessControlAction.AlertingRuleRead]),
|
||||||
|
component: importAlertingComponent(
|
||||||
|
() =>
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: "AlertingRuleForm"*/ 'app/features/alerting/unified/components/export/ExportNewGrafanaRule'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/alerting/:sourceName/:id/view',
|
path: '/alerting/:sourceName/:id/view',
|
||||||
pageClass: 'page-alerting',
|
pageClass: 'page-alerting',
|
||||||
|
@ -27,6 +27,7 @@ export const LogMessages = {
|
|||||||
unknownMessageFromError: 'unknown messageFromError',
|
unknownMessageFromError: 'unknown messageFromError',
|
||||||
grafanaRecording: 'creating Grafana recording rule from scratch',
|
grafanaRecording: 'creating Grafana recording rule from scratch',
|
||||||
loadedCentralAlertStateHistory: 'loaded central alert state history',
|
loadedCentralAlertStateHistory: 'loaded central alert state history',
|
||||||
|
exportNewGrafanaRule: 'exporting new Grafana rule',
|
||||||
};
|
};
|
||||||
|
|
||||||
const { logInfo, logError, logMeasurement, logWarning } = createMonitoringLogger('features.alerting', {
|
const { logInfo, logError, logMeasurement, logWarning } = createMonitoringLogger('features.alerting', {
|
||||||
|
@ -120,7 +120,7 @@ const ui = {
|
|||||||
rulesFilterInput: byTestId('search-query-input'),
|
rulesFilterInput: byTestId('search-query-input'),
|
||||||
moreErrorsButton: byRole('button', { name: /more errors/ }),
|
moreErrorsButton: byRole('button', { name: /more errors/ }),
|
||||||
editCloudGroupIcon: byTestId('edit-group'),
|
editCloudGroupIcon: byTestId('edit-group'),
|
||||||
newRuleButton: byText(/new alert rule/i),
|
newRuleButton: byRole('link', { name: 'New alert rule' }),
|
||||||
exportButton: byText(/export rules/i),
|
exportButton: byText(/export rules/i),
|
||||||
editGroupModal: {
|
editGroupModal: {
|
||||||
dialog: byRole('dialog'),
|
dialog: byRole('dialog'),
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { AlertingPageWrapper } from '../AlertingPageWrapper';
|
||||||
|
import { ModifyExportRuleForm } from '../rule-editor/alert-rule-form/ModifyExportRuleForm';
|
||||||
|
|
||||||
|
export default function ExportNewGrafanaRule() {
|
||||||
|
return (
|
||||||
|
<ExportNewGrafanaRuleWrapper>
|
||||||
|
<ModifyExportRuleForm />
|
||||||
|
</ExportNewGrafanaRuleWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ExportNewGrafanaRuleWrapperProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ExportNewGrafanaRuleWrapper({ children }: ExportNewGrafanaRuleWrapperProps) {
|
||||||
|
return (
|
||||||
|
<AlertingPageWrapper
|
||||||
|
navId="alert-list"
|
||||||
|
pageNav={{
|
||||||
|
text: 'Export new Grafana rule',
|
||||||
|
subTitle: 'Export a new rule definition in Terraform(HCL) format. Any changes you make will not be saved.',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</AlertingPageWrapper>
|
||||||
|
);
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import { Drawer } from '@grafana/ui';
|
import { Drawer } from '@grafana/ui';
|
||||||
|
import { t } from 'app/core/internationalization';
|
||||||
|
|
||||||
import { RuleInspectorTabs } from '../rule-editor/RuleInspector';
|
import { RuleInspectorTabs } from '../rule-editor/RuleInspector';
|
||||||
|
|
||||||
@ -27,10 +28,17 @@ export function GrafanaExportDrawer({
|
|||||||
label: provider.name,
|
label: provider.name,
|
||||||
value: provider.exportFormat,
|
value: provider.exportFormat,
|
||||||
}));
|
}));
|
||||||
|
const subtitle =
|
||||||
|
formatProviders.length > 1
|
||||||
|
? t(
|
||||||
|
'alerting.export.subtitle.formats',
|
||||||
|
'Select the format and download the file or copy the contents to clipboard'
|
||||||
|
)
|
||||||
|
: t('alerting.export.subtitle.one-format', 'Download the file or copy the contents to clipboard');
|
||||||
return (
|
return (
|
||||||
<Drawer
|
<Drawer
|
||||||
title={title}
|
title={title}
|
||||||
subtitle="Select the format and download the file or copy the contents to clipboard"
|
subtitle={subtitle}
|
||||||
tabs={
|
tabs={
|
||||||
<RuleInspectorTabs<ExportFormats> tabs={grafanaRulesTabs} setActiveTab={onTabChange} activeTab={activeTab} />
|
<RuleInspectorTabs<ExportFormats> tabs={grafanaRulesTabs} setActiveTab={onTabChange} activeTab={activeTab} />
|
||||||
}
|
}
|
||||||
|
@ -15,13 +15,18 @@ import { alertRuleApi } from '../../../api/alertRuleApi';
|
|||||||
import { fetchRulerRulesGroup } from '../../../api/ruler';
|
import { fetchRulerRulesGroup } from '../../../api/ruler';
|
||||||
import { useDataSourceFeatures } from '../../../hooks/useCombinedRule';
|
import { useDataSourceFeatures } from '../../../hooks/useCombinedRule';
|
||||||
import { useReturnTo } from '../../../hooks/useReturnTo';
|
import { useReturnTo } from '../../../hooks/useReturnTo';
|
||||||
import { RuleFormValues } from '../../../types/rule-form';
|
import { RuleFormType, RuleFormValues } from '../../../types/rule-form';
|
||||||
import { GRAFANA_RULES_SOURCE_NAME } from '../../../utils/datasource';
|
import { GRAFANA_RULES_SOURCE_NAME } from '../../../utils/datasource';
|
||||||
import { DEFAULT_GROUP_EVALUATION_INTERVAL, formValuesToRulerGrafanaRuleDTO } from '../../../utils/rule-form';
|
import {
|
||||||
|
DEFAULT_GROUP_EVALUATION_INTERVAL,
|
||||||
|
formValuesToRulerGrafanaRuleDTO,
|
||||||
|
getDefaultFormValues,
|
||||||
|
getDefaultQueries,
|
||||||
|
} from '../../../utils/rule-form';
|
||||||
import { isGrafanaRulerRule } from '../../../utils/rules';
|
import { isGrafanaRulerRule } from '../../../utils/rules';
|
||||||
import { FileExportPreview } from '../../export/FileExportPreview';
|
import { FileExportPreview } from '../../export/FileExportPreview';
|
||||||
import { GrafanaExportDrawer } from '../../export/GrafanaExportDrawer';
|
import { GrafanaExportDrawer } from '../../export/GrafanaExportDrawer';
|
||||||
import { ExportFormats, allGrafanaExportProviders } from '../../export/providers';
|
import { ExportFormats, HclExportProvider, allGrafanaExportProviders } from '../../export/providers';
|
||||||
import { AlertRuleNameAndMetric } from '../AlertRuleNameInput';
|
import { AlertRuleNameAndMetric } from '../AlertRuleNameInput';
|
||||||
import AnnotationsStep from '../AnnotationsStep';
|
import AnnotationsStep from '../AnnotationsStep';
|
||||||
import { GrafanaEvaluationBehaviorStep } from '../GrafanaEvaluationBehavior';
|
import { GrafanaEvaluationBehaviorStep } from '../GrafanaEvaluationBehavior';
|
||||||
@ -30,18 +35,30 @@ import { NotificationsStep } from '../NotificationsStep';
|
|||||||
import { QueryAndExpressionsStep } from '../query-and-alert-condition/QueryAndExpressionsStep';
|
import { QueryAndExpressionsStep } from '../query-and-alert-condition/QueryAndExpressionsStep';
|
||||||
|
|
||||||
interface ModifyExportRuleFormProps {
|
interface ModifyExportRuleFormProps {
|
||||||
alertUid: string;
|
alertUid?: string;
|
||||||
ruleForm?: RuleFormValues;
|
ruleForm?: RuleFormValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ModifyExportRuleForm({ ruleForm, alertUid }: ModifyExportRuleFormProps) {
|
export function ModifyExportRuleForm({ ruleForm, alertUid }: ModifyExportRuleFormProps) {
|
||||||
|
const defaultValuesForNewRule: RuleFormValues = useMemo(() => {
|
||||||
|
const defaultRuleType = RuleFormType.grafana;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...getDefaultFormValues(),
|
||||||
|
condition: 'C',
|
||||||
|
queries: getDefaultQueries(false),
|
||||||
|
type: defaultRuleType,
|
||||||
|
evaluateEvery: DEFAULT_GROUP_EVALUATION_INTERVAL,
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
const formAPI = useForm<RuleFormValues>({
|
const formAPI = useForm<RuleFormValues>({
|
||||||
mode: 'onSubmit',
|
mode: 'onSubmit',
|
||||||
defaultValues: ruleForm,
|
defaultValues: ruleForm ?? defaultValuesForNewRule,
|
||||||
shouldFocusError: true,
|
shouldFocusError: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const existing = Boolean(ruleForm); // always should be true
|
const existing = Boolean(ruleForm);
|
||||||
const notifyApp = useAppNotification();
|
const notifyApp = useAppNotification();
|
||||||
const { returnTo } = useReturnTo('/alerting/list');
|
const { returnTo } = useReturnTo('/alerting/list');
|
||||||
|
|
||||||
@ -129,21 +146,21 @@ interface GrafanaRuleDesignExportPreviewProps {
|
|||||||
exportFormat: ExportFormats;
|
exportFormat: ExportFormats;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
exportValues: RuleFormValues;
|
exportValues: RuleFormValues;
|
||||||
uid: string;
|
uid?: string;
|
||||||
}
|
}
|
||||||
export const getPayloadToExport = (
|
export const getPayloadToExport = (
|
||||||
uid: string,
|
|
||||||
formValues: RuleFormValues,
|
formValues: RuleFormValues,
|
||||||
existingGroup: RulerRuleGroupDTO<RulerRuleDTO> | null | undefined
|
existingGroup: RulerRuleGroupDTO<RulerRuleDTO> | null | undefined,
|
||||||
|
ruleUid?: string
|
||||||
): PostableRulerRuleGroupDTO => {
|
): PostableRulerRuleGroupDTO => {
|
||||||
const grafanaRuleDto = formValuesToRulerGrafanaRuleDTO(formValues);
|
const grafanaRuleDto = formValuesToRulerGrafanaRuleDTO(formValues);
|
||||||
|
|
||||||
const updatedRule = { ...grafanaRuleDto, grafana_alert: { ...grafanaRuleDto.grafana_alert, uid: uid } };
|
const updatedRule = { ...grafanaRuleDto, grafana_alert: { ...grafanaRuleDto.grafana_alert, uid: ruleUid } };
|
||||||
if (existingGroup?.rules) {
|
if (existingGroup?.rules) {
|
||||||
// we have to update the rule in the group in the same position if it exists, otherwise we have to add it at the end
|
// we have to update the rule in the group in the same position if it exists, otherwise we have to add it at the end
|
||||||
let alreadyExistsInGroup = false;
|
let alreadyExistsInGroup = false;
|
||||||
const updatedRules = existingGroup.rules.map((rule: RulerRuleDTO) => {
|
const updatedRules = existingGroup.rules.map((rule: RulerRuleDTO) => {
|
||||||
if (isGrafanaRulerRule(rule) && rule.grafana_alert.uid === uid) {
|
if (isGrafanaRulerRule(rule) && rule.grafana_alert.uid === ruleUid) {
|
||||||
alreadyExistsInGroup = true;
|
alreadyExistsInGroup = true;
|
||||||
return updatedRule;
|
return updatedRule;
|
||||||
} else {
|
} else {
|
||||||
@ -167,11 +184,11 @@ export const getPayloadToExport = (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const useGetPayloadToExport = (values: RuleFormValues, uid: string) => {
|
const useGetPayloadToExport = (values: RuleFormValues, ruleUid?: string) => {
|
||||||
const rulerGroupDto = useGetGroup(values.folder?.uid ?? '', values.group);
|
const rulerGroupDto = useGetGroup(values.folder?.uid ?? '', values.group);
|
||||||
const payload: PostableRulerRuleGroupDTO = useMemo(() => {
|
const payload: PostableRulerRuleGroupDTO = useMemo(() => {
|
||||||
return getPayloadToExport(uid, values, rulerGroupDto?.value);
|
return getPayloadToExport(values, rulerGroupDto?.value, ruleUid);
|
||||||
}, [uid, rulerGroupDto, values]);
|
}, [ruleUid, rulerGroupDto, values]);
|
||||||
return { payload, loadingGroup: rulerGroupDto.loading };
|
return { payload, loadingGroup: rulerGroupDto.loading };
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -187,7 +204,7 @@ const GrafanaRuleDesignExportPreview = ({
|
|||||||
const nameSpaceUID = exportValues.folder?.uid ?? '';
|
const nameSpaceUID = exportValues.folder?.uid ?? '';
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
!loadingGroup && getExport({ payload, format: exportFormat, nameSpaceUID });
|
!loadingGroup && payload.name && getExport({ payload, format: exportFormat, nameSpaceUID });
|
||||||
}, [nameSpaceUID, exportFormat, payload, getExport, loadingGroup]);
|
}, [nameSpaceUID, exportFormat, payload, getExport, loadingGroup]);
|
||||||
|
|
||||||
if (exportData.isLoading) {
|
if (exportData.isLoading) {
|
||||||
@ -209,11 +226,14 @@ const GrafanaRuleDesignExportPreview = ({
|
|||||||
interface GrafanaRuleDesignExporterProps {
|
interface GrafanaRuleDesignExporterProps {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
exportValues: RuleFormValues;
|
exportValues: RuleFormValues;
|
||||||
uid: string;
|
uid?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GrafanaRuleDesignExporter = memo(({ onClose, exportValues, uid }: GrafanaRuleDesignExporterProps) => {
|
export const GrafanaRuleDesignExporter = memo(({ onClose, exportValues, uid }: GrafanaRuleDesignExporterProps) => {
|
||||||
const [activeTab, setActiveTab] = useState<ExportFormats>('yaml');
|
const exportingNewRule = !uid;
|
||||||
|
const initialTab = exportingNewRule ? 'hcl' : 'yaml';
|
||||||
|
const [activeTab, setActiveTab] = useState<ExportFormats>(initialTab);
|
||||||
|
const formatProviders = exportingNewRule ? [HclExportProvider] : Object.values(allGrafanaExportProviders);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GrafanaExportDrawer
|
<GrafanaExportDrawer
|
||||||
@ -221,7 +241,7 @@ export const GrafanaRuleDesignExporter = memo(({ onClose, exportValues, uid }: G
|
|||||||
activeTab={activeTab}
|
activeTab={activeTab}
|
||||||
onTabChange={setActiveTab}
|
onTabChange={setActiveTab}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
formatProviders={Object.values(allGrafanaExportProviders)}
|
formatProviders={formatProviders}
|
||||||
>
|
>
|
||||||
<GrafanaRuleDesignExportPreview
|
<GrafanaRuleDesignExportPreview
|
||||||
exportFormat={activeTab}
|
exportFormat={activeTab}
|
||||||
|
@ -162,16 +162,16 @@ describe('getPayloadFromDto', () => {
|
|||||||
|
|
||||||
it('should return a ModifyExportPayload with the updated rule added to a group with this rule belongs, in the same position', () => {
|
it('should return a ModifyExportPayload with the updated rule added to a group with this rule belongs, in the same position', () => {
|
||||||
// for alerting rule
|
// for alerting rule
|
||||||
const resultForAlerting = getPayloadToExport('uid-rule-2', formValuesForRule2Updated, groupDto);
|
const resultForAlerting = getPayloadToExport(formValuesForRule2Updated, groupDto, 'uid-rule-2');
|
||||||
expect(resultForAlerting).toEqual({
|
expect(resultForAlerting).toEqual({
|
||||||
name: 'Test Group',
|
name: 'Test Group',
|
||||||
rules: [rule1, expectedModifiedRule2('uid-rule-2'), rule3, rule4],
|
rules: [rule1, expectedModifiedRule2('uid-rule-2'), rule3, rule4],
|
||||||
});
|
});
|
||||||
// for recording rule
|
// for recording rule
|
||||||
const resultForRecording = getPayloadToExport(
|
const resultForRecording = getPayloadToExport(
|
||||||
'uid-rule-4',
|
|
||||||
{ ...formValuesForRecordingRule4Updated, type: RuleFormType.grafanaRecording },
|
{ ...formValuesForRecordingRule4Updated, type: RuleFormType.grafanaRecording },
|
||||||
groupDto
|
groupDto,
|
||||||
|
'uid-rule-4'
|
||||||
);
|
);
|
||||||
expect(resultForRecording).toEqual({
|
expect(resultForRecording).toEqual({
|
||||||
name: 'Test Group',
|
name: 'Test Group',
|
||||||
@ -180,16 +180,16 @@ describe('getPayloadFromDto', () => {
|
|||||||
});
|
});
|
||||||
it('should return a ModifyExportPayload with the updated rule added to a non empty rule where this rule does not belong, in the last position', () => {
|
it('should return a ModifyExportPayload with the updated rule added to a non empty rule where this rule does not belong, in the last position', () => {
|
||||||
// for alerting rule
|
// for alerting rule
|
||||||
const result = getPayloadToExport('uid-rule-5', formValuesForRule2Updated, groupDto);
|
const result = getPayloadToExport(formValuesForRule2Updated, groupDto, 'uid-rule-5');
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
name: 'Test Group',
|
name: 'Test Group',
|
||||||
rules: [rule1, rule2, rule3, rule4, expectedModifiedRule2('uid-rule-5')],
|
rules: [rule1, rule2, rule3, rule4, expectedModifiedRule2('uid-rule-5')],
|
||||||
});
|
});
|
||||||
// for recording rule
|
// for recording rule
|
||||||
const resultForRecording = getPayloadToExport(
|
const resultForRecording = getPayloadToExport(
|
||||||
'uid-rule-5',
|
|
||||||
{ ...formValuesForRecordingRule4Updated, type: RuleFormType.grafanaRecording },
|
{ ...formValuesForRecordingRule4Updated, type: RuleFormType.grafanaRecording },
|
||||||
groupDto
|
groupDto,
|
||||||
|
'uid-rule-5'
|
||||||
);
|
);
|
||||||
expect(resultForRecording).toEqual({
|
expect(resultForRecording).toEqual({
|
||||||
name: 'Test Group',
|
name: 'Test Group',
|
||||||
@ -202,7 +202,7 @@ describe('getPayloadFromDto', () => {
|
|||||||
name: 'Empty Group',
|
name: 'Empty Group',
|
||||||
rules: [],
|
rules: [],
|
||||||
};
|
};
|
||||||
const result = getPayloadToExport('uid-rule-2', formValuesForRule2Updated, emptyGroupDto);
|
const result = getPayloadToExport(formValuesForRule2Updated, emptyGroupDto, 'uid-rule-2');
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
name: 'Empty Group',
|
name: 'Empty Group',
|
||||||
rules: [expectedModifiedRule2('uid-rule-2')],
|
rules: [expectedModifiedRule2('uid-rule-2')],
|
||||||
|
@ -27,6 +27,7 @@ import { useUnifiedAlertingSelector } from '../hooks/useUnifiedAlertingSelector'
|
|||||||
import { fetchAllPromAndRulerRulesAction, fetchAllPromRulesAction, fetchRulerRulesAction } from '../state/actions';
|
import { fetchAllPromAndRulerRulesAction, fetchAllPromRulesAction, fetchRulerRulesAction } from '../state/actions';
|
||||||
import { RULE_LIST_POLL_INTERVAL_MS } from '../utils/constants';
|
import { RULE_LIST_POLL_INTERVAL_MS } from '../utils/constants';
|
||||||
import { getAllRulesSourceNames, GRAFANA_RULES_SOURCE_NAME } from '../utils/datasource';
|
import { getAllRulesSourceNames, GRAFANA_RULES_SOURCE_NAME } from '../utils/datasource';
|
||||||
|
import { createRelativeUrl } from '../utils/url';
|
||||||
|
|
||||||
const VIEWS = {
|
const VIEWS = {
|
||||||
groups: RuleListGroupView,
|
groups: RuleListGroupView,
|
||||||
@ -119,7 +120,17 @@ const RuleListV1 = () => {
|
|||||||
return (
|
return (
|
||||||
// We don't want to show the Loading... indicator for the whole page.
|
// We don't want to show the Loading... indicator for the whole page.
|
||||||
// We show separate indicators for Grafana-managed and Cloud rules
|
// We show separate indicators for Grafana-managed and Cloud rules
|
||||||
<AlertingPageWrapper navId="alert-list" isLoading={false} actions={hasAlertRulesCreated && <CreateAlertButton />}>
|
<AlertingPageWrapper
|
||||||
|
navId="alert-list"
|
||||||
|
isLoading={false}
|
||||||
|
actions={
|
||||||
|
hasAlertRulesCreated && (
|
||||||
|
<Stack gap={1}>
|
||||||
|
<CreateAlertButton /> <ExportNewRuleButton />
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
<Stack direction="column">
|
<Stack direction="column">
|
||||||
<RuleListErrors />
|
<RuleListErrors />
|
||||||
<RulesFilter onClear={onFilterCleared} />
|
<RulesFilter onClear={onFilterCleared} />
|
||||||
@ -169,3 +180,21 @@ export function CreateAlertButton() {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ExportNewRuleButton() {
|
||||||
|
const returnTo = location.pathname + location.search;
|
||||||
|
const url = createRelativeUrl(`/alerting/export-new-rule`, {
|
||||||
|
returnTo,
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<LinkButton
|
||||||
|
href={url}
|
||||||
|
icon="download-alt"
|
||||||
|
variant="secondary"
|
||||||
|
tooltip="Export new grafana rule"
|
||||||
|
onClick={() => logInfo(LogMessages.exportNewGrafanaRule)}
|
||||||
|
>
|
||||||
|
<Trans i18nKey="alerting.list-view.section.grafanaManaged.export-new-rule">Export new alert rule</Trans>
|
||||||
|
</LinkButton>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -280,6 +280,12 @@
|
|||||||
"contactPointFilter": {
|
"contactPointFilter": {
|
||||||
"label": "Contact point"
|
"label": "Contact point"
|
||||||
},
|
},
|
||||||
|
"export": {
|
||||||
|
"subtitle": {
|
||||||
|
"formats": "Select the format and download the file or copy the contents to clipboard",
|
||||||
|
"one-format": "Download the file or copy the contents to clipboard"
|
||||||
|
}
|
||||||
|
},
|
||||||
"folderAndGroup": {
|
"folderAndGroup": {
|
||||||
"evaluation": {
|
"evaluation": {
|
||||||
"modal": {
|
"modal": {
|
||||||
@ -301,6 +307,7 @@
|
|||||||
"title": "Data source-managed"
|
"title": "Data source-managed"
|
||||||
},
|
},
|
||||||
"grafanaManaged": {
|
"grafanaManaged": {
|
||||||
|
"export-new-rule": "Export new alert rule",
|
||||||
"export-rules": "Export rules",
|
"export-rules": "Export rules",
|
||||||
"loading": "Loading...",
|
"loading": "Loading...",
|
||||||
"new-recording-rule": "New recording rule",
|
"new-recording-rule": "New recording rule",
|
||||||
|
@ -280,6 +280,12 @@
|
|||||||
"contactPointFilter": {
|
"contactPointFilter": {
|
||||||
"label": "Cőʼnŧäčŧ pőįʼnŧ"
|
"label": "Cőʼnŧäčŧ pőįʼnŧ"
|
||||||
},
|
},
|
||||||
|
"export": {
|
||||||
|
"subtitle": {
|
||||||
|
"formats": "Ŝęľęčŧ ŧĥę ƒőřmäŧ äʼnđ đőŵʼnľőäđ ŧĥę ƒįľę őř čőpy ŧĥę čőʼnŧęʼnŧş ŧő čľįpþőäřđ",
|
||||||
|
"one-format": "Đőŵʼnľőäđ ŧĥę ƒįľę őř čőpy ŧĥę čőʼnŧęʼnŧş ŧő čľįpþőäřđ"
|
||||||
|
}
|
||||||
|
},
|
||||||
"folderAndGroup": {
|
"folderAndGroup": {
|
||||||
"evaluation": {
|
"evaluation": {
|
||||||
"modal": {
|
"modal": {
|
||||||
@ -301,6 +307,7 @@
|
|||||||
"title": "Đäŧä şőūřčę-mäʼnäģęđ"
|
"title": "Đäŧä şőūřčę-mäʼnäģęđ"
|
||||||
},
|
},
|
||||||
"grafanaManaged": {
|
"grafanaManaged": {
|
||||||
|
"export-new-rule": "Ēχpőřŧ ʼnęŵ äľęřŧ řūľę",
|
||||||
"export-rules": "Ēχpőřŧ řūľęş",
|
"export-rules": "Ēχpőřŧ řūľęş",
|
||||||
"loading": "Ŀőäđįʼnģ...",
|
"loading": "Ŀőäđįʼnģ...",
|
||||||
"new-recording-rule": "Ńęŵ řęčőřđįʼnģ řūľę",
|
"new-recording-rule": "Ńęŵ řęčőřđįʼnģ řūľę",
|
||||||
|
Loading…
Reference in New Issue
Block a user