mirror of https://github.com/grafana/grafana
Graphite: Migrate to React (part 1: move state to redux) (#36484)
* Add UMLs * Add rendered diagrams * Move QueryCtrl to flux * Remove redundant param in the reducer * Use named imports for lodash and fix typing for GraphiteTagOperator * Add missing async/await * Extract providers to a separate file * Clean up async await * Rename controller functions back to main * Simplify creating actions * Re-order controller functions * Separate helpers from actions * Rename vars * Simplify helpers * Move controller methods to state reducers * Remove docs (they are added in design doc) * Move actions.ts to state folder * Add docs * Add old methods stubs for easier review * Check how state dependencies will be mapped * Rename state to store * Rename state to store * Rewrite spec tests for Graphite Query Controller * Update docs * Update docs * Update public/app/plugins/datasource/graphite/state/helpers.ts Co-authored-by: Giordano Ricci <me@giordanoricci.com> * Update public/app/plugins/datasource/graphite/state/helpers.ts Co-authored-by: Giordano Ricci <me@giordanoricci.com> * Update public/app/plugins/datasource/graphite/state/helpers.ts Co-authored-by: Giordano Ricci <me@giordanoricci.com> * Update public/app/plugins/datasource/graphite/state/providers.ts Co-authored-by: Giordano Ricci <me@giordanoricci.com> * Update public/app/plugins/datasource/graphite/state/providers.ts Co-authored-by: Giordano Ricci <me@giordanoricci.com> * Update public/app/plugins/datasource/graphite/state/providers.ts Co-authored-by: Giordano Ricci <me@giordanoricci.com> * Update public/app/plugins/datasource/graphite/state/providers.ts Co-authored-by: Giordano Ricci <me@giordanoricci.com> * Update public/app/plugins/datasource/graphite/state/providers.ts Co-authored-by: Giordano Ricci <me@giordanoricci.com> * Update public/app/plugins/datasource/graphite/state/providers.ts Co-authored-by: Giordano Ricci <me@giordanoricci.com> * Add more type definitions * Load function definitions before parsing the target on initial load * Change targetChanged to updateQuery to avoid mutating state directly It's also needed for extra refresh/runQuery execution as handleTargetChanged doesn't handle changing the raw query * Fix updating query after adding a function * Simplify updating function params * Remove redundant awaits * Use redux Action * Use more specific type for GraphiteTag Co-authored-by: Giordano Ricci <me@giordanoricci.com>pull/36797/head^2
parent
71a9f7ce3c
commit
dbe7d3298d
@ -0,0 +1,45 @@ |
||||
import { GraphiteQueryEditorAngularDependencies, GraphiteSegment, GraphiteTag } from '../types'; |
||||
import { createAction } from '@reduxjs/toolkit'; |
||||
import { FuncInstance } from '../gfunc'; |
||||
|
||||
/** |
||||
* List of possible actions changing the state of QueryEditor |
||||
*/ |
||||
|
||||
/** |
||||
* This is used only during the transition to react. It will be removed after migrating all components. |
||||
*/ |
||||
const init = createAction<GraphiteQueryEditorAngularDependencies>('init'); |
||||
|
||||
// Metrics & Tags
|
||||
const segmentValueChanged = createAction<{ segment: GraphiteSegment; index: number }>('segment-value-changed'); |
||||
|
||||
// Tags
|
||||
const addNewTag = createAction<{ segment: GraphiteSegment }>('add-new-tag'); |
||||
const tagChanged = createAction<{ tag: GraphiteTag; index: number }>('tag-changed'); |
||||
const unpause = createAction('unpause'); |
||||
|
||||
// Functions
|
||||
const addFunction = createAction<{ name: string }>('add-function'); |
||||
const removeFunction = createAction<{ func: FuncInstance }>('remove-function'); |
||||
const moveFunction = createAction<{ func: FuncInstance; offset: number }>('move-function'); |
||||
// TODO: at the moment parameters are modified directly, new value of the param will be passed in the action
|
||||
const updateFunctionParam = createAction<{ func: FuncInstance }>('update-function-param'); |
||||
|
||||
// Text editor
|
||||
const updateQuery = createAction<{ query: string }>('update-query'); |
||||
const toggleEditorMode = createAction('toggle-editor'); |
||||
|
||||
export const actions = { |
||||
init, |
||||
segmentValueChanged, |
||||
tagChanged, |
||||
addNewTag, |
||||
unpause, |
||||
addFunction, |
||||
removeFunction, |
||||
moveFunction, |
||||
updateFunctionParam, |
||||
updateQuery, |
||||
toggleEditorMode, |
||||
}; |
@ -0,0 +1,213 @@ |
||||
import { GraphiteQueryEditorState } from './store'; |
||||
import { each, map } from 'lodash'; |
||||
import { dispatch } from '../../../../store/store'; |
||||
import { notifyApp } from '../../../../core/reducers/appNotification'; |
||||
import { createErrorNotification } from '../../../../core/copy/appNotification'; |
||||
|
||||
/** |
||||
* Helpers used by reducers and providers. They modify state object directly so should operate on a copy of the state. |
||||
*/ |
||||
|
||||
export const GRAPHITE_TAG_OPERATORS = ['=', '!=', '=~', '!=~']; |
||||
|
||||
/** |
||||
* Tag names and metric names are displayed in a single dropdown. This prefix is used to |
||||
* distinguish both in the UI. |
||||
*/ |
||||
export const TAG_PREFIX = 'tag: '; |
||||
|
||||
/** |
||||
* Create new AST based on new query. |
||||
* Build segments from parsed metric name and functions. |
||||
*/ |
||||
export async function parseTarget(state: GraphiteQueryEditorState): Promise<void> { |
||||
state.queryModel.parseTarget(); |
||||
await buildSegments(state); |
||||
} |
||||
|
||||
/** |
||||
* Create segments out of the current metric path + add "select metrics" if it's possible to add more to the path |
||||
*/ |
||||
export async function buildSegments(state: GraphiteQueryEditorState, modifyLastSegment = true): Promise<void> { |
||||
state.segments = map(state.queryModel.segments, (segment) => { |
||||
return state.uiSegmentSrv.newSegment(segment); |
||||
}); |
||||
|
||||
const checkOtherSegmentsIndex = state.queryModel.checkOtherSegmentsIndex || 0; |
||||
|
||||
await checkOtherSegments(state, checkOtherSegmentsIndex, modifyLastSegment); |
||||
|
||||
if (state.queryModel.seriesByTagUsed) { |
||||
fixTagSegments(state); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Add "select metric" segment at the end |
||||
*/ |
||||
export function addSelectMetricSegment(state: GraphiteQueryEditorState): void { |
||||
state.queryModel.addSelectMetricSegment(); |
||||
state.segments.push(state.uiSegmentSrv.newSelectMetric()); |
||||
} |
||||
|
||||
/** |
||||
* Validates the state after adding or changing a segment: |
||||
* - adds "select metric" only when more segments can be added to the metric name |
||||
* - check if subsequent segments are still valid if in-between segment changes and |
||||
* removes invalid segments. |
||||
*/ |
||||
export async function checkOtherSegments( |
||||
state: GraphiteQueryEditorState, |
||||
fromIndex: number, |
||||
modifyLastSegment = true |
||||
): Promise<void> { |
||||
if (state.queryModel.segments.length === 1 && state.queryModel.segments[0].type === 'series-ref') { |
||||
return; |
||||
} |
||||
|
||||
if (fromIndex === 0) { |
||||
addSelectMetricSegment(state); |
||||
return; |
||||
} |
||||
|
||||
const path = state.queryModel.getSegmentPathUpTo(fromIndex + 1); |
||||
if (path === '') { |
||||
return; |
||||
} |
||||
|
||||
try { |
||||
const segments = await state.datasource.metricFindQuery(path); |
||||
if (segments.length === 0) { |
||||
if (path !== '' && modifyLastSegment) { |
||||
state.queryModel.segments = state.queryModel.segments.splice(0, fromIndex); |
||||
state.segments = state.segments.splice(0, fromIndex); |
||||
addSelectMetricSegment(state); |
||||
} |
||||
} else if (segments[0].expandable) { |
||||
if (state.segments.length === fromIndex) { |
||||
addSelectMetricSegment(state); |
||||
} else { |
||||
await checkOtherSegments(state, fromIndex + 1); |
||||
} |
||||
} |
||||
} catch (err) { |
||||
handleMetricsAutoCompleteError(state, err); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Changes segment being in focus. After changing the value, next segment gets focus. |
||||
* |
||||
* Note: It's a bit hidden feature. After selecting one metric, and pressing down arrow the dropdown can be expanded. |
||||
* But there's nothing indicating what's in focus and how to expand the dropdown. |
||||
*/ |
||||
export function setSegmentFocus(state: GraphiteQueryEditorState, segmentIndex: number): void { |
||||
each(state.segments, (segment, index) => { |
||||
segment.focus = segmentIndex === index; |
||||
}); |
||||
} |
||||
|
||||
export function spliceSegments(state: GraphiteQueryEditorState, index: number): void { |
||||
state.segments = state.segments.splice(0, index); |
||||
state.queryModel.segments = state.queryModel.segments.splice(0, index); |
||||
} |
||||
|
||||
export function emptySegments(state: GraphiteQueryEditorState): void { |
||||
state.queryModel.segments = []; |
||||
state.segments = []; |
||||
} |
||||
|
||||
/** |
||||
* When seriesByTag function is added the UI changes it's state and only tags can be added from now. |
||||
*/ |
||||
export async function addSeriesByTagFunc(state: GraphiteQueryEditorState, tag: string): Promise<void> { |
||||
const newFunc = state.datasource.createFuncInstance('seriesByTag', { |
||||
withDefaultParams: false, |
||||
}); |
||||
const tagParam = `${tag}=`; |
||||
newFunc.params = [tagParam]; |
||||
state.queryModel.addFunction(newFunc); |
||||
newFunc.added = true; |
||||
|
||||
emptySegments(state); |
||||
handleTargetChanged(state); |
||||
await parseTarget(state); |
||||
} |
||||
|
||||
export function smartlyHandleNewAliasByNode( |
||||
state: GraphiteQueryEditorState, |
||||
func: { def: { name: string }; params: number[]; added: boolean } |
||||
): void { |
||||
if (func.def.name !== 'aliasByNode') { |
||||
return; |
||||
} |
||||
|
||||
for (let i = 0; i < state.segments.length; i++) { |
||||
if (state.segments[i].value.indexOf('*') >= 0) { |
||||
func.params[0] = i; |
||||
func.added = false; |
||||
handleTargetChanged(state); |
||||
return; |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Add "+" button for adding tags once at least one tag is selected |
||||
*/ |
||||
export function fixTagSegments(state: GraphiteQueryEditorState): void { |
||||
// Adding tag with the same name as just removed works incorrectly if single segment is used (instead of array)
|
||||
state.addTagSegments = [state.uiSegmentSrv.newPlusButton()]; |
||||
} |
||||
|
||||
/** |
||||
* Pauses running the query to allow selecting tag value. This is to prevent getting errors if the query is run |
||||
* for a tag with no selected value. |
||||
*/ |
||||
export function pause(state: GraphiteQueryEditorState): void { |
||||
state.paused = true; |
||||
} |
||||
|
||||
export function removeTagPrefix(value: string): string { |
||||
return value.replace(TAG_PREFIX, ''); |
||||
} |
||||
|
||||
export function handleTargetChanged(state: GraphiteQueryEditorState): void { |
||||
if (state.queryModel.error) { |
||||
return; |
||||
} |
||||
|
||||
const oldTarget = state.queryModel.target.target; |
||||
state.queryModel.updateModelTarget(state.panelCtrl.panel.targets); |
||||
|
||||
if (state.queryModel.target.target !== oldTarget && !state.paused) { |
||||
state.panelCtrl.refresh(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* When metrics autocomplete fails - the error is shown, but only once per page view |
||||
*/ |
||||
export function handleMetricsAutoCompleteError( |
||||
state: GraphiteQueryEditorState, |
||||
error: Error |
||||
): GraphiteQueryEditorState { |
||||
console.error(error); |
||||
if (!state.metricAutoCompleteErrorShown) { |
||||
state.metricAutoCompleteErrorShown = true; |
||||
dispatch(notifyApp(createErrorNotification(`Fetching metrics failed: ${error.message}.`))); |
||||
} |
||||
return state; |
||||
} |
||||
|
||||
/** |
||||
* When tags autocomplete fails - the error is shown, but only once per page view |
||||
*/ |
||||
export function handleTagsAutoCompleteError(state: GraphiteQueryEditorState, error: Error): GraphiteQueryEditorState { |
||||
console.error(error); |
||||
if (!state.tagsAutoCompleteErrorShown) { |
||||
state.tagsAutoCompleteErrorShown = true; |
||||
dispatch(notifyApp(createErrorNotification(`Fetching tags failed: ${error.message}.`))); |
||||
} |
||||
return state; |
||||
} |
@ -0,0 +1,190 @@ |
||||
import { GraphiteQueryEditorState } from './store'; |
||||
import { eachRight, map, remove } from 'lodash'; |
||||
import { |
||||
TAG_PREFIX, |
||||
GRAPHITE_TAG_OPERATORS, |
||||
handleMetricsAutoCompleteError, |
||||
handleTagsAutoCompleteError, |
||||
} from './helpers'; |
||||
import { AngularDropdownOptions, GraphiteSegment, GraphiteTag } from '../types'; |
||||
|
||||
/** |
||||
* Providers are hooks for views to provide temporal data for autocomplete. They don't modify the state. |
||||
*/ |
||||
|
||||
/** |
||||
* Return list of available options for a segment with given index |
||||
* |
||||
* It may be: |
||||
* - mixed list of metrics and tags (only when nothing was selected) |
||||
* - list of metric names (if a metric name was selected for this segment) |
||||
*/ |
||||
export async function getAltSegments( |
||||
state: GraphiteQueryEditorState, |
||||
index: number, |
||||
prefix: string |
||||
): Promise<GraphiteSegment[]> { |
||||
let query = prefix.length > 0 ? '*' + prefix + '*' : '*'; |
||||
if (index > 0) { |
||||
query = state.queryModel.getSegmentPathUpTo(index) + '.' + query; |
||||
} |
||||
const options = { |
||||
range: state.panelCtrl.range, |
||||
requestId: 'get-alt-segments', |
||||
}; |
||||
|
||||
try { |
||||
const segments = await state.datasource.metricFindQuery(query, options); |
||||
const altSegments = map(segments, (segment) => { |
||||
return state.uiSegmentSrv.newSegment({ |
||||
value: segment.text, |
||||
expandable: segment.expandable, |
||||
}); |
||||
}); |
||||
|
||||
if (index > 0 && altSegments.length === 0) { |
||||
return altSegments; |
||||
} |
||||
|
||||
// add query references
|
||||
if (index === 0) { |
||||
eachRight(state.panelCtrl.panel.targets, (target) => { |
||||
if (target.refId === state.queryModel.target.refId) { |
||||
return; |
||||
} |
||||
|
||||
altSegments.unshift( |
||||
state.uiSegmentSrv.newSegment({ |
||||
type: 'series-ref', |
||||
value: '#' + target.refId, |
||||
expandable: false, |
||||
}) |
||||
); |
||||
}); |
||||
} |
||||
|
||||
// add template variables
|
||||
eachRight(state.templateSrv.getVariables(), (variable) => { |
||||
altSegments.unshift( |
||||
state.uiSegmentSrv.newSegment({ |
||||
type: 'template', |
||||
value: '$' + variable.name, |
||||
expandable: true, |
||||
}) |
||||
); |
||||
}); |
||||
|
||||
// add wildcard option
|
||||
altSegments.unshift(state.uiSegmentSrv.newSegment('*')); |
||||
|
||||
if (state.supportsTags && index === 0) { |
||||
removeTaggedEntry(altSegments); |
||||
return await addAltTagSegments(state, prefix, altSegments); |
||||
} else { |
||||
return altSegments; |
||||
} |
||||
} catch (err) { |
||||
handleMetricsAutoCompleteError(state, err); |
||||
} |
||||
|
||||
return []; |
||||
} |
||||
|
||||
export function getTagOperators(): AngularDropdownOptions[] { |
||||
return mapToDropdownOptions(GRAPHITE_TAG_OPERATORS); |
||||
} |
||||
|
||||
/** |
||||
* Returns tags as dropdown options |
||||
*/ |
||||
export async function getTags( |
||||
state: GraphiteQueryEditorState, |
||||
index: number, |
||||
tagPrefix: string |
||||
): Promise<AngularDropdownOptions[]> { |
||||
try { |
||||
const tagExpressions = state.queryModel.renderTagExpressions(index); |
||||
const values = await state.datasource.getTagsAutoComplete(tagExpressions, tagPrefix); |
||||
|
||||
const altTags = map(values, 'text'); |
||||
altTags.splice(0, 0, state.removeTagValue); |
||||
return mapToDropdownOptions(altTags); |
||||
} catch (err) { |
||||
handleTagsAutoCompleteError(state, err); |
||||
} |
||||
|
||||
return []; |
||||
} |
||||
|
||||
/** |
||||
* List of tags when a tag is added. getTags is used for editing. |
||||
* When adding - segment is used. When editing - dropdown is used. |
||||
*/ |
||||
export async function getTagsAsSegments( |
||||
state: GraphiteQueryEditorState, |
||||
tagPrefix: string |
||||
): Promise<GraphiteSegment[]> { |
||||
let tagsAsSegments: GraphiteSegment[] = []; |
||||
try { |
||||
const tagExpressions = state.queryModel.renderTagExpressions(); |
||||
const values = await state.datasource.getTagsAutoComplete(tagExpressions, tagPrefix); |
||||
tagsAsSegments = map(values, (val) => { |
||||
return state.uiSegmentSrv.newSegment({ |
||||
value: val.text, |
||||
type: 'tag', |
||||
expandable: false, |
||||
}); |
||||
}); |
||||
} catch (err) { |
||||
tagsAsSegments = []; |
||||
handleTagsAutoCompleteError(state, err); |
||||
} |
||||
|
||||
return tagsAsSegments; |
||||
} |
||||
|
||||
export async function getTagValues( |
||||
state: GraphiteQueryEditorState, |
||||
tag: GraphiteTag, |
||||
index: number, |
||||
valuePrefix: string |
||||
): Promise<AngularDropdownOptions[]> { |
||||
const tagExpressions = state.queryModel.renderTagExpressions(index); |
||||
const tagKey = tag.key; |
||||
const values = await state.datasource.getTagValuesAutoComplete(tagExpressions, tagKey, valuePrefix, {}); |
||||
const altValues = map(values, 'text'); |
||||
// Add template variables as additional values
|
||||
eachRight(state.templateSrv.getVariables(), (variable) => { |
||||
altValues.push('${' + variable.name + ':regex}'); |
||||
}); |
||||
|
||||
return mapToDropdownOptions(altValues); |
||||
} |
||||
|
||||
/** |
||||
* Add segments with tags prefixed with "tag: " to include them in the same list as metrics |
||||
*/ |
||||
async function addAltTagSegments( |
||||
state: GraphiteQueryEditorState, |
||||
prefix: string, |
||||
altSegments: GraphiteSegment[] |
||||
): Promise<GraphiteSegment[]> { |
||||
let tagSegments = await getTagsAsSegments(state, prefix); |
||||
|
||||
tagSegments = map(tagSegments, (segment) => { |
||||
segment.value = TAG_PREFIX + segment.value; |
||||
return segment; |
||||
}); |
||||
|
||||
return altSegments.concat(...tagSegments); |
||||
} |
||||
|
||||
function removeTaggedEntry(altSegments: GraphiteSegment[]) { |
||||
remove(altSegments, (s) => s.value === '_tagged'); |
||||
} |
||||
|
||||
function mapToDropdownOptions(results: string[]) { |
||||
return map(results, (value) => { |
||||
return { text: value, value: value }; |
||||
}); |
||||
} |
@ -0,0 +1,173 @@ |
||||
import GraphiteQuery from '../graphite_query'; |
||||
import { GraphiteActionDispatcher, GraphiteSegment, GraphiteTagOperator } from '../types'; |
||||
import { GraphiteDatasource } from '../datasource'; |
||||
import { TemplateSrv } from '../../../../features/templating/template_srv'; |
||||
import { actions } from './actions'; |
||||
import { getTemplateSrv } from '@grafana/runtime'; |
||||
import { |
||||
addSeriesByTagFunc, |
||||
buildSegments, |
||||
checkOtherSegments, |
||||
emptySegments, |
||||
fixTagSegments, |
||||
handleTargetChanged, |
||||
parseTarget, |
||||
pause, |
||||
removeTagPrefix, |
||||
setSegmentFocus, |
||||
smartlyHandleNewAliasByNode, |
||||
spliceSegments, |
||||
} from './helpers'; |
||||
import { Action } from 'redux'; |
||||
|
||||
export type GraphiteQueryEditorState = { |
||||
/** |
||||
* Extra segment with plus button when tags are rendered |
||||
*/ |
||||
addTagSegments: GraphiteSegment[]; |
||||
|
||||
supportsTags: boolean; |
||||
paused: boolean; |
||||
removeTagValue: string; |
||||
|
||||
datasource: GraphiteDatasource; |
||||
|
||||
uiSegmentSrv: any; |
||||
templateSrv: TemplateSrv; |
||||
panelCtrl: any; |
||||
|
||||
target: { target: string; textEditor: boolean }; |
||||
|
||||
segments: GraphiteSegment[]; |
||||
queryModel: GraphiteQuery; |
||||
|
||||
error: Error | null; |
||||
|
||||
tagsAutoCompleteErrorShown: boolean; |
||||
metricAutoCompleteErrorShown: boolean; |
||||
}; |
||||
|
||||
const reducer = async (action: Action, state: GraphiteQueryEditorState): Promise<GraphiteQueryEditorState> => { |
||||
state = { ...state }; |
||||
|
||||
if (actions.init.match(action)) { |
||||
const deps = action.payload; |
||||
deps.target.target = deps.target.target || ''; |
||||
|
||||
await deps.datasource.waitForFuncDefsLoaded(); |
||||
|
||||
state = { |
||||
...state, |
||||
...deps, |
||||
queryModel: new GraphiteQuery(deps.datasource, deps.target, getTemplateSrv()), |
||||
supportsTags: deps.datasource.supportsTags, |
||||
paused: false, |
||||
removeTagValue: '-- remove tag --', |
||||
}; |
||||
|
||||
await buildSegments(state, false); |
||||
} |
||||
if (actions.segmentValueChanged.match(action)) { |
||||
const { segment, index: segmentIndex } = action.payload; |
||||
|
||||
state.error = null; |
||||
state.queryModel.updateSegmentValue(segment, segmentIndex); |
||||
|
||||
if (state.queryModel.functions.length > 0 && state.queryModel.functions[0].def.fake) { |
||||
state.queryModel.functions = []; |
||||
} |
||||
|
||||
if (segment.type === 'tag') { |
||||
const tag = removeTagPrefix(segment.value); |
||||
pause(state); |
||||
await addSeriesByTagFunc(state, tag); |
||||
return state; |
||||
} |
||||
|
||||
if (segment.expandable) { |
||||
await checkOtherSegments(state, segmentIndex + 1); |
||||
setSegmentFocus(state, segmentIndex + 1); |
||||
handleTargetChanged(state); |
||||
} else { |
||||
spliceSegments(state, segmentIndex + 1); |
||||
} |
||||
|
||||
setSegmentFocus(state, segmentIndex + 1); |
||||
handleTargetChanged(state); |
||||
} |
||||
if (actions.tagChanged.match(action)) { |
||||
const { tag, index: tagIndex } = action.payload; |
||||
state.queryModel.updateTag(tag, tagIndex); |
||||
handleTargetChanged(state); |
||||
} |
||||
if (actions.addNewTag.match(action)) { |
||||
const segment = action.payload.segment; |
||||
const newTagKey = segment.value; |
||||
const newTag = { key: newTagKey, operator: '=' as GraphiteTagOperator, value: '' }; |
||||
state.queryModel.addTag(newTag); |
||||
handleTargetChanged(state); |
||||
fixTagSegments(state); |
||||
} |
||||
if (actions.unpause.match(action)) { |
||||
state.paused = false; |
||||
state.panelCtrl.refresh(); |
||||
} |
||||
if (actions.addFunction.match(action)) { |
||||
const newFunc = state.datasource.createFuncInstance(action.payload.name, { |
||||
withDefaultParams: true, |
||||
}); |
||||
newFunc.added = true; |
||||
state.queryModel.addFunction(newFunc); |
||||
smartlyHandleNewAliasByNode(state, newFunc); |
||||
|
||||
if (state.segments.length === 1 && state.segments[0].fake) { |
||||
emptySegments(state); |
||||
} |
||||
|
||||
if (!newFunc.params.length && newFunc.added) { |
||||
handleTargetChanged(state); |
||||
} |
||||
|
||||
if (newFunc.def.name === 'seriesByTag') { |
||||
await parseTarget(state); |
||||
} |
||||
} |
||||
if (actions.removeFunction.match(action)) { |
||||
state.queryModel.removeFunction(action.payload.func); |
||||
handleTargetChanged(state); |
||||
} |
||||
if (actions.moveFunction.match(action)) { |
||||
const { func, offset } = action.payload; |
||||
state.queryModel.moveFunction(func, offset); |
||||
handleTargetChanged(state); |
||||
} |
||||
if (actions.updateFunctionParam.match(action)) { |
||||
handleTargetChanged(state); |
||||
} |
||||
if (actions.updateQuery.match(action)) { |
||||
state.target.target = action.payload.query; |
||||
handleTargetChanged(state); |
||||
// handleTargetChanged() builds target from segments/tags/functions only,
|
||||
// it doesn't handle refresh when target is change explicitly
|
||||
state.panelCtrl.refresh(); |
||||
} |
||||
if (actions.toggleEditorMode.match(action)) { |
||||
state.target.textEditor = !state.target.textEditor; |
||||
await parseTarget(state); |
||||
} |
||||
|
||||
return { ...state }; |
||||
}; |
||||
|
||||
export const createStore = ( |
||||
onChange: (state: GraphiteQueryEditorState) => void |
||||
): [GraphiteActionDispatcher, GraphiteQueryEditorState] => { |
||||
let state = {} as GraphiteQueryEditorState; |
||||
|
||||
const dispatch = async (action: Action) => { |
||||
state = await reducer(action, state); |
||||
onChange(state); |
||||
}; |
||||
|
||||
return [dispatch, state]; |
||||
}; |
Loading…
Reference in new issue