diff --git a/public/app/features/alerting/unified/components/MenuItemPauseRule.tsx b/public/app/features/alerting/unified/components/MenuItemPauseRule.tsx
index 6b9ef72b8be..489aa792332 100644
--- a/public/app/features/alerting/unified/components/MenuItemPauseRule.tsx
+++ b/public/app/features/alerting/unified/components/MenuItemPauseRule.tsx
@@ -7,8 +7,8 @@ import {
} from 'app/features/alerting/unified/utils/rules';
import { CombinedRule } from 'app/types/unified-alerting';
+import { usePauseRuleInGroup } from '../hooks/ruleGroup/usePauseAlertRule';
import { isLoading } from '../hooks/useAsync';
-import { usePauseRuleInGroup } from '../hooks/useProduceNewRuleGroup';
import { stringifyErrorLike } from '../utils/misc';
interface Props {
diff --git a/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/AlertRuleForm.tsx b/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/AlertRuleForm.tsx
index ccaf73da0a8..c587a9d87b2 100644
--- a/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/AlertRuleForm.tsx
+++ b/public/app/features/alerting/unified/components/rule-editor/alert-rule-form/AlertRuleForm.tsx
@@ -27,7 +27,7 @@ import {
trackAlertRuleFormCancelled,
trackAlertRuleFormSaved,
} from '../../../Analytics';
-import { useDeleteRuleFromGroup } from '../../../hooks/useProduceNewRuleGroup';
+import { useDeleteRuleFromGroup } from '../../../hooks/ruleGroup/useDeleteRuleFromGroup';
import { useUnifiedAlertingSelector } from '../../../hooks/useUnifiedAlertingSelector';
import { saveRuleFormAction } from '../../../state/actions';
import { RuleFormType, RuleFormValues } from '../../../types/rule-form';
diff --git a/public/app/features/alerting/unified/components/rule-viewer/DeleteModal.tsx b/public/app/features/alerting/unified/components/rule-viewer/DeleteModal.tsx
index 75e37e95b7f..fcd813f71c6 100644
--- a/public/app/features/alerting/unified/components/rule-viewer/DeleteModal.tsx
+++ b/public/app/features/alerting/unified/components/rule-viewer/DeleteModal.tsx
@@ -5,7 +5,7 @@ import { ConfirmModal } from '@grafana/ui';
import { dispatch } from 'app/store/store';
import { CombinedRule } from 'app/types/unified-alerting';
-import { useDeleteRuleFromGroup } from '../../hooks/useProduceNewRuleGroup';
+import { useDeleteRuleFromGroup } from '../../hooks/ruleGroup/useDeleteRuleFromGroup';
import { fetchPromAndRulerRulesAction } from '../../state/actions';
import { getRuleGroupLocationFromCombinedRule } from '../../utils/rules';
diff --git a/public/app/features/alerting/unified/components/rules/EditRuleGroupModal.tsx b/public/app/features/alerting/unified/components/rules/EditRuleGroupModal.tsx
index 59caf5d775d..93df670343b 100644
--- a/public/app/features/alerting/unified/components/rules/EditRuleGroupModal.tsx
+++ b/public/app/features/alerting/unified/components/rules/EditRuleGroupModal.tsx
@@ -10,12 +10,12 @@ import { dispatch } from 'app/store/store';
import { CombinedRuleGroup, CombinedRuleNamespace, RuleGroupIdentifier } from 'app/types/unified-alerting';
import { RulerRuleDTO } from 'app/types/unified-alerting-dto';
-import { anyOfRequestState } from '../../hooks/useAsync';
import {
- useMoveRuleGroup,
- useRenameRuleGroup,
useUpdateRuleGroupConfiguration,
-} from '../../hooks/useProduceNewRuleGroup';
+ useRenameRuleGroup,
+ useMoveRuleGroup,
+} from '../../hooks/ruleGroup/useUpdateRuleGroup';
+import { anyOfRequestState } from '../../hooks/useAsync';
import { fetchRulerRulesAction, rulesInSameGroupHaveInvalidFor } from '../../state/actions';
import { checkEvaluationIntervalGlobalLimit } from '../../utils/config';
import { getRulesSourceName, GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource';
diff --git a/public/app/features/alerting/unified/hooks/ruleGroup/__snapshots__/useDeleteRuleFromGroup.test.tsx.snap b/public/app/features/alerting/unified/hooks/ruleGroup/__snapshots__/useDeleteRuleFromGroup.test.tsx.snap
new file mode 100644
index 00000000000..e354c61a29e
--- /dev/null
+++ b/public/app/features/alerting/unified/hooks/ruleGroup/__snapshots__/useDeleteRuleFromGroup.test.tsx.snap
@@ -0,0 +1,156 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`delete rule should be able to delete a Data source managed rule 1`] = `
+[
+ {
+ "body": "",
+ "headers": [
+ [
+ "accept",
+ "application/json, text/plain, */*",
+ ],
+ ],
+ "method": "GET",
+ "url": "http://localhost/api/ruler/grafana/api/v1/rules/mockCombinedNamespace/mockCombinedRuleGroup?subtype=cortex",
+ },
+ {
+ "body": {
+ "name": "group-1",
+ "rules": [
+ {
+ "alert": "r1",
+ "annotations": {
+ "summary": "test alert",
+ },
+ "expr": "up = 1",
+ "labels": {
+ "foo": "bar",
+ },
+ },
+ ],
+ },
+ "headers": [
+ [
+ "content-type",
+ "application/json",
+ ],
+ [
+ "accept",
+ "application/json, text/plain, */*",
+ ],
+ ],
+ "method": "POST",
+ "url": "http://localhost/api/ruler/grafana/api/v1/rules/mockCombinedNamespace?subtype=cortex",
+ },
+ {
+ "body": "",
+ "headers": [
+ [
+ "accept",
+ "application/json, text/plain, */*",
+ ],
+ ],
+ "method": "GET",
+ "url": "http://localhost/api/ruler/grafana/api/v1/rules/mockCombinedNamespace/mockCombinedRuleGroup?subtype=cortex",
+ },
+]
+`;
+
+exports[`delete rule should be able to delete a Grafana managed rule 1`] = `
+[
+ {
+ "body": "",
+ "headers": [
+ [
+ "accept",
+ "application/json, text/plain, */*",
+ ],
+ ],
+ "method": "GET",
+ "url": "http://localhost/api/ruler/grafana/api/v1/rules/NAMESPACE_UID/mockCombinedRuleGroup?subtype=cortex",
+ },
+ {
+ "body": {
+ "name": "group-1",
+ "rules": [
+ {
+ "annotations": {},
+ "for": "",
+ "grafana_alert": {
+ "condition": "",
+ "data": [],
+ "exec_err_state": "Error",
+ "namespace_uid": "NAMESPACE_UID",
+ "no_data_state": "NoData",
+ "rule_group": "my-group",
+ "title": "my rule",
+ "uid": "r1",
+ },
+ "labels": {},
+ },
+ ],
+ },
+ "headers": [
+ [
+ "content-type",
+ "application/json",
+ ],
+ [
+ "accept",
+ "application/json, text/plain, */*",
+ ],
+ ],
+ "method": "POST",
+ "url": "http://localhost/api/ruler/grafana/api/v1/rules/NAMESPACE_UID?subtype=cortex",
+ },
+ {
+ "body": "",
+ "headers": [
+ [
+ "accept",
+ "application/json, text/plain, */*",
+ ],
+ ],
+ "method": "GET",
+ "url": "http://localhost/api/ruler/grafana/api/v1/rules/NAMESPACE_UID/mockCombinedRuleGroup?subtype=cortex",
+ },
+]
+`;
+
+exports[`delete rule should delete the entire group if no more rules are left 1`] = `
+[
+ {
+ "body": "",
+ "headers": [
+ [
+ "accept",
+ "application/json, text/plain, */*",
+ ],
+ ],
+ "method": "GET",
+ "url": "http://localhost/api/ruler/grafana/api/v1/rules/uuid020c61ef/mockCombinedRuleGroup?subtype=cortex",
+ },
+ {
+ "body": "",
+ "headers": [
+ [
+ "accept",
+ "application/json, text/plain, */*",
+ ],
+ ],
+ "method": "DELETE",
+ "url": "http://localhost/api/ruler/grafana/api/v1/rules/uuid020c61ef/mockCombinedRuleGroup?subtype=cortex",
+ },
+ {
+ "body": "",
+ "headers": [
+ [
+ "accept",
+ "application/json, text/plain, */*",
+ ],
+ ],
+ "method": "GET",
+ "url": "http://localhost/api/ruler/grafana/api/v1/rules/uuid020c61ef/mockCombinedRuleGroup?subtype=cortex",
+ },
+]
+`;
diff --git a/public/app/features/alerting/unified/hooks/ruleGroup/__snapshots__/usePauseAlertRule.test.tsx.snap b/public/app/features/alerting/unified/hooks/ruleGroup/__snapshots__/usePauseAlertRule.test.tsx.snap
new file mode 100644
index 00000000000..89ee35e898a
--- /dev/null
+++ b/public/app/features/alerting/unified/hooks/ruleGroup/__snapshots__/usePauseAlertRule.test.tsx.snap
@@ -0,0 +1,88 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`pause rule should be able to pause a rule 1`] = `
+[
+ {
+ "body": "",
+ "headers": [
+ [
+ "accept",
+ "application/json, text/plain, */*",
+ ],
+ ],
+ "method": "GET",
+ "url": "http://localhost/api/ruler/grafana/api/v1/rules/uuid020c61ef/grafana-group-1?subtype=cortex",
+ },
+ {
+ "body": {
+ "interval": "1m",
+ "name": "grafana-group-1",
+ "rules": [
+ {
+ "annotations": {
+ "summary": "Test alert",
+ },
+ "for": "5m",
+ "grafana_alert": {
+ "condition": "A",
+ "data": [
+ {
+ "datasourceUid": "datasource-uid",
+ "model": {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "datasource-uid",
+ },
+ "expression": "vector(1)",
+ "queryType": "alerting",
+ "refId": "A",
+ },
+ "queryType": "alerting",
+ "refId": "A",
+ "relativeTimeRange": {
+ "from": 1000,
+ "to": 2000,
+ },
+ },
+ ],
+ "exec_err_state": "Error",
+ "is_paused": true,
+ "namespace_uid": "uuid020c61ef",
+ "no_data_state": "NoData",
+ "rule_group": "grafana-group-1",
+ "title": "Grafana-rule",
+ "uid": "4d7125fee983",
+ },
+ "labels": {
+ "region": "nasa",
+ "severity": "critical",
+ },
+ },
+ ],
+ },
+ "headers": [
+ [
+ "content-type",
+ "application/json",
+ ],
+ [
+ "accept",
+ "application/json, text/plain, */*",
+ ],
+ ],
+ "method": "POST",
+ "url": "http://localhost/api/ruler/grafana/api/v1/rules/uuid020c61ef?subtype=cortex",
+ },
+ {
+ "body": "",
+ "headers": [
+ [
+ "accept",
+ "application/json, text/plain, */*",
+ ],
+ ],
+ "method": "GET",
+ "url": "http://localhost/api/ruler/grafana/api/v1/rules/uuid020c61ef/grafana-group-1?subtype=cortex",
+ },
+]
+`;
diff --git a/public/app/features/alerting/unified/hooks/__snapshots__/useProduceNewRuleGroup.test.tsx.snap b/public/app/features/alerting/unified/hooks/ruleGroup/__snapshots__/useUpdateRuleGroup.test.tsx.snap
similarity index 58%
rename from public/app/features/alerting/unified/hooks/__snapshots__/useProduceNewRuleGroup.test.tsx.snap
rename to public/app/features/alerting/unified/hooks/ruleGroup/__snapshots__/useUpdateRuleGroup.test.tsx.snap
index 4cd3f9c52df..62c6e987364 100644
--- a/public/app/features/alerting/unified/hooks/__snapshots__/useProduceNewRuleGroup.test.tsx.snap
+++ b/public/app/features/alerting/unified/hooks/ruleGroup/__snapshots__/useUpdateRuleGroup.test.tsx.snap
@@ -1,247 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`delete rule should be able to delete a Data source managed rule 1`] = `
-[
- {
- "body": "",
- "headers": [
- [
- "accept",
- "application/json, text/plain, */*",
- ],
- ],
- "method": "GET",
- "url": "http://localhost/api/ruler/grafana/api/v1/rules/mockCombinedNamespace/mockCombinedRuleGroup?subtype=cortex",
- },
- {
- "body": {
- "name": "group-1",
- "rules": [
- {
- "alert": "r1",
- "annotations": {
- "summary": "test alert",
- },
- "expr": "up = 1",
- "labels": {
- "foo": "bar",
- },
- },
- ],
- },
- "headers": [
- [
- "content-type",
- "application/json",
- ],
- [
- "accept",
- "application/json, text/plain, */*",
- ],
- ],
- "method": "POST",
- "url": "http://localhost/api/ruler/grafana/api/v1/rules/mockCombinedNamespace?subtype=cortex",
- },
- {
- "body": "",
- "headers": [
- [
- "accept",
- "application/json, text/plain, */*",
- ],
- ],
- "method": "GET",
- "url": "http://localhost/api/ruler/grafana/api/v1/rules/mockCombinedNamespace/mockCombinedRuleGroup?subtype=cortex",
- },
-]
-`;
-
-exports[`delete rule should be able to delete a Grafana managed rule 1`] = `
-[
- {
- "body": "",
- "headers": [
- [
- "accept",
- "application/json, text/plain, */*",
- ],
- ],
- "method": "GET",
- "url": "http://localhost/api/ruler/grafana/api/v1/rules/NAMESPACE_UID/mockCombinedRuleGroup?subtype=cortex",
- },
- {
- "body": {
- "name": "group-1",
- "rules": [
- {
- "annotations": {},
- "for": "",
- "grafana_alert": {
- "condition": "",
- "data": [],
- "exec_err_state": "Error",
- "namespace_uid": "NAMESPACE_UID",
- "no_data_state": "NoData",
- "rule_group": "my-group",
- "title": "my rule",
- "uid": "r1",
- },
- "labels": {},
- },
- ],
- },
- "headers": [
- [
- "content-type",
- "application/json",
- ],
- [
- "accept",
- "application/json, text/plain, */*",
- ],
- ],
- "method": "POST",
- "url": "http://localhost/api/ruler/grafana/api/v1/rules/NAMESPACE_UID?subtype=cortex",
- },
- {
- "body": "",
- "headers": [
- [
- "accept",
- "application/json, text/plain, */*",
- ],
- ],
- "method": "GET",
- "url": "http://localhost/api/ruler/grafana/api/v1/rules/NAMESPACE_UID/mockCombinedRuleGroup?subtype=cortex",
- },
-]
-`;
-
-exports[`delete rule should delete the entire group if no more rules are left 1`] = `
-[
- {
- "body": "",
- "headers": [
- [
- "accept",
- "application/json, text/plain, */*",
- ],
- ],
- "method": "GET",
- "url": "http://localhost/api/ruler/grafana/api/v1/rules/uuid020c61ef/mockCombinedRuleGroup?subtype=cortex",
- },
- {
- "body": "",
- "headers": [
- [
- "accept",
- "application/json, text/plain, */*",
- ],
- ],
- "method": "DELETE",
- "url": "http://localhost/api/ruler/grafana/api/v1/rules/uuid020c61ef/mockCombinedRuleGroup?subtype=cortex",
- },
- {
- "body": "",
- "headers": [
- [
- "accept",
- "application/json, text/plain, */*",
- ],
- ],
- "method": "GET",
- "url": "http://localhost/api/ruler/grafana/api/v1/rules/uuid020c61ef/mockCombinedRuleGroup?subtype=cortex",
- },
-]
-`;
-
-exports[`pause rule should be able to pause a rule 1`] = `
-[
- {
- "body": "",
- "headers": [
- [
- "accept",
- "application/json, text/plain, */*",
- ],
- ],
- "method": "GET",
- "url": "http://localhost/api/ruler/grafana/api/v1/rules/uuid020c61ef/grafana-group-1?subtype=cortex",
- },
- {
- "body": {
- "interval": "1m",
- "name": "grafana-group-1",
- "rules": [
- {
- "annotations": {
- "summary": "Test alert",
- },
- "for": "5m",
- "grafana_alert": {
- "condition": "A",
- "data": [
- {
- "datasourceUid": "datasource-uid",
- "model": {
- "datasource": {
- "type": "prometheus",
- "uid": "datasource-uid",
- },
- "expression": "vector(1)",
- "queryType": "alerting",
- "refId": "A",
- },
- "queryType": "alerting",
- "refId": "A",
- "relativeTimeRange": {
- "from": 1000,
- "to": 2000,
- },
- },
- ],
- "exec_err_state": "Error",
- "is_paused": true,
- "namespace_uid": "uuid020c61ef",
- "no_data_state": "NoData",
- "rule_group": "grafana-group-1",
- "title": "Grafana-rule",
- "uid": "4d7125fee983",
- },
- "labels": {
- "region": "nasa",
- "severity": "critical",
- },
- },
- ],
- },
- "headers": [
- [
- "content-type",
- "application/json",
- ],
- [
- "accept",
- "application/json, text/plain, */*",
- ],
- ],
- "method": "POST",
- "url": "http://localhost/api/ruler/grafana/api/v1/rules/uuid020c61ef?subtype=cortex",
- },
- {
- "body": "",
- "headers": [
- [
- "accept",
- "application/json, text/plain, */*",
- ],
- ],
- "method": "GET",
- "url": "http://localhost/api/ruler/grafana/api/v1/rules/uuid020c61ef/grafana-group-1?subtype=cortex",
- },
-]
-`;
-
exports[`useUpdateRuleGroupConfiguration should be able to move a Data Source managed rule group 1`] = `
[
{
diff --git a/public/app/features/alerting/unified/hooks/ruleGroup/useDeleteRuleFromGroup.test.tsx b/public/app/features/alerting/unified/hooks/ruleGroup/useDeleteRuleFromGroup.test.tsx
new file mode 100644
index 00000000000..7ede0ce5ce1
--- /dev/null
+++ b/public/app/features/alerting/unified/hooks/ruleGroup/useDeleteRuleFromGroup.test.tsx
@@ -0,0 +1,152 @@
+import userEvent from '@testing-library/user-event';
+import { HttpResponse } from 'msw';
+import { render } from 'test/test-utils';
+import { byRole, byText } from 'testing-library-selector';
+
+import { setBackendSrv } from '@grafana/runtime';
+import { backendSrv } from 'app/core/services/backend_srv';
+import { AccessControlAction } from 'app/types';
+import { CombinedRule } from 'app/types/unified-alerting';
+
+import server, { setupMswServer } from '../../mockApi';
+import {
+ mockCombinedRule,
+ mockGrafanaRulerRule,
+ mockRulerRuleGroup,
+ mockRulerAlertingRule,
+ mockRulerRecordingRule,
+ grantUserPermissions,
+} from '../../mocks';
+import { grafanaRulerRule } from '../../mocks/grafanaRulerApi';
+import { setUpdateRulerRuleNamespaceHandler, setRulerRuleGroupHandler } from '../../mocks/server/configure';
+import { captureRequests, serializeRequests } from '../../mocks/server/events';
+import { rulerRuleGroupHandler, updateRulerRuleNamespaceHandler } from '../../mocks/server/handlers/mimirRuler';
+import { getRuleGroupLocationFromCombinedRule } from '../../utils/rules';
+import { SerializeState } from '../useAsync';
+
+import { useDeleteRuleFromGroup } from './useDeleteRuleFromGroup';
+
+setupMswServer();
+
+beforeAll(() => {
+ setBackendSrv(backendSrv);
+ grantUserPermissions([AccessControlAction.AlertingRuleExternalRead, AccessControlAction.AlertingRuleRead]);
+});
+
+describe('delete rule', () => {
+ it('should be able to delete a Grafana managed rule', async () => {
+ const rules = [
+ mockCombinedRule({
+ name: 'r1',
+ rulerRule: mockGrafanaRulerRule({ uid: 'r1' }),
+ }),
+ mockCombinedRule({
+ name: 'r2',
+ rulerRule: mockGrafanaRulerRule({ uid: 'r2' }),
+ }),
+ ];
+ const group = mockRulerRuleGroup({
+ name: 'group-1',
+ rules: [rules[0].rulerRule!, rules[1].rulerRule!],
+ });
+
+ const getGroup = rulerRuleGroupHandler({
+ delay: 0,
+ response: HttpResponse.json(group),
+ });
+
+ const updateNamespace = updateRulerRuleNamespaceHandler({
+ response: new HttpResponse(undefined, { status: 200 }),
+ });
+
+ server.use(getGroup, updateNamespace);
+
+ const capture = captureRequests();
+
+ render();
+
+ await userEvent.click(byRole('button').get());
+
+ expect(await byText(/success/i).find()).toBeInTheDocument();
+
+ const requests = await capture;
+ const serializedRequests = await serializeRequests(requests);
+ expect(serializedRequests).toMatchSnapshot();
+ });
+
+ it('should be able to delete a Data source managed rule', async () => {
+ setUpdateRulerRuleNamespaceHandler({
+ response: new HttpResponse(undefined, { status: 200 }),
+ });
+
+ const rules = [
+ mockCombinedRule({
+ name: 'r1',
+ rulerRule: mockRulerAlertingRule({ alert: 'r1', labels: { foo: 'bar' } }),
+ }),
+ mockCombinedRule({
+ name: 'r2',
+ rulerRule: mockRulerRecordingRule({ record: 'r2', labels: { bar: 'baz' } }),
+ }),
+ ];
+
+ const group = mockRulerRuleGroup({
+ name: 'group-1',
+ rules: [rules[0].rulerRule!, rules[1].rulerRule!],
+ });
+
+ setRulerRuleGroupHandler({
+ delay: 0,
+ response: HttpResponse.json(group),
+ });
+
+ const capture = captureRequests();
+
+ render();
+
+ await userEvent.click(byRole('button').get());
+
+ expect(await byText(/success/i).find()).toBeInTheDocument();
+
+ const requests = await capture;
+ const serializedRequests = await serializeRequests(requests);
+ expect(serializedRequests).toMatchSnapshot();
+ });
+
+ it('should delete the entire group if no more rules are left', async () => {
+ const capture = captureRequests();
+
+ const combined = mockCombinedRule({
+ rulerRule: grafanaRulerRule,
+ });
+
+ render();
+ await userEvent.click(byRole('button').get());
+
+ expect(await byText(/success/i).find()).toBeInTheDocument();
+
+ const requests = await capture;
+ const serializedRequests = await serializeRequests(requests);
+ expect(serializedRequests).toMatchSnapshot();
+ });
+});
+
+type DeleteTestComponentProps = {
+ rule: CombinedRule;
+};
+const DeleteTestComponent = ({ rule }: DeleteTestComponentProps) => {
+ const [requestState, deleteRuleFromGroup] = useDeleteRuleFromGroup();
+
+ // always handle your errors!
+ const ruleGroupID = getRuleGroupLocationFromCombinedRule(rule);
+ const onClick = () => {
+ deleteRuleFromGroup.execute(ruleGroupID, rule.rulerRule!);
+ };
+
+ return (
+ <>
+