reducers.ts 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import { createSlice, PayloadAction } from '@reduxjs/toolkit';
  2. import { dateTime } from '@grafana/data';
  3. import {
  4. AlertRule,
  5. AlertRuleDTO,
  6. AlertRulesState,
  7. NotificationChannelOption,
  8. NotificationChannelState,
  9. NotifierDTO,
  10. } from 'app/types';
  11. import unifiedAlertingReducer from '../unified/state/reducers';
  12. import alertDef from './alertDef';
  13. export const initialState: AlertRulesState = {
  14. items: [],
  15. searchQuery: '',
  16. isLoading: false,
  17. };
  18. export const initialChannelState: NotificationChannelState = {
  19. notificationChannelTypes: [],
  20. notificationChannel: {},
  21. notifiers: [],
  22. };
  23. function convertToAlertRule(dto: AlertRuleDTO, state: string): AlertRule {
  24. const stateModel = alertDef.getStateDisplayModel(state);
  25. const rule: AlertRule = {
  26. ...dto,
  27. stateText: stateModel.text,
  28. stateIcon: stateModel.iconClass,
  29. stateClass: stateModel.stateClass,
  30. stateAge: dateTime(dto.newStateDate).fromNow(true),
  31. };
  32. if (rule.state !== 'paused') {
  33. if (rule.executionError) {
  34. rule.info = 'Execution Error: ' + rule.executionError;
  35. }
  36. if (rule.evalData && rule.evalData.noData) {
  37. rule.info = 'Query returned no data';
  38. }
  39. }
  40. return rule;
  41. }
  42. const alertRulesSlice = createSlice({
  43. name: 'alertRules',
  44. initialState,
  45. reducers: {
  46. loadAlertRules: (state) => {
  47. return { ...state, isLoading: true };
  48. },
  49. loadedAlertRules: (state, action: PayloadAction<AlertRuleDTO[]>): AlertRulesState => {
  50. const alertRules: AlertRuleDTO[] = action.payload;
  51. const alertRulesViewModel: AlertRule[] = alertRules.map((rule) => {
  52. return convertToAlertRule(rule, rule.state);
  53. });
  54. return { ...state, items: alertRulesViewModel, isLoading: false };
  55. },
  56. setSearchQuery: (state, action: PayloadAction<string>): AlertRulesState => {
  57. return { ...state, searchQuery: action.payload };
  58. },
  59. },
  60. });
  61. const notificationChannelSlice = createSlice({
  62. name: 'notificationChannel',
  63. initialState: initialChannelState,
  64. reducers: {
  65. setNotificationChannels: (state, action: PayloadAction<NotifierDTO[]>): NotificationChannelState => {
  66. return {
  67. ...state,
  68. notificationChannelTypes: transformNotifiers(action.payload),
  69. notifiers: action.payload,
  70. };
  71. },
  72. notificationChannelLoaded: (state, action: PayloadAction<any>): NotificationChannelState => {
  73. const notificationChannel = action.payload;
  74. const selectedType: NotifierDTO = state.notifiers.find((t) => t.type === notificationChannel.type)!;
  75. const secureChannelOptions = selectedType.options.filter((o: NotificationChannelOption) => o.secure);
  76. /*
  77. If any secure field is in plain text we need to migrate it to use secure field instead.
  78. */
  79. if (
  80. secureChannelOptions.length > 0 &&
  81. secureChannelOptions.some((o: NotificationChannelOption) => {
  82. return notificationChannel.settings[o.propertyName] !== '';
  83. })
  84. ) {
  85. return migrateSecureFields(state, action.payload, secureChannelOptions);
  86. }
  87. return { ...state, notificationChannel: notificationChannel };
  88. },
  89. resetSecureField: (state, action: PayloadAction<string>): NotificationChannelState => {
  90. return {
  91. ...state,
  92. notificationChannel: {
  93. ...state.notificationChannel,
  94. secureFields: { ...state.notificationChannel.secureFields, [action.payload]: false },
  95. },
  96. };
  97. },
  98. },
  99. });
  100. export const { loadAlertRules, loadedAlertRules, setSearchQuery } = alertRulesSlice.actions;
  101. export const { setNotificationChannels, notificationChannelLoaded, resetSecureField } =
  102. notificationChannelSlice.actions;
  103. export const alertRulesReducer = alertRulesSlice.reducer;
  104. export const notificationChannelReducer = notificationChannelSlice.reducer;
  105. export default {
  106. alertRules: alertRulesReducer,
  107. notificationChannel: notificationChannelReducer,
  108. unifiedAlerting: unifiedAlertingReducer,
  109. };
  110. function migrateSecureFields(
  111. state: NotificationChannelState,
  112. notificationChannel: any,
  113. secureChannelOptions: NotificationChannelOption[]
  114. ) {
  115. const cleanedSettings: { [key: string]: string } = {};
  116. const secureSettings: { [key: string]: string } = {};
  117. secureChannelOptions.forEach((option) => {
  118. secureSettings[option.propertyName] = notificationChannel.settings[option.propertyName];
  119. cleanedSettings[option.propertyName] = '';
  120. });
  121. return {
  122. ...state,
  123. notificationChannel: {
  124. ...notificationChannel,
  125. settings: { ...notificationChannel.settings, ...cleanedSettings },
  126. secureSettings: { ...secureSettings },
  127. },
  128. };
  129. }
  130. function transformNotifiers(notifiers: NotifierDTO[]) {
  131. return notifiers
  132. .map((option: NotifierDTO) => {
  133. return {
  134. value: option.type,
  135. label: option.name,
  136. ...option,
  137. typeName: option.type,
  138. };
  139. })
  140. .sort((o1, o2) => {
  141. if (o1.name > o2.name) {
  142. return 1;
  143. }
  144. return -1;
  145. });
  146. }