Commit a6daa8f8 authored by Andrew Fontaine's avatar Andrew Fontaine

Merge branch 'add-text-type-variable-support-for-monitoring-dashboard' into 'master'

Add text variable parser for monitoring dashboard

See merge request gitlab-org/gitlab!31523
parents ccd3b3fe 62c4095b
import { isString } from 'lodash';
import { VARIABLE_TYPES } from '../constants'; import { VARIABLE_TYPES } from '../constants';
/** /**
* This file exclusively deals with parsing user-defined variables * This file exclusively deals with parsing user-defined variables
* in dashboard yml file. * in dashboard yml file.
* *
* As of 13.0, simple custom and advanced custom variables are supported. * As of 13.0, simple text, advanced text, simple custom and
* advanced custom variables are supported.
* *
* In the future iterations, text and query variables will be * In the future iterations, text and query variables will be
* supported * supported
...@@ -12,13 +14,30 @@ import { VARIABLE_TYPES } from '../constants'; ...@@ -12,13 +14,30 @@ import { VARIABLE_TYPES } from '../constants';
*/ */
/** /**
* Utility method to determine if a custom variable is * Simple text variable is a string value only.
* simple or not. If its not simple, it is advanced. * This method parses such variables to a standard format.
* *
* @param {Array|Object} customVar Array if simple, object if advanced * @param {String|Object} simpleTextVar
* @returns {Boolean} true if simple, false if advanced * @returns {Object}
*/ */
const isSimpleCustomVariable = customVar => Array.isArray(customVar); const textSimpleVariableParser = simpleTextVar => ({
type: VARIABLE_TYPES.text,
label: null,
value: simpleTextVar,
});
/**
* Advanced text variable is an object.
* This method parses such variables to a standard format.
*
* @param {Object} advTextVar
* @returns {Object}
*/
const textAdvancedVariableParser = advTextVar => ({
type: VARIABLE_TYPES.text,
label: advTextVar.label,
value: advTextVar.options.default_value,
});
/** /**
* Normalize simple and advanced custom variable options to a standard * Normalize simple and advanced custom variable options to a standard
...@@ -26,20 +45,12 @@ const isSimpleCustomVariable = customVar => Array.isArray(customVar); ...@@ -26,20 +45,12 @@ const isSimpleCustomVariable = customVar => Array.isArray(customVar);
* @param {Object} custom variable option * @param {Object} custom variable option
* @returns {Object} normalized custom variable options * @returns {Object} normalized custom variable options
*/ */
const normalizeDropdownOptions = ({ default: defaultOpt = false, text, value }) => ({ const normalizeCustomVariableOptions = ({ default: defaultOpt = false, text, value }) => ({
default: defaultOpt, default: defaultOpt,
text, text,
value, value,
}); });
/**
* Simple custom variables have an array of values.
* This method parses such variables options to a standard format.
*
* @param {String} opt option from simple custom variable
*/
const parseSimpleDropdownOptions = opt => ({ text: opt, value: opt });
/** /**
* Custom advanced variables are rendered as dropdown elements in the dashboard * Custom advanced variables are rendered as dropdown elements in the dashboard
* header. This method parses advanced custom variables. * header. This method parses advanced custom variables.
...@@ -52,10 +63,19 @@ const customAdvancedVariableParser = advVariable => { ...@@ -52,10 +63,19 @@ const customAdvancedVariableParser = advVariable => {
return { return {
type: VARIABLE_TYPES.custom, type: VARIABLE_TYPES.custom,
label: advVariable.label, label: advVariable.label,
options: options.map(normalizeDropdownOptions), options: options.map(normalizeCustomVariableOptions),
}; };
}; };
/**
* Simple custom variables have an array of values.
* This method parses such variables options to a standard format.
*
* @param {String} opt option from simple custom variable
* @returns {Object}
*/
const parseSimpleCustomOptions = opt => ({ text: opt, value: opt });
/** /**
* Custom simple variables are rendered as dropdown elements in the dashboard * Custom simple variables are rendered as dropdown elements in the dashboard
* header. This method parses simple custom variables. * header. This method parses simple custom variables.
...@@ -66,14 +86,23 @@ const customAdvancedVariableParser = advVariable => { ...@@ -66,14 +86,23 @@ const customAdvancedVariableParser = advVariable => {
* @returns {Object} * @returns {Object}
*/ */
const customSimpleVariableParser = simpleVar => { const customSimpleVariableParser = simpleVar => {
const options = (simpleVar || []).map(parseSimpleDropdownOptions); const options = (simpleVar || []).map(parseSimpleCustomOptions);
return { return {
type: VARIABLE_TYPES.custom, type: VARIABLE_TYPES.custom,
label: null, label: null,
options: options.map(normalizeDropdownOptions), options: options.map(normalizeCustomVariableOptions),
}; };
}; };
/**
* Utility method to determine if a custom variable is
* simple or not. If its not simple, it is advanced.
*
* @param {Array|Object} customVar Array if simple, object if advanced
* @returns {Boolean} true if simple, false if advanced
*/
const isSimpleCustomVariable = customVar => Array.isArray(customVar);
/** /**
* This method returns a parser based on the type of the variable. * This method returns a parser based on the type of the variable.
* Currently, the supported variables are simple custom and * Currently, the supported variables are simple custom and
...@@ -88,6 +117,10 @@ const getVariableParser = variable => { ...@@ -88,6 +117,10 @@ const getVariableParser = variable => {
return customSimpleVariableParser; return customSimpleVariableParser;
} else if (variable.type === VARIABLE_TYPES.custom) { } else if (variable.type === VARIABLE_TYPES.custom) {
return customAdvancedVariableParser; return customAdvancedVariableParser;
} else if (variable.type === VARIABLE_TYPES.text) {
return textAdvancedVariableParser;
} else if (isString(variable)) {
return textSimpleVariableParser;
} }
return () => null; return () => null;
}; };
......
...@@ -560,3 +560,214 @@ export const mockNamespacedData = { ...@@ -560,3 +560,214 @@ export const mockNamespacedData = {
export const mockLogsPath = '/mockLogsPath'; export const mockLogsPath = '/mockLogsPath';
export const mockLogsHref = `${mockLogsPath}?duration_seconds=${mockTimeRange.duration.seconds}`; export const mockLogsHref = `${mockLogsPath}?duration_seconds=${mockTimeRange.duration.seconds}`;
const templatingVariableTypes = {
text: {
simple: 'Simple text',
advanced: {
label: 'Variable 4',
type: 'text',
options: {
default_value: 'default',
},
},
},
custom: {
simple: ['value1', 'value2', 'value3'],
advanced: {
normal: {
label: 'Advanced Var',
type: 'custom',
options: {
values: [
{ value: 'value1', text: 'Var 1 Option 1' },
{
value: 'value2',
text: 'Var 1 Option 2',
default: true,
},
],
},
},
withoutOpts: {
type: 'custom',
options: {},
},
withoutLabel: {
type: 'custom',
options: {
values: [
{ value: 'value1', text: 'Var 1 Option 1' },
{
value: 'value2',
text: 'Var 1 Option 2',
default: true,
},
],
},
},
withoutType: {
label: 'Variable 2',
options: {
values: [
{ value: 'value1', text: 'Var 1 Option 1' },
{
value: 'value2',
text: 'Var 1 Option 2',
default: true,
},
],
},
},
},
},
};
const generateMockTemplatingData = data => {
const vars = data
? {
variables: {
...data,
},
}
: {};
return {
dashboard: {
templating: vars,
},
};
};
const responseForSimpleTextVariable = {
simpleText: {
label: 'simpleText',
type: 'text',
value: 'Simple text',
},
};
const responseForAdvTextVariable = {
advText: {
label: 'Variable 4',
type: 'text',
value: 'default',
},
};
const responseForSimpleCustomVariable = {
simpleCustom: {
label: 'simpleCustom',
options: [
{
default: false,
text: 'value1',
value: 'value1',
},
{
default: false,
text: 'value2',
value: 'value2',
},
{
default: false,
text: 'value3',
value: 'value3',
},
],
type: 'custom',
},
};
const responseForAdvancedCustomVariableWithoutOptions = {
advCustomWithoutOpts: {
label: 'advCustomWithoutOpts',
options: [],
type: 'custom',
},
};
const responseForAdvancedCustomVariableWithoutLabel = {
advCustomWithoutLabel: {
label: 'advCustomWithoutLabel',
options: [
{
default: false,
text: 'Var 1 Option 1',
value: 'value1',
},
{
default: true,
text: 'Var 1 Option 2',
value: 'value2',
},
],
type: 'custom',
},
};
const responseForAdvancedCustomVariable = {
...responseForSimpleCustomVariable,
advCustomNormal: {
label: 'Advanced Var',
options: [
{
default: false,
text: 'Var 1 Option 1',
value: 'value1',
},
{
default: true,
text: 'Var 1 Option 2',
value: 'value2',
},
],
type: 'custom',
},
};
const responsesForAllVariableTypes = {
...responseForSimpleTextVariable,
...responseForAdvTextVariable,
...responseForSimpleCustomVariable,
...responseForAdvancedCustomVariable,
};
export const mockTemplatingData = {
emptyTemplatingProp: generateMockTemplatingData(),
emptyVariablesProp: generateMockTemplatingData({}),
simpleText: generateMockTemplatingData({ simpleText: templatingVariableTypes.text.simple }),
advText: generateMockTemplatingData({ advText: templatingVariableTypes.text.advanced }),
simpleCustom: generateMockTemplatingData({ simpleCustom: templatingVariableTypes.custom.simple }),
advCustomWithoutOpts: generateMockTemplatingData({
advCustomWithoutOpts: templatingVariableTypes.custom.advanced.withoutOpts,
}),
advCustomWithoutType: generateMockTemplatingData({
advCustomWithoutType: templatingVariableTypes.custom.advanced.withoutType,
}),
advCustomWithoutLabel: generateMockTemplatingData({
advCustomWithoutLabel: templatingVariableTypes.custom.advanced.withoutLabel,
}),
simpleAndAdv: generateMockTemplatingData({
simpleCustom: templatingVariableTypes.custom.simple,
advCustomNormal: templatingVariableTypes.custom.advanced.normal,
}),
allVariableTypes: generateMockTemplatingData({
simpleText: templatingVariableTypes.text.simple,
advText: templatingVariableTypes.text.advanced,
simpleCustom: templatingVariableTypes.custom.simple,
advCustomNormal: templatingVariableTypes.custom.advanced.normal,
}),
};
export const mockTemplatingDataResponses = {
emptyTemplatingProp: {},
emptyVariablesProp: {},
simpleText: responseForSimpleTextVariable,
advText: responseForAdvTextVariable,
simpleCustom: responseForSimpleCustomVariable,
advCustomWithoutOpts: responseForAdvancedCustomVariableWithoutOptions,
advCustomWithoutType: {},
advCustomWithoutLabel: responseForAdvancedCustomVariableWithoutLabel,
simpleAndAdv: responseForAdvancedCustomVariable,
allVariableTypes: responsesForAllVariableTypes,
};
import { parseTemplatingVariables } from '~/monitoring/stores/variable_mapping'; import { parseTemplatingVariables } from '~/monitoring/stores/variable_mapping';
import { mockTemplatingData, mockTemplatingDataResponses } from '../mock_data';
describe('parseTemplatingVariables', () => { describe('parseTemplatingVariables', () => {
const generateMockTemplatingData = data => {
const vars = data
? {
variables: {
...data,
},
}
: {};
return {
dashboard: {
templating: vars,
},
};
};
const simpleVar = ['value1', 'value2', 'value3'];
const advVar = {
label: 'Advanced Var',
type: 'custom',
options: {
values: [
{ value: 'value1', text: 'Var 1 Option 1' },
{
value: 'value2',
text: 'Var 1 Option 2',
default: true,
},
],
},
};
const advVarWithoutOptions = {
type: 'custom',
options: {},
};
const advVarWithoutLabel = {
type: 'custom',
options: {
values: [
{ value: 'value1', text: 'Var 1 Option 1' },
{
value: 'value2',
text: 'Var 1 Option 2',
default: true,
},
],
},
};
const advVarWithoutType = {
label: 'Variable 2',
options: {
values: [
{ value: 'value1', text: 'Var 1 Option 1' },
{
value: 'value2',
text: 'Var 1 Option 2',
default: true,
},
],
},
};
const responseForSimpleCustomVariable = {
simpleVar: {
label: 'simpleVar',
options: [
{
default: false,
text: 'value1',
value: 'value1',
},
{
default: false,
text: 'value2',
value: 'value2',
},
{
default: false,
text: 'value3',
value: 'value3',
},
],
type: 'custom',
},
};
const responseForAdvancedCustomVariableWithoutOptions = {
advVarWithoutOptions: {
label: 'advVarWithoutOptions',
options: [],
type: 'custom',
},
};
const responseForAdvancedCustomVariableWithoutLabel = {
advVarWithoutLabel: {
label: 'advVarWithoutLabel',
options: [
{
default: false,
text: 'Var 1 Option 1',
value: 'value1',
},
{
default: true,
text: 'Var 1 Option 2',
value: 'value2',
},
],
type: 'custom',
},
};
const responseForAdvancedCustomVariable = {
...responseForSimpleCustomVariable,
advVar: {
label: 'Advanced Var',
options: [
{
default: false,
text: 'Var 1 Option 1',
value: 'value1',
},
{
default: true,
text: 'Var 1 Option 2',
value: 'value2',
},
],
type: 'custom',
},
};
it.each` it.each`
case | input | expected case | input | expected
${'Returns empty object for no dashboard input'} | ${{}} | ${{}} ${'Returns empty object for no dashboard input'} | ${{}} | ${{}}
${'Returns empty object for empty dashboard input'} | ${{ dashboard: {} }} | ${{}} ${'Returns empty object for empty dashboard input'} | ${{ dashboard: {} }} | ${{}}
${'Returns empty object for empty templating prop'} | ${generateMockTemplatingData()} | ${{}} ${'Returns empty object for empty templating prop'} | ${mockTemplatingData.emptyTemplatingProp} | ${{}}
${'Returns empty object for empty variables prop'} | ${generateMockTemplatingData({})} | ${{}} ${'Returns empty object for empty variables prop'} | ${mockTemplatingData.emptyVariablesProp} | ${{}}
${'Returns parsed object for simple variable'} | ${generateMockTemplatingData({ simpleVar })} | ${responseForSimpleCustomVariable} ${'Returns parsed object for simple text variable'} | ${mockTemplatingData.simpleText} | ${mockTemplatingDataResponses.simpleText}
${'Returns parsed object for advanced variable without options'} | ${generateMockTemplatingData({ advVarWithoutOptions })} | ${responseForAdvancedCustomVariableWithoutOptions} ${'Returns parsed object for advanced text variable'} | ${mockTemplatingData.advText} | ${mockTemplatingDataResponses.advText}
${'Returns parsed object for advanced variable without type'} | ${generateMockTemplatingData({ advVarWithoutType })} | ${{}} ${'Returns parsed object for simple custom variable'} | ${mockTemplatingData.simpleCustom} | ${mockTemplatingDataResponses.simpleCustom}
${'Returns parsed object for advanced variable without label'} | ${generateMockTemplatingData({ advVarWithoutLabel })} | ${responseForAdvancedCustomVariableWithoutLabel} ${'Returns parsed object for advanced custom variable without options'} | ${mockTemplatingData.advCustomWithoutOpts} | ${mockTemplatingDataResponses.advCustomWithoutOpts}
${'Returns parsed object for simple and advanced variables'} | ${generateMockTemplatingData({ simpleVar, advVar })} | ${responseForAdvancedCustomVariable} ${'Returns parsed object for advanced custom variable without type'} | ${mockTemplatingData.advCustomWithoutType} | ${{}}
${'Returns parsed object for advanced custom variable without label'} | ${mockTemplatingData.advCustomWithoutLabel} | ${mockTemplatingDataResponses.advCustomWithoutLabel}
${'Returns parsed object for simple and advanced custom variables'} | ${mockTemplatingData.simpleAndAdv} | ${mockTemplatingDataResponses.simpleAndAdv}
${'Returns parsed object for all variable types'} | ${mockTemplatingData.allVariableTypes} | ${mockTemplatingDataResponses.allVariableTypes}
`('$case', ({ input, expected }) => { `('$case', ({ input, expected }) => {
expect(parseTemplatingVariables(input?.dashboard?.templating)).toEqual(expected); expect(parseTemplatingVariables(input?.dashboard?.templating)).toEqual(expected);
}); });
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment