Commit c0159a2b authored by Kushal Pandya's avatar Kushal Pandya

Merge branch 'add-dropdown-parsers-for-dashboard-yml-variables' into 'master'

Add parsers for dashboard yml custom variables

See merge request gitlab-org/gitlab!31423
parents cd1f348f 09d6a1bb
......@@ -198,3 +198,21 @@ export const OPERATORS = {
equalTo: '==',
lessThan: '<',
};
/**
* Dashboard yml files support custom user-defined variables that
* are rendered as input elements in the monitoring dashboard.
* These values can be edited by the user and are passed on to the
* the backend and eventually to Prometheus API proxy.
*
* As of 13.0, the supported types are:
* simple custom -> dropdown elements
* advanced custom -> dropdown elements
* text -> text input elements
*
* Custom variables have a simple and a advanced variant.
*/
export const VARIABLE_TYPES = {
custom: 'custom',
text: 'text',
};
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.
*
* In the future iterations, text and query variables will be
* supported
*
*/
/**
* 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);
/**
* Normalize simple and advanced custom variable options to a standard
* format
* @param {Object} custom variable option
* @returns {Object} normalized custom variable options
*/
const normalizeDropdownOptions = ({ 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.
*
* @param {Object} advVariable advance custom variable
* @returns {Object}
*/
const customAdvancedVariableParser = advVariable => {
const options = advVariable?.options?.values ?? [];
return {
type: VARIABLE_TYPES.custom,
label: advVariable.label,
options: options.map(normalizeDropdownOptions),
};
};
/**
* Custom simple variables are rendered as dropdown elements in the dashboard
* header. This method parses simple custom variables.
*
* Simple custom variables do not have labels so its set to null here.
*
* @param {Array} customVariable array of options
* @returns {Object}
*/
const customSimpleVariableParser = simpleVar => {
const options = (simpleVar || []).map(parseSimpleDropdownOptions);
return {
type: VARIABLE_TYPES.custom,
label: null,
options: options.map(normalizeDropdownOptions),
};
};
/**
* This method returns a parser based on the type of the variable.
* Currently, the supported variables are simple custom and
* advanced custom only. In the future, this method will support
* text and query variables.
*
* @param {Array|Object} variable
* @return {Function} parser method
*/
const getVariableParser = variable => {
if (isSimpleCustomVariable(variable)) {
return customSimpleVariableParser;
} else if (variable.type === VARIABLE_TYPES.custom) {
return customAdvancedVariableParser;
}
return () => null;
};
/**
* This method parses the templating property in the dashboard yml file.
* The templating property has variables that are rendered as input elements
* for the user to edit. The values from input elements are relayed to
* backend and eventually Prometheus API.
*
* This method currently is not used anywhere. Once the issue
* https://gitlab.com/gitlab-org/gitlab/-/issues/214536 is completed,
* this method will have been used by the monitoring dashboard.
*
* @param {Object} templating templating variables from the dashboard yml file
* @returns {Object} a map of processed templating variables
*/
export const parseTemplatingVariables = ({ variables = {} } = {}) =>
Object.entries(variables).reduce((acc, [key, variable]) => {
// get the parser
const parser = getVariableParser(variable);
// parse the variable
const parsedVar = parser(variable);
// for simple custom variable label is null and it should be
// replace with key instead
if (parsedVar) {
acc[key] = {
...parsedVar,
label: parsedVar.label || key,
};
}
return acc;
}, {});
export default {};
import { parseTemplatingVariables } from '~/monitoring/stores/variable_mapping';
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 }) => {
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