AmRoutes.tsx 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. import { css } from '@emotion/css';
  2. import React, { FC, useCallback, useEffect, useMemo, useState } from 'react';
  3. import { useDispatch } from 'react-redux';
  4. import { GrafanaTheme2 } from '@grafana/data';
  5. import { Alert, LoadingPlaceholder, useStyles2, withErrorBoundary } from '@grafana/ui';
  6. import { Receiver } from 'app/plugins/datasource/alertmanager/types';
  7. import { useCleanup } from '../../../core/hooks/useCleanup';
  8. import { AlertManagerPicker } from './components/AlertManagerPicker';
  9. import { AlertingPageWrapper } from './components/AlertingPageWrapper';
  10. import { NoAlertManagerWarning } from './components/NoAlertManagerWarning';
  11. import { AmRootRoute } from './components/amroutes/AmRootRoute';
  12. import { AmSpecificRouting } from './components/amroutes/AmSpecificRouting';
  13. import { MuteTimingsTable } from './components/amroutes/MuteTimingsTable';
  14. import { useAlertManagerSourceName } from './hooks/useAlertManagerSourceName';
  15. import { useAlertManagersByPermission } from './hooks/useAlertManagerSources';
  16. import { useUnifiedAlertingSelector } from './hooks/useUnifiedAlertingSelector';
  17. import { fetchAlertManagerConfigAction, updateAlertManagerConfigAction } from './state/actions';
  18. import { AmRouteReceiver, FormAmRoute } from './types/amroutes';
  19. import { amRouteToFormAmRoute, formAmRouteToAmRoute, stringsToSelectableValues } from './utils/amroutes';
  20. import { isVanillaPrometheusAlertManagerDataSource } from './utils/datasource';
  21. import { initialAsyncRequestState } from './utils/redux';
  22. const AmRoutes: FC = () => {
  23. const dispatch = useDispatch();
  24. const styles = useStyles2(getStyles);
  25. const [isRootRouteEditMode, setIsRootRouteEditMode] = useState(false);
  26. const alertManagers = useAlertManagersByPermission('notification');
  27. const [alertManagerSourceName, setAlertManagerSourceName] = useAlertManagerSourceName(alertManagers);
  28. const readOnly = alertManagerSourceName ? isVanillaPrometheusAlertManagerDataSource(alertManagerSourceName) : true;
  29. const amConfigs = useUnifiedAlertingSelector((state) => state.amConfigs);
  30. const fetchConfig = useCallback(() => {
  31. if (alertManagerSourceName) {
  32. dispatch(fetchAlertManagerConfigAction(alertManagerSourceName));
  33. }
  34. }, [alertManagerSourceName, dispatch]);
  35. useEffect(() => {
  36. fetchConfig();
  37. }, [fetchConfig]);
  38. const {
  39. result,
  40. loading: resultLoading,
  41. error: resultError,
  42. } = (alertManagerSourceName && amConfigs[alertManagerSourceName]) || initialAsyncRequestState;
  43. const config = result?.alertmanager_config;
  44. const [rootRoute, id2ExistingRoute] = useMemo(() => amRouteToFormAmRoute(config?.route), [config?.route]);
  45. const receivers = stringsToSelectableValues(
  46. (config?.receivers ?? []).map((receiver: Receiver) => receiver.name)
  47. ) as AmRouteReceiver[];
  48. const enterRootRouteEditMode = () => {
  49. setIsRootRouteEditMode(true);
  50. };
  51. const exitRootRouteEditMode = () => {
  52. setIsRootRouteEditMode(false);
  53. };
  54. useCleanup((state) => state.unifiedAlerting.saveAMConfig);
  55. const handleSave = (data: Partial<FormAmRoute>) => {
  56. if (!result) {
  57. return;
  58. }
  59. const newData = formAmRouteToAmRoute(
  60. alertManagerSourceName,
  61. {
  62. ...rootRoute,
  63. ...data,
  64. },
  65. id2ExistingRoute
  66. );
  67. if (isRootRouteEditMode) {
  68. exitRootRouteEditMode();
  69. }
  70. dispatch(
  71. updateAlertManagerConfigAction({
  72. newConfig: {
  73. ...result,
  74. alertmanager_config: {
  75. ...result.alertmanager_config,
  76. route: newData,
  77. },
  78. },
  79. oldConfig: result,
  80. alertManagerSourceName: alertManagerSourceName!,
  81. successMessage: 'Saved',
  82. refetch: true,
  83. })
  84. );
  85. };
  86. if (!alertManagerSourceName) {
  87. return (
  88. <AlertingPageWrapper pageId="am-routes">
  89. <NoAlertManagerWarning availableAlertManagers={alertManagers} />
  90. </AlertingPageWrapper>
  91. );
  92. }
  93. return (
  94. <AlertingPageWrapper pageId="am-routes">
  95. <AlertManagerPicker
  96. current={alertManagerSourceName}
  97. onChange={setAlertManagerSourceName}
  98. dataSources={alertManagers}
  99. />
  100. {resultError && !resultLoading && (
  101. <Alert severity="error" title="Error loading Alertmanager config">
  102. {resultError.message || 'Unknown error.'}
  103. </Alert>
  104. )}
  105. {resultLoading && <LoadingPlaceholder text="Loading Alertmanager config..." />}
  106. {result && !resultLoading && !resultError && (
  107. <>
  108. <AmRootRoute
  109. alertManagerSourceName={alertManagerSourceName}
  110. isEditMode={isRootRouteEditMode}
  111. onSave={handleSave}
  112. onEnterEditMode={enterRootRouteEditMode}
  113. onExitEditMode={exitRootRouteEditMode}
  114. receivers={receivers}
  115. routes={rootRoute}
  116. />
  117. <div className={styles.break} />
  118. <AmSpecificRouting
  119. alertManagerSourceName={alertManagerSourceName}
  120. onChange={handleSave}
  121. readOnly={readOnly}
  122. onRootRouteEdit={enterRootRouteEditMode}
  123. receivers={receivers}
  124. routes={rootRoute}
  125. />
  126. <div className={styles.break} />
  127. <MuteTimingsTable alertManagerSourceName={alertManagerSourceName} />
  128. </>
  129. )}
  130. </AlertingPageWrapper>
  131. );
  132. };
  133. export default withErrorBoundary(AmRoutes, { style: 'page' });
  134. const getStyles = (theme: GrafanaTheme2) => ({
  135. break: css`
  136. width: 100%;
  137. height: 0;
  138. margin-bottom: ${theme.spacing(2)};
  139. `,
  140. });