Commit 09d6a1bb authored by Dhiraj Bodicherla's avatar Dhiraj Bodicherla

Add parsers for dashboard yml custom variables

The dashboard yml file now supports custom variables.
This MR adds parsers to extract the variables
and parse them
parent a11d61d1
......@@ -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