misc.ts 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. import { sortBy } from 'lodash';
  2. import { urlUtil, UrlQueryMap, Labels } from '@grafana/data';
  3. import { config } from '@grafana/runtime';
  4. import { alertInstanceKey } from 'app/features/alerting/unified/utils/rules';
  5. import { SortOrder } from 'app/plugins/panel/alertlist/types';
  6. import { Alert, CombinedRule, FilterState, RulesSource, SilenceFilterState } from 'app/types/unified-alerting';
  7. import {
  8. GrafanaAlertState,
  9. PromAlertingRuleState,
  10. mapStateWithReasonToBaseState,
  11. } from 'app/types/unified-alerting-dto';
  12. import { ALERTMANAGER_NAME_QUERY_KEY } from './constants';
  13. import { getRulesSourceName } from './datasource';
  14. import { getMatcherQueryParams } from './matchers';
  15. import * as ruleId from './rule-id';
  16. export function createViewLink(ruleSource: RulesSource, rule: CombinedRule, returnTo: string): string {
  17. const sourceName = getRulesSourceName(ruleSource);
  18. const identifier = ruleId.fromCombinedRule(sourceName, rule);
  19. const paramId = encodeURIComponent(ruleId.stringifyIdentifier(identifier));
  20. const paramSource = encodeURIComponent(sourceName);
  21. return urlUtil.renderUrl(`${config.appSubUrl}/alerting/${paramSource}/${paramId}/view`, { returnTo });
  22. }
  23. export function createExploreLink(dataSourceName: string, query: string) {
  24. return urlUtil.renderUrl(`${config.appSubUrl}/explore`, {
  25. left: JSON.stringify([
  26. 'now-1h',
  27. 'now',
  28. dataSourceName,
  29. { datasource: dataSourceName, expr: query },
  30. { ui: [true, true, true, 'none'] },
  31. ]),
  32. });
  33. }
  34. export function arrayToRecord(items: Array<{ key: string; value: string }>): Record<string, string> {
  35. return items.reduce<Record<string, string>>((rec, { key, value }) => {
  36. rec[key] = value;
  37. return rec;
  38. }, {});
  39. }
  40. export const getFiltersFromUrlParams = (queryParams: UrlQueryMap): FilterState => {
  41. const queryString = queryParams['queryString'] === undefined ? undefined : String(queryParams['queryString']);
  42. const alertState = queryParams['alertState'] === undefined ? undefined : String(queryParams['alertState']);
  43. const dataSource = queryParams['dataSource'] === undefined ? undefined : String(queryParams['dataSource']);
  44. const ruleType = queryParams['ruleType'] === undefined ? undefined : String(queryParams['ruleType']);
  45. const groupBy = queryParams['groupBy'] === undefined ? undefined : String(queryParams['groupBy']).split(',');
  46. return { queryString, alertState, dataSource, groupBy, ruleType };
  47. };
  48. export const getNotificationPoliciesFilters = (searchParams: URLSearchParams) => {
  49. return {
  50. queryString: searchParams.get('queryString') ?? undefined,
  51. contactPoint: searchParams.get('contactPoint') ?? undefined,
  52. };
  53. };
  54. export const getSilenceFiltersFromUrlParams = (queryParams: UrlQueryMap): SilenceFilterState => {
  55. const queryString = queryParams['queryString'] === undefined ? undefined : String(queryParams['queryString']);
  56. const silenceState = queryParams['silenceState'] === undefined ? undefined : String(queryParams['silenceState']);
  57. return { queryString, silenceState };
  58. };
  59. export function recordToArray(record: Record<string, string>): Array<{ key: string; value: string }> {
  60. return Object.entries(record).map(([key, value]) => ({ key, value }));
  61. }
  62. export function makeAMLink(path: string, alertManagerName?: string, options?: Record<string, string>): string {
  63. const search = new URLSearchParams(options);
  64. if (alertManagerName) {
  65. search.append(ALERTMANAGER_NAME_QUERY_KEY, alertManagerName);
  66. }
  67. return `${path}?${search.toString()}`;
  68. }
  69. export function makeRuleBasedSilenceLink(alertManagerSourceName: string, rule: CombinedRule) {
  70. const labels: Labels = {
  71. alertname: rule.name,
  72. ...rule.labels,
  73. };
  74. return makeLabelBasedSilenceLink(alertManagerSourceName, labels);
  75. }
  76. export function makeLabelBasedSilenceLink(alertManagerSourceName: string, labels: Labels) {
  77. const silenceUrlParams = new URLSearchParams();
  78. silenceUrlParams.append('alertmanager', alertManagerSourceName);
  79. const matcherParams = getMatcherQueryParams(labels);
  80. matcherParams.forEach((value, key) => silenceUrlParams.append(key, value));
  81. return `${config.appSubUrl}/alerting/silence/new?${silenceUrlParams.toString()}`;
  82. }
  83. // keep retrying fn if it's error passes shouldRetry(error) and timeout has not elapsed yet
  84. export function retryWhile<T, E = Error>(
  85. fn: () => Promise<T>,
  86. shouldRetry: (e: E) => boolean,
  87. timeout: number, // milliseconds, how long to keep retrying
  88. pause = 1000 // milliseconds, pause between retries
  89. ): Promise<T> {
  90. const start = new Date().getTime();
  91. const makeAttempt = (): Promise<T> =>
  92. fn().catch((e) => {
  93. if (shouldRetry(e) && new Date().getTime() - start < timeout) {
  94. return new Promise((resolve) => setTimeout(resolve, pause)).then(makeAttempt);
  95. }
  96. throw e;
  97. });
  98. return makeAttempt();
  99. }
  100. const alertStateSortScore = {
  101. [GrafanaAlertState.Alerting]: 1,
  102. [PromAlertingRuleState.Firing]: 1,
  103. [GrafanaAlertState.Error]: 1,
  104. [GrafanaAlertState.Pending]: 2,
  105. [PromAlertingRuleState.Pending]: 2,
  106. [PromAlertingRuleState.Inactive]: 2,
  107. [GrafanaAlertState.NoData]: 3,
  108. [GrafanaAlertState.Normal]: 4,
  109. };
  110. export function sortAlerts(sortOrder: SortOrder, alerts: Alert[]): Alert[] {
  111. // Make sure to handle tie-breaks because API returns alert instances in random order every time
  112. if (sortOrder === SortOrder.Importance) {
  113. return sortBy(alerts, (alert) => [
  114. alertStateSortScore[mapStateWithReasonToBaseState(alert.state)],
  115. alertInstanceKey(alert).toLocaleLowerCase(),
  116. ]);
  117. } else if (sortOrder === SortOrder.TimeAsc) {
  118. return sortBy(alerts, (alert) => [
  119. new Date(alert.activeAt) || new Date(),
  120. alertInstanceKey(alert).toLocaleLowerCase(),
  121. ]);
  122. } else if (sortOrder === SortOrder.TimeDesc) {
  123. return sortBy(alerts, (alert) => [
  124. new Date(alert.activeAt) || new Date(),
  125. alertInstanceKey(alert).toLocaleLowerCase(),
  126. ]).reverse();
  127. }
  128. const result = sortBy(alerts, (alert) => alertInstanceKey(alert).toLocaleLowerCase());
  129. if (sortOrder === SortOrder.AlphaDesc) {
  130. result.reverse();
  131. }
  132. return result;
  133. }