reducer.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. import { createSlice, PayloadAction } from '@reduxjs/toolkit';
  2. import { isNumber, sortBy, toLower, uniqBy } from 'lodash';
  3. import { MetricFindValue, stringToJsRegex } from '@grafana/data';
  4. import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE, NONE_VARIABLE_TEXT, NONE_VARIABLE_VALUE } from '../constants';
  5. import { getInstanceState } from '../state/selectors';
  6. import { initialVariablesState, VariablePayload, VariablesState } from '../state/types';
  7. import { initialVariableModelState, QueryVariableModel, VariableOption, VariableRefresh, VariableSort } from '../types';
  8. interface VariableOptionsUpdate {
  9. templatedRegex: string;
  10. results: MetricFindValue[];
  11. }
  12. export const initialQueryVariableModelState: QueryVariableModel = {
  13. ...initialVariableModelState,
  14. type: 'query',
  15. datasource: null,
  16. query: '',
  17. regex: '',
  18. sort: VariableSort.disabled,
  19. refresh: VariableRefresh.onDashboardLoad,
  20. multi: false,
  21. includeAll: false,
  22. allValue: null,
  23. options: [],
  24. current: {} as VariableOption,
  25. definition: '',
  26. };
  27. export const sortVariableValues = (options: any[], sortOrder: VariableSort) => {
  28. if (sortOrder === VariableSort.disabled) {
  29. return options;
  30. }
  31. const sortType = Math.ceil(sortOrder / 2);
  32. const reverseSort = sortOrder % 2 === 0;
  33. if (sortType === 1) {
  34. options = sortBy(options, 'text');
  35. } else if (sortType === 2) {
  36. options = sortBy(options, (opt) => {
  37. if (!opt.text) {
  38. return -1;
  39. }
  40. const matches = opt.text.match(/.*?(\d+).*/);
  41. if (!matches || matches.length < 2) {
  42. return -1;
  43. } else {
  44. return parseInt(matches[1], 10);
  45. }
  46. });
  47. } else if (sortType === 3) {
  48. options = sortBy(options, (opt) => {
  49. return toLower(opt.text);
  50. });
  51. }
  52. if (reverseSort) {
  53. options = options.reverse();
  54. }
  55. return options;
  56. };
  57. const getAllMatches = (str: string, regex: RegExp): RegExpExecArray[] => {
  58. const results: RegExpExecArray[] = [];
  59. let matches = null;
  60. regex.lastIndex = 0;
  61. do {
  62. matches = regex.exec(str);
  63. if (matches) {
  64. results.push(matches);
  65. }
  66. } while (regex.global && matches && matches[0] !== '' && matches[0] !== undefined);
  67. return results;
  68. };
  69. export const metricNamesToVariableValues = (variableRegEx: string, sort: VariableSort, metricNames: any[]) => {
  70. let regex;
  71. let options: VariableOption[] = [];
  72. if (variableRegEx) {
  73. regex = stringToJsRegex(variableRegEx);
  74. }
  75. for (let i = 0; i < metricNames.length; i++) {
  76. const item = metricNames[i];
  77. let text = item.text === undefined || item.text === null ? item.value : item.text;
  78. let value = item.value === undefined || item.value === null ? item.text : item.value;
  79. if (isNumber(value)) {
  80. value = value.toString();
  81. }
  82. if (isNumber(text)) {
  83. text = text.toString();
  84. }
  85. if (regex) {
  86. const matches = getAllMatches(value, regex);
  87. if (!matches.length) {
  88. continue;
  89. }
  90. const valueGroup = matches.find((m) => m.groups && m.groups.value);
  91. const textGroup = matches.find((m) => m.groups && m.groups.text);
  92. const firstMatch = matches.find((m) => m.length > 1);
  93. const manyMatches = matches.length > 1 && firstMatch;
  94. if (valueGroup || textGroup) {
  95. value = valueGroup?.groups?.value ?? textGroup?.groups?.text;
  96. text = textGroup?.groups?.text ?? valueGroup?.groups?.value;
  97. } else if (manyMatches) {
  98. for (let j = 0; j < matches.length; j++) {
  99. const match = matches[j];
  100. options.push({ text: match[1], value: match[1], selected: false });
  101. }
  102. continue;
  103. } else if (firstMatch) {
  104. text = firstMatch[1];
  105. value = firstMatch[1];
  106. }
  107. }
  108. options.push({ text: text, value: value, selected: false });
  109. }
  110. options = uniqBy(options, 'value');
  111. return sortVariableValues(options, sort);
  112. };
  113. export const queryVariableSlice = createSlice({
  114. name: 'templating/query',
  115. initialState: initialVariablesState,
  116. reducers: {
  117. updateVariableOptions: (state: VariablesState, action: PayloadAction<VariablePayload<VariableOptionsUpdate>>) => {
  118. const { results, templatedRegex } = action.payload.data;
  119. const instanceState = getInstanceState<QueryVariableModel>(state, action.payload.id);
  120. const { includeAll, sort } = instanceState;
  121. const options = metricNamesToVariableValues(templatedRegex, sort, results);
  122. if (includeAll) {
  123. options.unshift({ text: ALL_VARIABLE_TEXT, value: ALL_VARIABLE_VALUE, selected: false });
  124. }
  125. if (!options.length) {
  126. options.push({ text: NONE_VARIABLE_TEXT, value: NONE_VARIABLE_VALUE, isNone: true, selected: false });
  127. }
  128. instanceState.options = options;
  129. },
  130. },
  131. });
  132. export const queryVariableReducer = queryVariableSlice.reducer;
  133. export const { updateVariableOptions } = queryVariableSlice.actions;