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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/monitoring/csv_export.js')
-rw-r--r--app/assets/javascripts/monitoring/csv_export.js147
1 files changed, 147 insertions, 0 deletions
diff --git a/app/assets/javascripts/monitoring/csv_export.js b/app/assets/javascripts/monitoring/csv_export.js
new file mode 100644
index 00000000000..734e8dc07a7
--- /dev/null
+++ b/app/assets/javascripts/monitoring/csv_export.js
@@ -0,0 +1,147 @@
+import { getSeriesLabel } from '~/helpers/monitor_helper';
+
+/**
+ * Returns a label for a header of the csv.
+ *
+ * Includes double quotes ("") in case the header includes commas or other separator.
+ *
+ * @param {String} axisLabel
+ * @param {String} metricLabel
+ * @param {Object} metricAttributes
+ */
+const csvHeader = (axisLabel, metricLabel, metricAttributes = {}) =>
+ `${axisLabel} > ${getSeriesLabel(metricLabel, metricAttributes)}`;
+
+/**
+ * Returns an array with the header labels given a list of metrics
+ *
+ * ```
+ * metrics = [
+ * {
+ * label: "..." // user-defined label
+ * result: [
+ * {
+ * metric: { ... } // metricAttributes
+ * },
+ * ...
+ * ]
+ * },
+ * ...
+ * ]
+ * ```
+ *
+ * When metrics have a `label` or `metricAttributes`, they are
+ * used to generate the column name.
+ *
+ * @param {String} axisLabel - Main label
+ * @param {Array} metrics - Metrics with results
+ */
+const csvMetricHeaders = (axisLabel, metrics) =>
+ metrics.flatMap(({ label, result }) =>
+ // The `metric` in a `result` is a map of `metricAttributes`
+ // contains key-values to identify the series, rename it
+ // here for clarity.
+ result.map(({ metric: metricAttributes }) => {
+ return csvHeader(axisLabel, label, metricAttributes);
+ }),
+ );
+
+/**
+ * Returns a (flat) array with all the values arrays in each
+ * metric and series
+ *
+ * ```
+ * metrics = [
+ * {
+ * result: [
+ * {
+ * values: [ ... ] // `values`
+ * },
+ * ...
+ * ]
+ * },
+ * ...
+ * ]
+ * ```
+ *
+ * @param {Array} metrics - Metrics with results
+ */
+const csvMetricValues = metrics =>
+ metrics.flatMap(({ result }) => result.map(res => res.values || []));
+
+/**
+ * Returns headers and rows for csv, sorted by their timestamp.
+ *
+ * {
+ * headers: ["timestamp", "<col_1_name>", "col_2_name"],
+ * rows: [
+ * [ <timestamp>, <col_1_value>, <col_2_value> ],
+ * [ <timestamp>, <col_1_value>, <col_2_value> ]
+ * ...
+ * ]
+ * }
+ *
+ * @param {Array} metricHeaders
+ * @param {Array} metricValues
+ */
+const csvData = (metricHeaders, metricValues) => {
+ const rowsByTimestamp = {};
+
+ metricValues.forEach((values, colIndex) => {
+ values.forEach(([timestamp, value]) => {
+ if (!rowsByTimestamp[timestamp]) {
+ rowsByTimestamp[timestamp] = [];
+ }
+ // `value` should be in the right column
+ rowsByTimestamp[timestamp][colIndex] = value;
+ });
+ });
+
+ const rows = Object.keys(rowsByTimestamp)
+ .sort()
+ .map(timestamp => {
+ // force each row to have the same number of entries
+ rowsByTimestamp[timestamp].length = metricHeaders.length;
+ // add timestamp as the first entry
+ return [timestamp, ...rowsByTimestamp[timestamp]];
+ });
+
+ // Escape double quotes and enclose headers:
+ // "If double-quotes are used to enclose fields, then a double-quote
+ // appearing inside a field must be escaped by preceding it with
+ // another double quote."
+ // https://tools.ietf.org/html/rfc4180#page-2
+ const headers = metricHeaders.map(header => `"${header.replace(/"/g, '""')}"`);
+
+ return {
+ headers: ['timestamp', ...headers],
+ rows,
+ };
+};
+
+/**
+ * Returns dashboard panel's data in a string in CSV format
+ *
+ * @param {Object} graphData - Panel contents
+ * @returns {String}
+ */
+// eslint-disable-next-line import/prefer-default-export
+export const graphDataToCsv = graphData => {
+ const delimiter = ',';
+ const br = '\r\n';
+ const { metrics = [], y_label: axisLabel } = graphData;
+
+ const metricsWithResults = metrics.filter(metric => metric.result);
+ const metricHeaders = csvMetricHeaders(axisLabel, metricsWithResults);
+ const metricValues = csvMetricValues(metricsWithResults);
+ const { headers, rows } = csvData(metricHeaders, metricValues);
+
+ if (rows.length === 0) {
+ return '';
+ }
+
+ const headerLine = headers.join(delimiter) + br;
+ const lines = rows.map(row => row.join(delimiter));
+
+ return headerLine + lines.join(br) + br;
+};