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';
/**
* This file exclusively deals with parsing user-defined variables
* 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
* supported
......@@ -12,13 +14,30 @@ import { VARIABLE_TYPES } from '../constants';
*/
/**
* Utility method to determine if a custom variable is
* simple or not. If its not simple, it is advanced.
* Simple text variable is a string value only.
* This method parses such variables to a standard format.
*
* @param {Array|Object} customVar Array if simple, object if advanced
* @returns {Boolean} true if simple, false if advanced
* @param {String|Object} simpleTextVar
* @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
......@@ -26,20 +45,12 @@ const isSimpleCustomVariable = customVar => Array.isArray(customVar);
* @param {Object} custom variable option
* @returns {Object} normalized custom variable options
*/
const normalizeDropdownOptions = ({ default: defaultOpt = false, text, value }) => ({
const normalizeCustomVariableOptions = ({ default: defaultOpt = false, text, value }) => ({
default: defaultOpt,
text,
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
* header. This method parses advanced custom variables.
......@@ -52,10 +63,19 @@ const customAdvancedVariableParser = advVariable => {
return {
type: VARIABLE_TYPES.custom,
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
* header. This method parses simple custom variables.
......@@ -66,14 +86,23 @@ const customAdvancedVariableParser = advVariable => {
* @returns {Object}
*/
const customSimpleVariableParser = simpleVar => {
const options = (simpleVar || []).map(parseSimpleDropdownOptions);
const options = (simpleVar || []).map(parseSimpleCustomOptions);
return {
type: VARIABLE_TYPES.custom,
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.
* Currently, the supported variables are simple custom and
......@@ -88,6 +117,10 @@ const getVariableParser = variable => {
return customSimpleVariableParser;
} else if (variable.type === VARIABLE_TYPES.custom) {
return customAdvancedVariableParser;
} else if (variable.type === VARIABLE_TYPES.text) {
return textAdvancedVariableParser;
} else if (isString(variable)) {
return textSimpleVariableParser;
}
return () => null;
};
......
......@@ -560,3 +560,214 @@ export const mockNamespacedData = {
export const mockLogsPath = '/mockLogsPath';
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 { mockTemplatingData, mockTemplatingDataResponses } from '../mock_data';
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`
case | input | expected
${'Returns empty object for no dashboard input'} | ${{}} | ${{}}
${'Returns empty object for empty dashboard input'} | ${{ dashboard: {} }} | ${{}}
${'Returns empty object for empty templating prop'} | ${generateMockTemplatingData()} | ${{}}
${'Returns empty object for empty variables prop'} | ${generateMockTemplatingData({})} | ${{}}
${'Returns parsed object for simple variable'} | ${generateMockTemplatingData({ simpleVar })} | ${responseForSimpleCustomVariable}
${'Returns parsed object for advanced variable without options'} | ${generateMockTemplatingData({ advVarWithoutOptions })} | ${responseForAdvancedCustomVariableWithoutOptions}
${'Returns parsed object for advanced variable without type'} | ${generateMockTemplatingData({ advVarWithoutType })} | ${{}}
${'Returns parsed object for advanced variable without label'} | ${generateMockTemplatingData({ advVarWithoutLabel })} | ${responseForAdvancedCustomVariableWithoutLabel}
${'Returns parsed object for simple and advanced variables'} | ${generateMockTemplatingData({ simpleVar, advVar })} | ${responseForAdvancedCustomVariable}
case | input | expected
${'Returns empty object for no dashboard input'} | ${{}} | ${{}}
${'Returns empty object for empty dashboard input'} | ${{ dashboard: {} }} | ${{}}
${'Returns empty object for empty templating prop'} | ${mockTemplatingData.emptyTemplatingProp} | ${{}}
${'Returns empty object for empty variables prop'} | ${mockTemplatingData.emptyVariablesProp} | ${{}}
${'Returns parsed object for simple text variable'} | ${mockTemplatingData.simpleText} | ${mockTemplatingDataResponses.simpleText}
${'Returns parsed object for advanced text variable'} | ${mockTemplatingData.advText} | ${mockTemplatingDataResponses.advText}
${'Returns parsed object for simple custom variable'} | ${mockTemplatingData.simpleCustom} | ${mockTemplatingDataResponses.simpleCustom}
${'Returns parsed object for advanced custom variable without options'} | ${mockTemplatingData.advCustomWithoutOpts} | ${mockTemplatingDataResponses.advCustomWithoutOpts}
${'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 }) => {
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