Welcome to mirror list, hosted at ThFree Co, Russian Federation.

variable_mapping.js « stores « monitoring « javascripts « assets « app - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 9245ffdb3b9a900960527e55c9552444f80e1d53 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
import { isString } from 'lodash';
import { templatingVariablesFromUrl } from '../utils';
import { VARIABLE_TYPES } from '../constants';

/**
 * This file exclusively deals with parsing user-defined variables
 * in dashboard yml file.
 *
 * 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
 *
 */

/**
 * Simple text variable is a string value only.
 * This method parses such variables to a standard format.
 *
 * @param {String|Object} simpleTextVar
 * @returns {Object}
 */
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
 * format
 * @param {Object} custom variable option
 * @returns {Object} normalized custom variable options
 */
const normalizeVariableValues = ({ default: defaultOpt = false, text, value = null }) => ({
  default: defaultOpt,
  text: text || value,
  value,
});

/**
 * Custom advanced variables are rendered as dropdown elements in the dashboard
 * header. This method parses advanced custom variables.
 *
 * 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 advanced custom variable
 * @returns {Object}
 */
const customAdvancedVariableParser = advVariable => {
  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,
    options: {
      values,
    },
    value: defaultValue?.value || null,
  };
};

/**
 * 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}
 */
export const parseSimpleCustomValues = opt => ({ text: opt, value: opt });

/**
 * 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.
 *
 * The default value is set to the first option as the user cannot
 * set a default value for this format
 *
 * @param {Array} customVariable array of options
 * @returns {Object}
 */
const customSimpleVariableParser = simpleVar => {
  const values = (simpleVar || []).map(parseSimpleCustomValues);
  return {
    type: VARIABLE_TYPES.custom,
    label: null,
    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.
 *
 * @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
 * 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 (isString(variable)) {
    return textSimpleVariableParser;
  } else if (isSimpleCustomVariable(variable)) {
    return customSimpleVariableParser;
  } else if (variable.type === VARIABLE_TYPES.text) {
    return textAdvancedVariableParser;
  } else if (variable.type === VARIABLE_TYPES.custom) {
    return customAdvancedVariableParser;
  } else if (variable.type === VARIABLE_TYPES.metric_label_values) {
    return metricLabelValuesVariableParser;
  }
  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.
 *
 * @param {Object} templating variables from the dashboard yml file
 * @returns {array} An array of variables to display as inputs
 */
export const parseTemplatingVariables = (ymlVariables = {}) =>
  Object.entries(ymlVariables).reduce((acc, [name, ymlVariable]) => {
    // get the parser
    const parser = getVariableParser(ymlVariable);
    // parse the variable
    const variable = parser(ymlVariable);
    // for simple custom variable label is null and it should be
    // replace with key instead
    if (variable) {
      acc.push({
        ...variable,
        name,
        label: variable.label || name,
      });
    }
    return acc;
  }, []);

/**
 * 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
 *
 * @param {array} parsedYmlVariables - template variables from yml file
 * @returns {Object}
 */
export const mergeURLVariables = (parsedYmlVariables = []) => {
  const varsFromURL = templatingVariablesFromUrl();
  parsedYmlVariables.forEach(variable => {
    const { name } = variable;
    if (Object.prototype.hasOwnProperty.call(varsFromURL, name)) {
      Object.assign(variable, { value: varsFromURL[name] });
    }
  });
  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 {};