store.ts 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. import { AnyAction } from '@reduxjs/toolkit';
  2. import { Action, Dispatch } from 'redux';
  3. import { DataQuery, TimeRange } from '@grafana/data';
  4. import { getTemplateSrv } from '@grafana/runtime';
  5. import { TemplateSrv } from '../../../../features/templating/template_srv';
  6. import { GraphiteDatasource } from '../datasource';
  7. import { FuncDefs } from '../gfunc';
  8. import GraphiteQuery, { GraphiteTarget } from '../graphite_query';
  9. import { GraphiteSegment, GraphiteTagOperator } from '../types';
  10. import { actions } from './actions';
  11. import {
  12. addSeriesByTagFunc,
  13. buildSegments,
  14. checkOtherSegments,
  15. emptySegments,
  16. handleTargetChanged,
  17. parseTarget,
  18. pause,
  19. removeTagPrefix,
  20. smartlyHandleNewAliasByNode,
  21. spliceSegments,
  22. } from './helpers';
  23. export type GraphiteQueryEditorState = {
  24. // external dependencies
  25. datasource: GraphiteDatasource;
  26. target: GraphiteTarget;
  27. refresh: () => void;
  28. queries?: DataQuery[];
  29. templateSrv: TemplateSrv;
  30. range?: TimeRange;
  31. // internal
  32. supportsTags: boolean;
  33. paused: boolean;
  34. removeTagValue: string;
  35. funcDefs: FuncDefs | null;
  36. segments: GraphiteSegment[];
  37. queryModel: GraphiteQuery;
  38. error: Error | null;
  39. tagsAutoCompleteErrorShown: boolean;
  40. metricAutoCompleteErrorShown: boolean;
  41. };
  42. const reducer = async (action: Action, state: GraphiteQueryEditorState): Promise<GraphiteQueryEditorState> => {
  43. state = { ...state };
  44. if (actions.init.match(action)) {
  45. const deps = action.payload;
  46. deps.target.target = deps.target.target || '';
  47. await deps.datasource.waitForFuncDefsLoaded();
  48. state = {
  49. ...state,
  50. ...deps,
  51. queryModel: new GraphiteQuery(deps.datasource, deps.target, getTemplateSrv()),
  52. supportsTags: deps.datasource.supportsTags,
  53. paused: false,
  54. removeTagValue: '-- remove tag --',
  55. funcDefs: deps.datasource.funcDefs,
  56. queries: deps.queries,
  57. };
  58. await buildSegments(state, false);
  59. }
  60. if (actions.timeRangeChanged.match(action)) {
  61. state.range = action.payload;
  62. }
  63. if (actions.queriesChanged.match(action)) {
  64. state.queries = action.payload;
  65. handleTargetChanged(state);
  66. }
  67. if (actions.queryChanged.match(action)) {
  68. state.target.target = action.payload.target || '';
  69. await parseTarget(state);
  70. handleTargetChanged(state);
  71. }
  72. if (actions.segmentValueChanged.match(action)) {
  73. const { segment: segmentOrString, index: segmentIndex } = action.payload;
  74. let segment;
  75. // is segment was changed to a string - create a new segment
  76. if (typeof segmentOrString === 'string') {
  77. segment = {
  78. value: segmentOrString,
  79. expandable: true,
  80. fake: false,
  81. };
  82. } else {
  83. segment = segmentOrString as GraphiteSegment;
  84. }
  85. state.error = null;
  86. state.segments[segmentIndex] = segment;
  87. state.queryModel.updateSegmentValue(segment, segmentIndex);
  88. if (state.queryModel.functions.length > 0 && state.queryModel.functions[0].def.fake) {
  89. state.queryModel.functions = [];
  90. }
  91. if (segment.type === 'tag') {
  92. const tag = removeTagPrefix(segment.value);
  93. pause(state);
  94. await addSeriesByTagFunc(state, tag);
  95. return state;
  96. }
  97. // if newly selected segment can be expanded -> check if the path is correct
  98. if (segment.expandable) {
  99. await checkOtherSegments(state, segmentIndex + 1);
  100. } else {
  101. // if not expandable -> remove all other segments
  102. spliceSegments(state, segmentIndex + 1);
  103. }
  104. handleTargetChanged(state);
  105. }
  106. if (actions.tagChanged.match(action)) {
  107. const { tag, index: tagIndex } = action.payload;
  108. state.queryModel.updateTag(tag, tagIndex);
  109. handleTargetChanged(state);
  110. if (state.queryModel.tags.length === 0) {
  111. await checkOtherSegments(state, 0);
  112. state.paused = false;
  113. }
  114. }
  115. if (actions.addNewTag.match(action)) {
  116. const segment = action.payload.segment;
  117. const newTagKey = segment.value;
  118. const newTag = { key: newTagKey, operator: '=' as GraphiteTagOperator, value: '' };
  119. state.queryModel.addTag(newTag);
  120. handleTargetChanged(state);
  121. }
  122. if (actions.unpause.match(action)) {
  123. state.paused = false;
  124. state.refresh();
  125. }
  126. if (actions.addFunction.match(action)) {
  127. const newFunc = state.datasource.createFuncInstance(action.payload.name, {
  128. withDefaultParams: true,
  129. });
  130. newFunc.added = true;
  131. state.queryModel.addFunction(newFunc);
  132. smartlyHandleNewAliasByNode(state, newFunc);
  133. if (state.segments.length === 1 && state.segments[0].fake) {
  134. emptySegments(state);
  135. }
  136. if (!newFunc.params.length && newFunc.added) {
  137. handleTargetChanged(state);
  138. }
  139. if (newFunc.def.name === 'seriesByTag') {
  140. await parseTarget(state);
  141. }
  142. }
  143. if (actions.removeFunction.match(action)) {
  144. state.queryModel.removeFunction(action.payload.func);
  145. handleTargetChanged(state);
  146. }
  147. if (actions.moveFunction.match(action)) {
  148. const { func, offset } = action.payload;
  149. state.queryModel.moveFunction(func, offset);
  150. handleTargetChanged(state);
  151. }
  152. if (actions.updateFunctionParam.match(action)) {
  153. const { func, index, value } = action.payload;
  154. func.updateParam(value, index);
  155. handleTargetChanged(state);
  156. }
  157. if (actions.updateQuery.match(action)) {
  158. state.target.target = action.payload.query;
  159. handleTargetChanged(state);
  160. }
  161. if (actions.runQuery.match(action)) {
  162. state.refresh();
  163. }
  164. if (actions.toggleEditorMode.match(action)) {
  165. state.target.textEditor = !state.target.textEditor;
  166. await parseTarget(state);
  167. }
  168. return { ...state };
  169. };
  170. export const createStore = (onChange: (state: GraphiteQueryEditorState) => void): Dispatch<AnyAction> => {
  171. let state = {} as GraphiteQueryEditorState;
  172. const dispatch = async (action: AnyAction) => {
  173. state = await reducer(action, state);
  174. onChange(state);
  175. };
  176. return dispatch as Dispatch<AnyAction>;
  177. };