Variables: Interpolate variables used in custom variable definition (#78800)

* Variables: Interpolate variables used in custom variable definition

* Update

* Fixing tests
This commit is contained in:
Torkel Ödegaard
2023-12-19 14:23:57 +01:00
committed by GitHub
parent a15681c93c
commit f7429b79ba
7 changed files with 26 additions and 19 deletions

View File

@ -58,7 +58,7 @@ describe('custom actions', () => {
.whenAsyncActionIsDispatched(updateCustomVariableOptions(toKeyedVariableIdentifier(variable)), true);
tester.thenDispatchedActionsShouldEqual(
toKeyedAction('key', createCustomOptionsFromQuery(toVariablePayload(variable))),
toKeyedAction('key', createCustomOptionsFromQuery(toVariablePayload(variable, variable.query))),
toKeyedAction('key', setCurrentVariableValue(toVariablePayload(variable, { option })))
);
});

View File

@ -1,16 +1,23 @@
import { getTemplateSrv } from '@grafana/runtime';
import { ThunkResult } from 'app/types';
import { validateVariableSelectionState } from '../state/actions';
import { toKeyedAction } from '../state/keyedVariablesReducer';
import { getVariable } from '../state/selectors';
import { KeyedVariableIdentifier } from '../state/types';
import { toVariablePayload } from '../utils';
import { createCustomOptionsFromQuery } from './reducer';
export const updateCustomVariableOptions = (identifier: KeyedVariableIdentifier): ThunkResult<void> => {
return async (dispatch) => {
return async (dispatch, getState) => {
const { rootStateKey } = identifier;
await dispatch(toKeyedAction(rootStateKey, createCustomOptionsFromQuery(toVariablePayload(identifier))));
const variable = getVariable(identifier, getState());
if (variable.type !== 'custom') {
return;
}
const query = getTemplateSrv().replace(variable.query);
await dispatch(toKeyedAction(rootStateKey, createCustomOptionsFromQuery(toVariablePayload(identifier, query))));
await dispatch(validateVariableSelectionState(identifier));
};
};

View File

@ -6,7 +6,7 @@ import { ALL_VARIABLE_TEXT } from '../constants';
import { optionPickerFactory } from '../pickers';
import { setOptionAsCurrent, setOptionFromUrl } from '../state/actions';
import { CustomVariableModel } from '../types';
import { isAllVariable, toKeyedVariableIdentifier } from '../utils';
import { containsVariable, isAllVariable, toKeyedVariableIdentifier } from '../utils';
import { CustomVariableEditor } from './CustomVariableEditor';
import { updateCustomVariableOptions } from './actions';
@ -21,8 +21,8 @@ export const createCustomVariableAdapter = (): VariableAdapter<CustomVariableMod
reducer: customVariableReducer,
picker: optionPickerFactory<CustomVariableModel>(),
editor: CustomVariableEditor,
dependsOn: () => {
return false;
dependsOn: (variable, variableToTest) => {
return containsVariable(variable.query, variableToTest.name);
},
setValue: async (variable, option, emitChanges = false) => {
await dispatch(setOptionAsCurrent(toKeyedVariableIdentifier(variable), option, emitChanges));

View File

@ -18,7 +18,7 @@ describe('customVariableReducer', () => {
const query = 'a,b,c,d : e';
const id = '0';
const { initialState } = getVariableTestContext(adapter, { id, query });
const payload = toVariablePayload({ id: '0', type: 'custom' });
const payload = toVariablePayload({ id: '0', type: 'custom' }, query);
reducerTester<VariablesState>()
.givenReducer(customVariableReducer, cloneDeep(initialState))
@ -58,7 +58,7 @@ describe('customVariableReducer', () => {
const query = 'a,b,c,d:e';
const id = '0';
const { initialState } = getVariableTestContext(adapter, { id, query });
const payload = toVariablePayload({ id: '0', type: 'custom' });
const payload = toVariablePayload({ id: '0', type: 'custom' }, query);
reducerTester<VariablesState>()
.givenReducer(customVariableReducer, cloneDeep(initialState))
@ -98,7 +98,7 @@ describe('customVariableReducer', () => {
const query = 'a, b, c, d : e ';
const id = '0';
const { initialState } = getVariableTestContext(adapter, { id, query });
const payload = toVariablePayload({ id: '0', type: 'constant' });
const payload = toVariablePayload({ id: '0', type: 'constant' }, query);
reducerTester<VariablesState>()
.givenReducer(customVariableReducer, cloneDeep(initialState))
@ -138,7 +138,7 @@ describe('customVariableReducer', () => {
const query = 'a, b, c, d : e';
const id = '0';
const { initialState } = getVariableTestContext(adapter, { id, query });
const payload = toVariablePayload({ id: '0', type: 'constant' });
const payload = toVariablePayload({ id: '0', type: 'constant' }, query);
reducerTester<VariablesState>()
.givenReducer(customVariableReducer, cloneDeep(initialState))
@ -178,7 +178,7 @@ describe('customVariableReducer', () => {
const query = 'a, b,http://www.google.com/, http://www.amazon.com/';
const id = '0';
const { initialState } = getVariableTestContext(adapter, { id, query });
const payload = toVariablePayload({ id: '0', type: 'constant' });
const payload = toVariablePayload({ id: '0', type: 'constant' }, query);
reducerTester<VariablesState>()
.givenReducer(customVariableReducer, cloneDeep(initialState))
@ -218,7 +218,7 @@ describe('customVariableReducer', () => {
const query = 'a, b, google : http://www.google.com/, amazon : http://www.amazon.com/';
const id = '0';
const { initialState } = getVariableTestContext(adapter, { id, query });
const payload = toVariablePayload({ id: '0', type: 'constant' });
const payload = toVariablePayload({ id: '0', type: 'constant' }, query);
reducerTester<VariablesState>()
.givenReducer(customVariableReducer, cloneDeep(initialState))
@ -258,7 +258,7 @@ describe('customVariableReducer', () => {
const query = 'a,b,c,d : e';
const id = '0';
const { initialState } = getVariableTestContext(adapter, { id, query, includeAll: true });
const payload = toVariablePayload({ id: '0', type: 'constant' });
const payload = toVariablePayload({ id: '0', type: 'constant' }, query);
reducerTester<VariablesState>()
.givenReducer(customVariableReducer, cloneDeep(initialState))

View File

@ -20,15 +20,15 @@ export const customVariableSlice = createSlice({
name: 'templating/custom',
initialState: initialVariablesState,
reducers: {
createCustomOptionsFromQuery: (state: VariablesState, action: PayloadAction<VariablePayload>) => {
createCustomOptionsFromQuery: (state: VariablesState, action: PayloadAction<VariablePayload<string>>) => {
const instanceState = getInstanceState(state, action.payload.id);
if (instanceState.type !== 'custom') {
return;
}
const { includeAll, query } = instanceState;
const match = query.match(/(?:\\,|[^,])+/g) ?? [];
const { includeAll } = instanceState;
const queryInterpolated = action.payload.data;
const match = queryInterpolated.match(/(?:\\,|[^,])+/g) ?? [];
const options = match.map((text) => {
text = text.replace(/\\,/g, ',');
const textMatch = /^(.+)\s:\s(.+)$/g.exec(text) ?? [];

View File

@ -231,7 +231,7 @@ describe('shared actions', () => {
toKeyedAction(
key,
createCustomOptionsFromQuery(
toVariablePayload({ ...custom, id: dispatchedActions[4].payload.action.payload.id })
toVariablePayload({ ...custom, id: dispatchedActions[4].payload.action.payload.id }, '')
)
)
);

View File

@ -131,7 +131,7 @@ describe('processVariable', () => {
expect(dispatchedActions[0]).toEqual(toKeyedAction(key, variableStateFetching(toVariablePayload(custom))));
expect(dispatchedActions[1]).toEqual(
toKeyedAction(key, createCustomOptionsFromQuery(toVariablePayload(custom)))
toKeyedAction(key, createCustomOptionsFromQuery(toVariablePayload(custom, 'A,B,C')))
);
expect(dispatchedActions[2].type).toEqual('templating/keyed/shared/setCurrentVariableValue');
expect(dispatchedActions[3]).toEqual(toKeyedAction(key, variableStateCompleted(toVariablePayload(custom))));