mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Adds support for editing group details for Grafana managed rules (#53120)
This commit is contained in:
@@ -60,6 +60,7 @@ export const CloudRules: FC<Props> = ({ namespaces, expandAll }) => {
|
||||
key={`${getRulesSourceUid(namespace.rulesSource)}-${namespace.name}-${group.name}`}
|
||||
namespace={namespace}
|
||||
expandAll={expandAll}
|
||||
viewMode={'grouped'}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -2,15 +2,18 @@ import { css } from '@emotion/css';
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { isValidGoDuration } from '@grafana/data';
|
||||
import { Modal, Button, Form, Field, Input, useStyles2 } from '@grafana/ui';
|
||||
import { useCleanup } from 'app/core/hooks/useCleanup';
|
||||
import { CombinedRuleGroup, CombinedRuleNamespace } from 'app/types/unified-alerting';
|
||||
|
||||
import { useUnifiedAlertingSelector } from '../../hooks/useUnifiedAlertingSelector';
|
||||
import { updateLotexNamespaceAndGroupAction } from '../../state/actions';
|
||||
import { checkEvaluationIntervalGlobalLimit } from '../../utils/config';
|
||||
import { getRulesSourceName } from '../../utils/datasource';
|
||||
import { initialAsyncRequestState } from '../../utils/redux';
|
||||
import { durationValidationPattern } from '../../utils/time';
|
||||
import { EvaluationIntervalLimitExceeded } from '../InvalidIntervalWarning';
|
||||
|
||||
interface Props {
|
||||
namespace: CombinedRuleNamespace;
|
||||
@@ -71,7 +74,7 @@ export function EditCloudGroupModal(props: Props): React.ReactElement {
|
||||
onClickBackdrop={onClose}
|
||||
>
|
||||
<Form defaultValues={defaultValues} onSubmit={onSubmit} key={JSON.stringify(defaultValues)}>
|
||||
{({ register, errors, formState: { isDirty } }) => (
|
||||
{({ register, errors, formState: { isDirty }, watch }) => (
|
||||
<>
|
||||
<Field label="Namespace" invalid={!!errors.namespaceName} error={errors.namespaceName?.message}>
|
||||
<Input
|
||||
@@ -99,9 +102,25 @@ export function EditCloudGroupModal(props: Props): React.ReactElement {
|
||||
placeholder="1m"
|
||||
{...register('groupInterval', {
|
||||
pattern: durationValidationPattern,
|
||||
validate: (input) => {
|
||||
const validDuration = isValidGoDuration(input);
|
||||
if (!validDuration) {
|
||||
return 'Invalid duration. Valid example: 1m (Available units: h, m, s)';
|
||||
}
|
||||
|
||||
const limitExceeded = !checkEvaluationIntervalGlobalLimit(input).exceedsLimit;
|
||||
if (limitExceeded) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
})}
|
||||
/>
|
||||
</Field>
|
||||
{checkEvaluationIntervalGlobalLimit(watch('groupInterval')).exceedsLimit && (
|
||||
<EvaluationIntervalLimitExceeded />
|
||||
)}
|
||||
|
||||
<Modal.ButtonRow>
|
||||
<Button variant="secondary" type="button" disabled={loading} onClick={onClose} fill="outline">
|
||||
@@ -49,7 +49,13 @@ export const GrafanaRules: FC<Props> = ({ namespaces, expandAll }) => {
|
||||
</div>
|
||||
|
||||
{pageItems.map(({ group, namespace }) => (
|
||||
<RulesGroup group={group} key={`${namespace.name}-${group.name}`} namespace={namespace} expandAll={expandAll} />
|
||||
<RulesGroup
|
||||
group={group}
|
||||
key={`${namespace.name}-${group.name}`}
|
||||
namespace={namespace}
|
||||
expandAll={expandAll}
|
||||
viewMode={wantsGroupedView ? 'grouped' : 'list'}
|
||||
/>
|
||||
))}
|
||||
{namespacesFormat?.length === 0 && <p>No rules found.</p>}
|
||||
<Pagination
|
||||
|
||||
@@ -45,7 +45,7 @@ describe('Rules group tests', () => {
|
||||
function renderRulesGroup(namespace: CombinedRuleNamespace, group: CombinedRuleGroup) {
|
||||
return render(
|
||||
<Provider store={store}>
|
||||
<RulesGroup group={group} namespace={namespace} expandAll={false} />
|
||||
<RulesGroup group={group} namespace={namespace} expandAll={false} viewMode={'grouped'} />
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,9 +4,7 @@ import React, { FC, useEffect, useState } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { Badge, ConfirmModal, HorizontalGroup, Icon, Spinner, Tooltip, useStyles2 } from '@grafana/ui';
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
import { CombinedRuleGroup, CombinedRuleNamespace } from 'app/types/unified-alerting';
|
||||
|
||||
import { useFolder } from '../../hooks/useFolder';
|
||||
@@ -14,22 +12,26 @@ import { useHasRuler } from '../../hooks/useHasRuler';
|
||||
import { deleteRulesGroupAction } from '../../state/actions';
|
||||
import { useRulesAccess } from '../../utils/accessControlHooks';
|
||||
import { GRAFANA_RULES_SOURCE_NAME, isCloudRulesSource } from '../../utils/datasource';
|
||||
import { makeFolderLink } from '../../utils/misc';
|
||||
import { isFederatedRuleGroup, isGrafanaRulerRule } from '../../utils/rules';
|
||||
import { CollapseToggle } from '../CollapseToggle';
|
||||
import { RuleLocation } from '../RuleLocation';
|
||||
|
||||
import { ActionIcon } from './ActionIcon';
|
||||
import { EditCloudGroupModal } from './EditCloudGroupModal';
|
||||
import { EditCloudGroupModal } from './EditRuleGroupModal';
|
||||
import { RuleStats } from './RuleStats';
|
||||
import { RulesTable } from './RulesTable';
|
||||
|
||||
type ViewMode = 'grouped' | 'list';
|
||||
|
||||
interface Props {
|
||||
namespace: CombinedRuleNamespace;
|
||||
group: CombinedRuleGroup;
|
||||
expandAll: boolean;
|
||||
viewMode: ViewMode;
|
||||
}
|
||||
|
||||
export const RulesGroup: FC<Props> = React.memo(({ group, namespace, expandAll }) => {
|
||||
export const RulesGroup: FC<Props> = React.memo(({ group, namespace, expandAll, viewMode }) => {
|
||||
const { rulesSource } = namespace;
|
||||
const dispatch = useDispatch();
|
||||
const styles = useStyles2(getStyles);
|
||||
@@ -54,6 +56,15 @@ export const RulesGroup: FC<Props> = React.memo(({ group, namespace, expandAll }
|
||||
hasRuler(rulesSource) && rulerRulesLoaded(rulesSource) && !group.rules.find((rule) => !!rule.rulerRule);
|
||||
const isFederated = isFederatedRuleGroup(group);
|
||||
|
||||
// check if group has provisioned items
|
||||
const isProvisioned = group.rules.some((rule) => {
|
||||
return isGrafanaRulerRule(rule.rulerRule) && rule.rulerRule.grafana_alert.provenance;
|
||||
});
|
||||
|
||||
// check what view mode we are in
|
||||
const isListView = viewMode === 'list';
|
||||
const isGroupView = viewMode === 'grouped';
|
||||
|
||||
const deleteGroup = () => {
|
||||
dispatch(deleteRulesGroupAction(namespace, group));
|
||||
setIsDeletingGroup(false);
|
||||
@@ -71,20 +82,34 @@ export const RulesGroup: FC<Props> = React.memo(({ group, namespace, expandAll }
|
||||
);
|
||||
} else if (rulesSource === GRAFANA_RULES_SOURCE_NAME) {
|
||||
if (folderUID) {
|
||||
const baseUrl = `${config.appSubUrl}/dashboards/f/${folderUID}/${kbn.slugifyForUrl(namespace.name)}`;
|
||||
const baseUrl = makeFolderLink(folderUID);
|
||||
if (folder?.canSave) {
|
||||
actionIcons.push(
|
||||
<ActionIcon
|
||||
aria-label="edit folder"
|
||||
key="edit"
|
||||
icon="pen"
|
||||
tooltip="edit folder"
|
||||
to={baseUrl + '/settings'}
|
||||
target="__blank"
|
||||
/>
|
||||
);
|
||||
if (isGroupView && !isProvisioned) {
|
||||
actionIcons.push(
|
||||
<ActionIcon
|
||||
aria-label="edit rule group"
|
||||
data-testid="edit-group"
|
||||
key="edit"
|
||||
icon="pen"
|
||||
tooltip="edit rule group"
|
||||
onClick={() => setIsEditingGroup(true)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (isListView) {
|
||||
actionIcons.push(
|
||||
<ActionIcon
|
||||
aria-label="go to folder"
|
||||
key="goto"
|
||||
icon="folder-open"
|
||||
tooltip="go to folder"
|
||||
to={baseUrl}
|
||||
target="__blank"
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
if (folder?.canAdmin) {
|
||||
if (folder?.canAdmin && isListView) {
|
||||
actionIcons.push(
|
||||
<ActionIcon
|
||||
aria-label="manage permissions"
|
||||
@@ -124,8 +149,7 @@ export const RulesGroup: FC<Props> = React.memo(({ group, namespace, expandAll }
|
||||
}
|
||||
|
||||
// ungrouped rules are rules that are in the "default" group name
|
||||
const isUngrouped = group.name === 'default';
|
||||
const groupName = isUngrouped ? (
|
||||
const groupName = isListView ? (
|
||||
<RuleLocation namespace={namespace.name} />
|
||||
) : (
|
||||
<RuleLocation namespace={namespace.name} group={group.name} />
|
||||
|
||||
Reference in New Issue
Block a user