diff options
Diffstat (limited to 'app/assets/javascripts/monitoring/stores/variable_mapping.js')
-rw-r--r-- | app/assets/javascripts/monitoring/stores/variable_mapping.js | 158 |
1 files changed, 115 insertions, 43 deletions
diff --git a/app/assets/javascripts/monitoring/stores/variable_mapping.js b/app/assets/javascripts/monitoring/stores/variable_mapping.js index c0a8150063b..9245ffdb3b9 100644 --- a/app/assets/javascripts/monitoring/stores/variable_mapping.js +++ b/app/assets/javascripts/monitoring/stores/variable_mapping.js @@ -46,7 +46,7 @@ const textAdvancedVariableParser = advTextVar => ({ * @param {Object} custom variable option * @returns {Object} normalized custom variable options */ -const normalizeCustomVariableOptions = ({ default: defaultOpt = false, text, value }) => ({ +const normalizeVariableValues = ({ default: defaultOpt = false, text, value = null }) => ({ default: defaultOpt, text: text || value, value, @@ -59,17 +59,19 @@ const normalizeCustomVariableOptions = ({ default: defaultOpt = false, text, val * The default value is the option with default set to true or the first option * if none of the options have default prop true. * - * @param {Object} advVariable advance custom variable + * @param {Object} advVariable advanced custom variable * @returns {Object} */ const customAdvancedVariableParser = advVariable => { - const options = (advVariable?.options?.values ?? []).map(normalizeCustomVariableOptions); - const defaultOpt = options.find(opt => opt.default === true) || options[0]; + const values = (advVariable?.options?.values ?? []).map(normalizeVariableValues); + const defaultValue = values.find(opt => opt.default === true) || values[0]; return { type: VARIABLE_TYPES.custom, label: advVariable.label, - value: defaultOpt?.value, - options, + options: { + values, + }, + value: defaultValue?.value || null, }; }; @@ -80,7 +82,7 @@ const customAdvancedVariableParser = advVariable => { * @param {String} opt option from simple custom variable * @returns {Object} */ -const parseSimpleCustomOptions = opt => ({ text: opt, value: opt }); +export const parseSimpleCustomValues = opt => ({ text: opt, value: opt }); /** * Custom simple variables are rendered as dropdown elements in the dashboard @@ -95,15 +97,28 @@ const parseSimpleCustomOptions = opt => ({ text: opt, value: opt }); * @returns {Object} */ const customSimpleVariableParser = simpleVar => { - const options = (simpleVar || []).map(parseSimpleCustomOptions); + const values = (simpleVar || []).map(parseSimpleCustomValues); return { type: VARIABLE_TYPES.custom, - value: options[0].value, label: null, - options: options.map(normalizeCustomVariableOptions), + value: values[0].value || null, + options: { + values: values.map(normalizeVariableValues), + }, }; }; +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 + }, +}); + /** * Utility method to determine if a custom variable is * simple or not. If its not simple, it is advanced. @@ -123,14 +138,16 @@ const isSimpleCustomVariable = customVar => Array.isArray(customVar); * @return {Function} parser method */ const getVariableParser = variable => { - if (isSimpleCustomVariable(variable)) { + if (isString(variable)) { + return textSimpleVariableParser; + } else if (isSimpleCustomVariable(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; + } else if (variable.type === VARIABLE_TYPES.custom) { + return customAdvancedVariableParser; + } else if (variable.type === VARIABLE_TYPES.metric_label_values) { + return metricLabelValuesVariableParser; } return () => null; }; @@ -141,29 +158,26 @@ const getVariableParser = variable => { * 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 + * @param {Object} templating variables from the dashboard yml file + * @returns {array} An array of variables to display as inputs */ -export const parseTemplatingVariables = ({ variables = {} } = {}) => - Object.entries(variables).reduce((acc, [key, variable]) => { +export const parseTemplatingVariables = (ymlVariables = {}) => + Object.entries(ymlVariables).reduce((acc, [name, ymlVariable]) => { // get the parser - const parser = getVariableParser(variable); + const parser = getVariableParser(ymlVariable); // parse the variable - const parsedVar = parser(variable); + const variable = parser(ymlVariable); // for simple custom variable label is null and it should be // replace with key instead - if (parsedVar) { - acc[key] = { - ...parsedVar, - label: parsedVar.label || key, - }; + if (variable) { + acc.push({ + ...variable, + name, + label: variable.label || name, + }); } return acc; - }, {}); + }, []); /** * Custom variables are defined in the dashboard yml file @@ -181,23 +195,81 @@ export const parseTemplatingVariables = ({ variables = {} } = {}) => * This method can be improved further. See the below issue * https://gitlab.com/gitlab-org/gitlab/-/issues/217713 * - * @param {Object} varsFromYML template variables from yml file + * @param {array} parsedYmlVariables - template variables from yml file * @returns {Object} */ -export const mergeURLVariables = (varsFromYML = {}) => { +export const mergeURLVariables = (parsedYmlVariables = []) => { const varsFromURL = templatingVariablesFromUrl(); - const variables = {}; - Object.keys(varsFromYML).forEach(key => { - if (Object.prototype.hasOwnProperty.call(varsFromURL, key)) { - variables[key] = { - ...varsFromYML[key], - value: varsFromURL[key], - }; - } else { - variables[key] = varsFromYML[key]; + parsedYmlVariables.forEach(variable => { + const { name } = variable; + if (Object.prototype.hasOwnProperty.call(varsFromURL, name)) { + Object.assign(variable, { value: varsFromURL[name] }); } }); - return variables; + return parsedYmlVariables; +}; + +/** + * 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); }; export default {}; |