From 2887f3f68b6318e804357c94a801fcaa7384e8d2 Mon Sep 17 00:00:00 2001 From: Marcus Andersson Date: Mon, 9 Nov 2020 10:37:59 +0100 Subject: [PATCH] Variables: make sure that we support both old and new syntax for custom variables. (#28896) --- .../features/variables/custom/reducer.test.ts | 174 +++++++++++++++++- .../app/features/variables/custom/reducer.ts | 7 +- 2 files changed, 171 insertions(+), 10 deletions(-) diff --git a/public/app/features/variables/custom/reducer.test.ts b/public/app/features/variables/custom/reducer.test.ts index b69ec68cb06..0f256a57716 100644 --- a/public/app/features/variables/custom/reducer.test.ts +++ b/public/app/features/variables/custom/reducer.test.ts @@ -10,9 +10,9 @@ import { CustomVariableModel } from '../types'; describe('customVariableReducer', () => { const adapter = createCustomVariableAdapter(); - describe('when createCustomOptionsFromQuery is dispatched', () => { - it('then state should be correct', () => { - const query = 'a,b,c,d:e'; + describe('when createCustomOptionsFromQuery is dispatched with key/value syntax', () => { + it('should then mutate state correctly', () => { + const query = 'a,b,c,d : e'; const id = '0'; const { initialState } = getVariableTestContext(adapter, { id, query }); const payload = toVariablePayload({ id: '0', type: 'custom' }); @@ -50,8 +50,88 @@ describe('customVariableReducer', () => { }); }); - describe('when createCustomOptionsFromQuery is dispatched and query contains spaces', () => { - it('then state should be correct', () => { + describe('when createCustomOptionsFromQuery is dispatched without key/value syntax', () => { + it('should then mutate state correctly', () => { + const query = 'a,b,c,d:e'; + const id = '0'; + const { initialState } = getVariableTestContext(adapter, { id, query }); + const payload = toVariablePayload({ id: '0', type: 'custom' }); + + reducerTester() + .givenReducer(customVariableReducer, cloneDeep(initialState)) + .whenActionIsDispatched(createCustomOptionsFromQuery(payload)) + .thenStateShouldEqual({ + [id]: { + ...initialState[id], + options: [ + { + text: 'a', + value: 'a', + selected: false, + }, + { + text: 'b', + value: 'b', + selected: false, + }, + { + text: 'c', + value: 'c', + selected: false, + }, + { + text: 'd:e', + value: 'd:e', + selected: false, + }, + ], + } as CustomVariableModel, + }); + }); + }); + + describe('when createCustomOptionsFromQuery is dispatched and query with key/value syntax contains spaces', () => { + it('should then mutate state correctly', () => { + const query = 'a, b, c, d : e '; + const id = '0'; + const { initialState } = getVariableTestContext(adapter, { id, query }); + const payload = toVariablePayload({ id: '0', type: 'constant' }); + + reducerTester() + .givenReducer(customVariableReducer, cloneDeep(initialState)) + .whenActionIsDispatched(createCustomOptionsFromQuery(payload)) + .thenStateShouldEqual({ + [id]: { + ...initialState[id], + options: [ + { + text: 'a', + value: 'a', + selected: false, + }, + { + text: 'b', + value: 'b', + selected: false, + }, + { + text: 'c', + value: 'c', + selected: false, + }, + { + text: 'd', + value: 'e', + selected: false, + }, + ], + } as CustomVariableModel, + }); + }); + }); + + describe('when createCustomOptionsFromQuery is dispatched and query without key/value syntax contains spaces', () => { + it('should then mutate state correctly', () => { const query = 'a, b, c, d : e'; const id = '0'; const { initialState } = getVariableTestContext(adapter, { id, query }); @@ -90,9 +170,89 @@ describe('customVariableReducer', () => { }); }); + describe('when createCustomOptionsFromQuery is dispatched and query without key/value syntax contains urls', () => { + it('should then mutate state correctly', () => { + 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' }); + + reducerTester() + .givenReducer(customVariableReducer, cloneDeep(initialState)) + .whenActionIsDispatched(createCustomOptionsFromQuery(payload)) + .thenStateShouldEqual({ + [id]: { + ...initialState[id], + options: [ + { + text: 'a', + value: 'a', + selected: false, + }, + { + text: 'b', + value: 'b', + selected: false, + }, + { + text: 'http://www.google.com/', + value: 'http://www.google.com/', + selected: false, + }, + { + text: 'http://www.amazon.com/', + value: 'http://www.amazon.com/', + selected: false, + }, + ], + } as CustomVariableModel, + }); + }); + }); + + describe('when createCustomOptionsFromQuery is dispatched and query with key/value syntax contains urls', () => { + it('should then mutate state correctly', () => { + 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' }); + + reducerTester() + .givenReducer(customVariableReducer, cloneDeep(initialState)) + .whenActionIsDispatched(createCustomOptionsFromQuery(payload)) + .thenStateShouldEqual({ + [id]: { + ...initialState[id], + options: [ + { + text: 'a', + value: 'a', + selected: false, + }, + { + text: 'b', + value: 'b', + selected: false, + }, + { + text: 'google', + value: 'http://www.google.com/', + selected: false, + }, + { + text: 'amazon', + value: 'http://www.amazon.com/', + selected: false, + }, + ], + } as CustomVariableModel, + }); + }); + }); + describe('when createCustomOptionsFromQuery is dispatched and includeAll is true', () => { - it('then state should be correct', () => { - const query = 'a,b,c,d:e'; + it('should then mutate state correctly', () => { + 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' }); diff --git a/public/app/features/variables/custom/reducer.ts b/public/app/features/variables/custom/reducer.ts index 8d4d396ded7..bbb12b7e2a0 100644 --- a/public/app/features/variables/custom/reducer.ts +++ b/public/app/features/variables/custom/reducer.ts @@ -22,12 +22,13 @@ export const customVariableSlice = createSlice({ createCustomOptionsFromQuery: (state: VariablesState, action: PayloadAction) => { const instanceState = getInstanceState(state, action.payload.id); const { includeAll, query } = instanceState; + const match = query.match(/(?:\\,|[^,])+/g) ?? []; const options = match.map(text => { text = text.replace(/\\,/g, ','); - const textMatch = text.match(/(?:\\:|[^:])+/g) ?? []; - if (textMatch.length > 1) { - const [key, value] = textMatch; + const textMatch = /^(.+)\s:\s(.+)$/g.exec(text) ?? []; + if (textMatch.length === 3) { + const [, key, value] = textMatch; return { text: key.trim(), value: value.trim(), selected: false }; } else { return { text: text.trim(), value: text.trim(), selected: false };