RuleEditor.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. import { css } from '@emotion/css';
  2. import React, { FC, useEffect } from 'react';
  3. import { useDispatch } from 'react-redux';
  4. import { useAsync } from 'react-use';
  5. import { GrafanaTheme2 } from '@grafana/data';
  6. import { Alert, LinkButton, LoadingPlaceholder, useStyles2, withErrorBoundary } from '@grafana/ui';
  7. import Page from 'app/core/components/Page/Page';
  8. import { useCleanup } from 'app/core/hooks/useCleanup';
  9. import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
  10. import { RuleIdentifier } from 'app/types/unified-alerting';
  11. import { AlertRuleForm } from './components/rule-editor/AlertRuleForm';
  12. import { useIsRuleEditable } from './hooks/useIsRuleEditable';
  13. import { useUnifiedAlertingSelector } from './hooks/useUnifiedAlertingSelector';
  14. import { fetchAllPromBuildInfoAction, fetchEditableRuleAction } from './state/actions';
  15. import { useRulesAccess } from './utils/accessControlHooks';
  16. import * as ruleId from './utils/rule-id';
  17. interface ExistingRuleEditorProps {
  18. identifier: RuleIdentifier;
  19. }
  20. const ExistingRuleEditor: FC<ExistingRuleEditorProps> = ({ identifier }) => {
  21. useCleanup((state) => state.unifiedAlerting.ruleForm.existingRule);
  22. const { loading, result, error, dispatched } = useUnifiedAlertingSelector((state) => state.ruleForm.existingRule);
  23. const dispatch = useDispatch();
  24. const { isEditable } = useIsRuleEditable(ruleId.ruleIdentifierToRuleSourceName(identifier), result?.rule);
  25. useEffect(() => {
  26. if (!dispatched) {
  27. dispatch(fetchEditableRuleAction(identifier));
  28. }
  29. }, [dispatched, dispatch, identifier]);
  30. if (loading || isEditable === undefined) {
  31. return (
  32. <Page.Contents>
  33. <LoadingPlaceholder text="Loading rule..." />
  34. </Page.Contents>
  35. );
  36. }
  37. if (error) {
  38. return (
  39. <Page.Contents>
  40. <Alert severity="error" title="Failed to load rule">
  41. {error.message}
  42. </Alert>
  43. </Page.Contents>
  44. );
  45. }
  46. if (!result) {
  47. return <AlertWarning title="Rule not found">Sorry! This rule does not exist.</AlertWarning>;
  48. }
  49. if (isEditable === false) {
  50. return <AlertWarning title="Cannot edit rule">Sorry! You do not have permission to edit this rule.</AlertWarning>;
  51. }
  52. return <AlertRuleForm existing={result} />;
  53. };
  54. type RuleEditorProps = GrafanaRouteComponentProps<{ id?: string }>;
  55. const RuleEditor: FC<RuleEditorProps> = ({ match }) => {
  56. const dispatch = useDispatch();
  57. const { id } = match.params;
  58. const identifier = ruleId.tryParse(id, true);
  59. const { loading } = useAsync(async () => {
  60. await dispatch(fetchAllPromBuildInfoAction());
  61. }, [dispatch]);
  62. const { canCreateGrafanaRules, canCreateCloudRules, canEditRules } = useRulesAccess();
  63. if (!identifier && !canCreateGrafanaRules && !canCreateCloudRules) {
  64. return <AlertWarning title="Cannot create rules">Sorry! You are not allowed to create rules.</AlertWarning>;
  65. }
  66. if (identifier && !canEditRules(identifier.ruleSourceName)) {
  67. return <AlertWarning title="Cannot edit rules">Sorry! You are not allowed to edit rules.</AlertWarning>;
  68. }
  69. if (loading) {
  70. return (
  71. <Page.Contents>
  72. <LoadingPlaceholder text="Loading..." />
  73. </Page.Contents>
  74. );
  75. }
  76. if (identifier) {
  77. return <ExistingRuleEditor key={id} identifier={identifier} />;
  78. }
  79. return <AlertRuleForm />;
  80. };
  81. const AlertWarning: FC<{ title: string }> = ({ title, children }) => (
  82. <Alert className={useStyles2(warningStyles).warning} severity="warning" title={title}>
  83. <p>{children}</p>
  84. <LinkButton href="alerting/list">To rule list</LinkButton>
  85. </Alert>
  86. );
  87. const warningStyles = (theme: GrafanaTheme2) => ({
  88. warning: css`
  89. margin: ${theme.spacing(4)};
  90. `,
  91. });
  92. export default withErrorBoundary(RuleEditor, { style: 'page' });