2020-05-11 21:09:40 +00:00
|
|
|
import { isString } from 'lodash';
|
2020-06-18 18:08:58 +00:00
|
|
|
import { templatingVariablesFromUrl } from '../utils';
|
2020-05-08 12:09:37 +00:00
|
|
|
import { VARIABLE_TYPES } from '../constants';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This file exclusively deals with parsing user-defined variables
|
|
|
|
* in dashboard yml file.
|
|
|
|
*
|
2020-05-11 21:09:40 +00:00
|
|
|
* As of 13.0, simple text, advanced text, simple custom and
|
|
|
|
* advanced custom variables are supported.
|
2020-05-08 12:09:37 +00:00
|
|
|
*
|
|
|
|
* In the future iterations, text and query variables will be
|
|
|
|
* supported
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2020-05-11 21:09:40 +00:00
|
|
|
* Simple text variable is a string value only.
|
|
|
|
* This method parses such variables to a standard format.
|
2020-05-08 12:09:37 +00:00
|
|
|
*
|
2020-05-11 21:09:40 +00:00
|
|
|
* @param {String|Object} simpleTextVar
|
|
|
|
* @returns {Object}
|
2020-05-08 12:09:37 +00:00
|
|
|
*/
|
2020-12-23 21:10:24 +00:00
|
|
|
const textSimpleVariableParser = (simpleTextVar) => ({
|
2020-05-11 21:09:40 +00:00
|
|
|
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}
|
|
|
|
*/
|
2020-12-23 21:10:24 +00:00
|
|
|
const textAdvancedVariableParser = (advTextVar) => ({
|
2020-05-11 21:09:40 +00:00
|
|
|
type: VARIABLE_TYPES.text,
|
|
|
|
label: advTextVar.label,
|
|
|
|
value: advTextVar.options.default_value,
|
|
|
|
});
|
2020-05-08 12:09:37 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Normalize simple and advanced custom variable options to a standard
|
|
|
|
* format
|
|
|
|
* @param {Object} custom variable option
|
|
|
|
* @returns {Object} normalized custom variable options
|
|
|
|
*/
|
2020-07-03 09:08:53 +00:00
|
|
|
const normalizeVariableValues = ({ default: defaultOpt = false, text, value = null }) => ({
|
2020-05-08 12:09:37 +00:00
|
|
|
default: defaultOpt,
|
2020-05-20 06:08:06 +00:00
|
|
|
text: text || value,
|
2020-05-08 12:09:37 +00:00
|
|
|
value,
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Custom advanced variables are rendered as dropdown elements in the dashboard
|
|
|
|
* header. This method parses advanced custom variables.
|
|
|
|
*
|
2020-05-16 00:08:12 +00:00
|
|
|
* The default value is the option with default set to true or the first option
|
|
|
|
* if none of the options have default prop true.
|
|
|
|
*
|
2020-06-24 03:08:44 +00:00
|
|
|
* @param {Object} advVariable advanced custom variable
|
2020-05-08 12:09:37 +00:00
|
|
|
* @returns {Object}
|
|
|
|
*/
|
2020-12-23 21:10:24 +00:00
|
|
|
const customAdvancedVariableParser = (advVariable) => {
|
2020-06-24 03:08:44 +00:00
|
|
|
const values = (advVariable?.options?.values ?? []).map(normalizeVariableValues);
|
2020-12-23 21:10:24 +00:00
|
|
|
const defaultValue = values.find((opt) => opt.default === true) || values[0];
|
2020-05-08 12:09:37 +00:00
|
|
|
return {
|
|
|
|
type: VARIABLE_TYPES.custom,
|
|
|
|
label: advVariable.label,
|
2020-06-24 03:08:44 +00:00
|
|
|
options: {
|
|
|
|
values,
|
|
|
|
},
|
2020-07-03 09:08:53 +00:00
|
|
|
value: defaultValue?.value || null,
|
2020-05-08 12:09:37 +00:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2020-05-11 21:09:40 +00:00
|
|
|
/**
|
|
|
|
* 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}
|
|
|
|
*/
|
2020-12-23 21:10:24 +00:00
|
|
|
export const parseSimpleCustomValues = (opt) => ({ text: opt, value: opt });
|
2020-05-11 21:09:40 +00:00
|
|
|
|
2020-05-08 12:09:37 +00:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*
|
2020-05-16 00:08:12 +00:00
|
|
|
* The default value is set to the first option as the user cannot
|
|
|
|
* set a default value for this format
|
|
|
|
*
|
2020-05-08 12:09:37 +00:00
|
|
|
* @param {Array} customVariable array of options
|
|
|
|
* @returns {Object}
|
|
|
|
*/
|
2020-12-23 21:10:24 +00:00
|
|
|
const customSimpleVariableParser = (simpleVar) => {
|
2020-06-24 03:08:44 +00:00
|
|
|
const values = (simpleVar || []).map(parseSimpleCustomValues);
|
2020-05-08 12:09:37 +00:00
|
|
|
return {
|
|
|
|
type: VARIABLE_TYPES.custom,
|
|
|
|
label: null,
|
2020-07-03 09:08:53 +00:00
|
|
|
value: values[0].value || null,
|
2020-06-24 03:08:44 +00:00
|
|
|
options: {
|
|
|
|
values: values.map(normalizeVariableValues),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2020-07-03 09:08:53 +00:00
|
|
|
const metricLabelValuesVariableParser = ({ label, options = {} }) => ({
|
|
|
|
type: VARIABLE_TYPES.metric_label_values,
|
|
|
|
label,
|
|
|
|
value: null,
|
|
|
|
options: {
|
|
|
|
prometheusEndpointPath: options.prometheus_endpoint_path || '',
|
|
|
|
label: options.label || null,
|
|
|
|
values: [], // values are initially empty
|
|
|
|
},
|
|
|
|
});
|
2020-05-08 12:09:37 +00:00
|
|
|
|
2020-05-11 21:09:40 +00:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
2020-12-23 21:10:24 +00:00
|
|
|
const isSimpleCustomVariable = (customVar) => Array.isArray(customVar);
|
2020-05-11 21:09:40 +00:00
|
|
|
|
2020-05-08 12:09:37 +00:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
2020-12-23 21:10:24 +00:00
|
|
|
const getVariableParser = (variable) => {
|
2020-06-24 03:08:44 +00:00
|
|
|
if (isString(variable)) {
|
|
|
|
return textSimpleVariableParser;
|
|
|
|
} else if (isSimpleCustomVariable(variable)) {
|
2020-05-08 12:09:37 +00:00
|
|
|
return customSimpleVariableParser;
|
2020-05-11 21:09:40 +00:00
|
|
|
} else if (variable.type === VARIABLE_TYPES.text) {
|
|
|
|
return textAdvancedVariableParser;
|
2020-06-24 03:08:44 +00:00
|
|
|
} else if (variable.type === VARIABLE_TYPES.custom) {
|
|
|
|
return customAdvancedVariableParser;
|
|
|
|
} else if (variable.type === VARIABLE_TYPES.metric_label_values) {
|
|
|
|
return metricLabelValuesVariableParser;
|
2020-05-08 12:09:37 +00:00
|
|
|
}
|
|
|
|
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.
|
|
|
|
*
|
2020-07-03 09:08:53 +00:00
|
|
|
* @param {Object} templating variables from the dashboard yml file
|
|
|
|
* @returns {array} An array of variables to display as inputs
|
2020-05-08 12:09:37 +00:00
|
|
|
*/
|
2020-07-03 09:08:53 +00:00
|
|
|
export const parseTemplatingVariables = (ymlVariables = {}) =>
|
|
|
|
Object.entries(ymlVariables).reduce((acc, [name, ymlVariable]) => {
|
2020-05-08 12:09:37 +00:00
|
|
|
// get the parser
|
2020-07-03 09:08:53 +00:00
|
|
|
const parser = getVariableParser(ymlVariable);
|
2020-05-08 12:09:37 +00:00
|
|
|
// parse the variable
|
2020-07-03 09:08:53 +00:00
|
|
|
const variable = parser(ymlVariable);
|
2020-05-08 12:09:37 +00:00
|
|
|
// for simple custom variable label is null and it should be
|
|
|
|
// replace with key instead
|
2020-07-03 09:08:53 +00:00
|
|
|
if (variable) {
|
|
|
|
acc.push({
|
|
|
|
...variable,
|
|
|
|
name,
|
|
|
|
label: variable.label || name,
|
|
|
|
});
|
2020-05-08 12:09:37 +00:00
|
|
|
}
|
|
|
|
return acc;
|
2020-07-03 09:08:53 +00:00
|
|
|
}, []);
|
2020-05-08 12:09:37 +00:00
|
|
|
|
2020-06-18 18:08:58 +00:00
|
|
|
/**
|
|
|
|
* Custom variables are defined in the dashboard yml file
|
|
|
|
* and their values can be passed through the URL.
|
|
|
|
*
|
|
|
|
* On component load, this method merges variables data
|
|
|
|
* from the yml file with URL data to store in the Vuex store.
|
|
|
|
* Not all params coming from the URL need to be stored. Only
|
|
|
|
* the ones that have a corresponding variable defined in the
|
|
|
|
* yml file.
|
|
|
|
*
|
|
|
|
* This ensures that there is always a single source of truth
|
|
|
|
* for variables
|
|
|
|
*
|
|
|
|
* This method can be improved further. See the below issue
|
|
|
|
* https://gitlab.com/gitlab-org/gitlab/-/issues/217713
|
|
|
|
*
|
2020-07-03 09:08:53 +00:00
|
|
|
* @param {array} parsedYmlVariables - template variables from yml file
|
2020-06-18 18:08:58 +00:00
|
|
|
* @returns {Object}
|
|
|
|
*/
|
2020-07-03 09:08:53 +00:00
|
|
|
export const mergeURLVariables = (parsedYmlVariables = []) => {
|
2020-06-18 18:08:58 +00:00
|
|
|
const varsFromURL = templatingVariablesFromUrl();
|
2020-12-23 21:10:24 +00:00
|
|
|
parsedYmlVariables.forEach((variable) => {
|
2020-07-03 09:08:53 +00:00
|
|
|
const { name } = variable;
|
|
|
|
if (Object.prototype.hasOwnProperty.call(varsFromURL, name)) {
|
|
|
|
Object.assign(variable, { value: varsFromURL[name] });
|
2020-06-18 18:08:58 +00:00
|
|
|
}
|
|
|
|
});
|
2020-07-03 09:08:53 +00:00
|
|
|
return parsedYmlVariables;
|
2020-06-18 18:08:58 +00:00
|
|
|
};
|
|
|
|
|
2020-06-24 03:08:44 +00:00
|
|
|
/**
|
|
|
|
* Converts series data to options that can be added to a
|
|
|
|
* variable. Series data is returned from the Prometheus API
|
|
|
|
* `/api/v1/series`.
|
|
|
|
*
|
|
|
|
* Finds a `label` in the series data, so it can be used as
|
|
|
|
* a filter.
|
|
|
|
*
|
|
|
|
* For example, for the arguments:
|
|
|
|
*
|
|
|
|
* {
|
|
|
|
* "label": "job"
|
|
|
|
* "data" : [
|
|
|
|
* {
|
|
|
|
* "__name__" : "up",
|
|
|
|
* "job" : "prometheus",
|
|
|
|
* "instance" : "localhost:9090"
|
|
|
|
* },
|
|
|
|
* {
|
|
|
|
* "__name__" : "up",
|
|
|
|
* "job" : "node",
|
|
|
|
* "instance" : "localhost:9091"
|
|
|
|
* },
|
|
|
|
* {
|
|
|
|
* "__name__" : "process_start_time_seconds",
|
|
|
|
* "job" : "prometheus",
|
|
|
|
* "instance" : "localhost:9090"
|
|
|
|
* }
|
|
|
|
* ]
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* It returns all the different "job" values:
|
|
|
|
*
|
|
|
|
* [
|
|
|
|
* {
|
|
|
|
* "label": "node",
|
|
|
|
* "value": "node"
|
|
|
|
* },
|
|
|
|
* {
|
|
|
|
* "label": "prometheus",
|
|
|
|
* "value": "prometheus"
|
|
|
|
* }
|
|
|
|
* ]
|
|
|
|
*
|
|
|
|
* @param {options} options object
|
|
|
|
* @param {options.seriesLabel} name of the searched series label
|
|
|
|
* @param {options.data} series data from the series API
|
|
|
|
* @return {array} Options objects with the shape `{ label, value }`
|
|
|
|
*
|
|
|
|
* @see https://prometheus.io/docs/prometheus/latest/querying/api/#finding-series-by-label-matchers
|
|
|
|
*/
|
|
|
|
export const optionsFromSeriesData = ({ label, data = [] }) => {
|
|
|
|
const optionsSet = data.reduce((set, seriesObject) => {
|
|
|
|
// Use `new Set` to deduplicate options
|
|
|
|
if (seriesObject[label]) {
|
|
|
|
set.add(seriesObject[label]);
|
|
|
|
}
|
|
|
|
return set;
|
|
|
|
}, new Set());
|
|
|
|
|
|
|
|
return [...optionsSet].map(parseSimpleCustomValues);
|
|
|
|
};
|