diff --git a/public/app/features/alerting/unified/MoreActionsRuleButtons.tsx b/public/app/features/alerting/unified/MoreActionsRuleButtons.tsx
index e22af5ca6a4..ba1d61121ec 100644
--- a/public/app/features/alerting/unified/MoreActionsRuleButtons.tsx
+++ b/public/app/features/alerting/unified/MoreActionsRuleButtons.tsx
@@ -1,18 +1,20 @@
import React from 'react';
import { useLocation } from 'react-router-dom';
+import { useToggle } from 'react-use';
import { urlUtil } from '@grafana/data';
import { Button, Dropdown, Icon, LinkButton, Menu, MenuItem } from '@grafana/ui';
import { logInfo, LogMessages } from './Analytics';
+import { GrafanaRulesExporter } from './components/export/GrafanaRulesExporter';
import { useRulesAccess } from './utils/accessControlHooks';
-import { createUrl } from './utils/url';
interface Props {}
export function MoreActionsRuleButtons({}: Props) {
const { canCreateGrafanaRules, canCreateCloudRules, canReadProvisioning } = useRulesAccess();
const location = useLocation();
+ const [showExportDrawer, toggleShowExportDrawer] = useToggle(false);
const newMenu = (
);
@@ -54,6 +47,7 @@ export function MoreActionsRuleButtons({}: Props) {
+ {showExportDrawer && }
>
);
}
diff --git a/public/app/features/alerting/unified/RuleList.test.tsx b/public/app/features/alerting/unified/RuleList.test.tsx
index f371c453c89..54a5f82af68 100644
--- a/public/app/features/alerting/unified/RuleList.test.tsx
+++ b/public/app/features/alerting/unified/RuleList.test.tsx
@@ -117,7 +117,9 @@ const ui = {
editCloudGroupIcon: byTestId('edit-group'),
newRuleButton: byRole('link', { name: 'New alert rule' }),
moreButton: byRole('button', { name: 'More' }),
- exportButton: byRole('link', { name: /export/i }),
+ exportButton: byRole('menuitem', {
+ name: /export all grafana\-managed rules/i,
+ }),
editGroupModal: {
dialog: byRole('dialog'),
namespaceInput: byRole('textbox', { name: /^Namespace/ }),
diff --git a/public/app/features/alerting/unified/api/alertRuleApi.ts b/public/app/features/alerting/unified/api/alertRuleApi.ts
index b3f0d7f6a25..d8b54019fe4 100644
--- a/public/app/features/alerting/unified/api/alertRuleApi.ts
+++ b/public/app/features/alerting/unified/api/alertRuleApi.ts
@@ -192,6 +192,13 @@ export const alertRuleApi = alertingApi.injectEndpoints({
responseType: 'text',
}),
}),
+ exportRules: build.query({
+ query: ({ format }) => ({
+ url: `/api/v1/provisioning/alert-rules/export`,
+ params: { format: format },
+ responseType: 'text',
+ }),
+ }),
exportReceiver: build.query({
query: ({ receiverName, decrypt, format }) => ({
url: `/api/v1/provisioning/contact-points/export/`,
diff --git a/public/app/features/alerting/unified/components/export/GrafanaRulesExporter.tsx b/public/app/features/alerting/unified/components/export/GrafanaRulesExporter.tsx
new file mode 100644
index 00000000000..ea8e8dbe000
--- /dev/null
+++ b/public/app/features/alerting/unified/components/export/GrafanaRulesExporter.tsx
@@ -0,0 +1,54 @@
+import React, { useState } from 'react';
+
+import { LoadingPlaceholder } from '@grafana/ui';
+
+import { alertRuleApi } from '../../api/alertRuleApi';
+
+import { FileExportPreview } from './FileExportPreview';
+import { GrafanaExportDrawer } from './GrafanaExportDrawer';
+import { allGrafanaExportProviders, ExportFormats } from './providers';
+
+interface GrafanaRulesExporterProps {
+ onClose: () => void;
+}
+
+export function GrafanaRulesExporter({ onClose }: GrafanaRulesExporterProps) {
+ const [activeTab, setActiveTab] = useState('yaml');
+
+ return (
+
+
+
+ );
+}
+
+interface GrafanaRulesExportPreviewProps {
+ exportFormat: ExportFormats;
+ onClose: () => void;
+}
+
+function GrafanaRulesExportPreview({ exportFormat, onClose }: GrafanaRulesExportPreviewProps) {
+ const { currentData: rulesDefinition = '', isFetching } = alertRuleApi.useExportRulesQuery({
+ format: exportFormat,
+ });
+
+ const downloadFileName = `alert-rules-${new Date().getTime()}`;
+
+ if (isFetching) {
+ return ;
+ }
+
+ return (
+
+ );
+}