Commit f90206a9 authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch '195739' into 'master'

Update query labels dynamically for embedded charts

See merge request gitlab-org/gitlab!29034
parents 27b453cb 42eef06a
/**
* @param {String} queryLabel - Default query label for chart
* @param {Object} metricAttributes - Default metric attribute values (e.g. method, instance)
* @returns {String} The formatted query label
* @example
* singleAttributeLabel('app', {__name__: "up", app: "prometheus"}) -> "app: prometheus"
*/
const singleAttributeLabel = (queryLabel, metricAttributes) => {
if (!queryLabel) return '';
const relevantAttribute = queryLabel.toLowerCase().replace(' ', '_');
const value = metricAttributes[relevantAttribute];
if (!value) return '';
return `${queryLabel}: ${value}`;
};
/**
* @param {String} queryLabel - Default query label for chart
* @param {Object} metricAttributes - Default metric attribute values (e.g. method, instance)
* @returns {String} The formatted query label
* @example
* templatedLabel('__name__', {__name__: "up", app: "prometheus"}) -> "__name__"
*/
const templatedLabel = (queryLabel, metricAttributes) => {
if (!queryLabel) return '';
// eslint-disable-next-line array-callback-return
Object.entries(metricAttributes).map(([templateVar, label]) => {
const regex = new RegExp(`{{\\s*${templateVar}\\s*}}`, 'g');
// eslint-disable-next-line no-param-reassign
queryLabel = queryLabel.replace(regex, label);
});
return queryLabel;
};
/**
* @param {Object} metricAttributes - Default metric attribute values (e.g. method, instance)
* @returns {String} The formatted query label
* @example
* multiMetricLabel('', {__name__: "up", app: "prometheus"}) -> "__name__: up, app: prometheus"
*/
const multiMetricLabel = metricAttributes => {
return Object.entries(metricAttributes)
.map(([templateVar, label]) => `${templateVar}: ${label}`)
.join(', ');
};
/**
* @param {String} queryLabel - Default query label for chart
* @param {Object} metricAttributes - Default metric attribute values (e.g. method, instance)
* @returns {String} The formatted query label
*/
const getSeriesLabel = (queryLabel, metricAttributes) => {
return (
singleAttributeLabel(queryLabel, metricAttributes) ||
templatedLabel(queryLabel, metricAttributes) ||
multiMetricLabel(metricAttributes) ||
queryLabel
);
};
/** /**
* @param {Array} queryResults - Array of Result objects * @param {Array} queryResults - Array of Result objects
* @param {Object} defaultConfig - Default chart config values (e.g. lineStyle, name) * @param {Object} defaultConfig - Default chart config values (e.g. lineStyle, name)
...@@ -12,21 +72,11 @@ export const makeDataSeries = (queryResults, defaultConfig) => ...@@ -12,21 +72,11 @@ export const makeDataSeries = (queryResults, defaultConfig) =>
if (!data.length) { if (!data.length) {
return null; return null;
} }
const relevantMetric = defaultConfig.name.toLowerCase().replace(' ', '_');
const name = result.metric[relevantMetric];
const series = { data }; const series = { data };
if (name) { return {
series.name = `${defaultConfig.name}: ${name}`; ...defaultConfig,
} else { ...series,
series.name = defaultConfig.name; name: getSeriesLabel(defaultConfig.name, result.metric),
Object.keys(result.metric).forEach(templateVar => { };
const value = result.metric[templateVar];
const regex = new RegExp(`{{\\s*${templateVar}\\s*}}`, 'g');
series.name = series.name.replace(regex, value);
});
}
return { ...defaultConfig, ...series };
}) })
.filter(series => series !== null); .filter(series => series !== null);
...@@ -58,7 +58,7 @@ export default { ...@@ -58,7 +58,7 @@ export default {
}, },
methods: { methods: {
formatLegendLabel(query) { formatLegendLabel(query) {
return `${query.label}`; return query.label;
}, },
onResize() { onResize() {
if (!this.$refs.barChart) return; if (!this.$refs.barChart) return;
......
...@@ -76,7 +76,7 @@ export default { ...@@ -76,7 +76,7 @@ export default {
}, },
methods: { methods: {
formatLegendLabel(query) { formatLegendLabel(query) {
return `${query.label}`; return query.label;
}, },
onResize() { onResize() {
if (!this.$refs.columnChart) return; if (!this.$refs.columnChart) return;
......
...@@ -251,7 +251,7 @@ export default { ...@@ -251,7 +251,7 @@ export default {
}, },
methods: { methods: {
formatLegendLabel(query) { formatLegendLabel(query) {
return `${query.label}`; return query.label;
}, },
isTooltipOfType(tooltipType, defaultType) { isTooltipOfType(tooltipType, defaultType) {
return tooltipType === defaultType; return tooltipType === defaultType;
......
...@@ -68,12 +68,11 @@ export const parseEnvironmentsResponse = (response = [], projectPath) => ...@@ -68,12 +68,11 @@ export const parseEnvironmentsResponse = (response = [], projectPath) =>
* https://gitlab.com/gitlab-org/gitlab/issues/207198 * https://gitlab.com/gitlab-org/gitlab/issues/207198
* *
* @param {Array} metrics - Array of prometheus metrics * @param {Array} metrics - Array of prometheus metrics
* @param {String} defaultLabel - Default label for metrics
* @returns {Object} * @returns {Object}
*/ */
const mapToMetricsViewModel = (metrics, defaultLabel) => const mapToMetricsViewModel = metrics =>
metrics.map(({ label, id, metric_id, query_range, prometheus_endpoint_path, ...metric }) => ({ metrics.map(({ label, id, metric_id, query_range, prometheus_endpoint_path, ...metric }) => ({
label: label || defaultLabel, label,
queryRange: query_range, queryRange: query_range,
prometheusEndpointPath: prometheus_endpoint_path, prometheusEndpointPath: prometheus_endpoint_path,
metricId: uniqMetricsId({ metric_id, id }), metricId: uniqMetricsId({ metric_id, id }),
......
---
title: Update query labels dynamically for embedded charts
merge_request: 29034
author:
type: other
...@@ -37,5 +37,8 @@ export function alertsValidator(value) { ...@@ -37,5 +37,8 @@ export function alertsValidator(value) {
// } // }
// ] // ]
export function queriesValidator(value) { export function queriesValidator(value) {
return value.every(query => query.metricId && typeof query.metricId === 'string' && query.label); return value.every(
query =>
query.metricId && typeof query.metricId === 'string' && typeof query.label === 'string',
);
} }
...@@ -56,6 +56,32 @@ describe('monitor helper', () => { ...@@ -56,6 +56,32 @@ describe('monitor helper', () => {
expect(result.name).toEqual('brpop'); expect(result.name).toEqual('brpop');
}); });
it('supports a multi metric label template expression', () => {
const config = {
...defaultConfig,
name: '',
};
const [result] = monitorHelper.makeDataSeries(
[
{
metric: {
backend: 'HA Server',
frontend: 'BA Server',
app: 'prometheus',
instance: 'k8 cluster 1',
},
values: series,
},
],
config,
);
expect(result.name).toBe(
'backend: HA Server, frontend: BA Server, app: prometheus, instance: k8 cluster 1',
);
});
it('supports space-padded template expressions', () => { it('supports space-padded template expressions', () => {
const config = { const config = {
...defaultConfig, ...defaultConfig,
......
...@@ -251,7 +251,7 @@ describe('mapToDashboardViewModel', () => { ...@@ -251,7 +251,7 @@ describe('mapToDashboardViewModel', () => {
}; };
it('creates a metric', () => { it('creates a metric', () => {
const dashboard = dashboardWithMetric({}); const dashboard = dashboardWithMetric({ label: 'Panel Label' });
expect(getMappedMetric(dashboard)).toEqual({ expect(getMappedMetric(dashboard)).toEqual({
label: expect.any(String), label: expect.any(String),
...@@ -268,11 +268,11 @@ describe('mapToDashboardViewModel', () => { ...@@ -268,11 +268,11 @@ describe('mapToDashboardViewModel', () => {
expect(getMappedMetric(dashboard).metricId).toEqual('1_http_responses'); expect(getMappedMetric(dashboard).metricId).toEqual('1_http_responses');
}); });
it('creates a metric with a default label', () => { it('creates a metric without a default label', () => {
const dashboard = dashboardWithMetric({}); const dashboard = dashboardWithMetric({});
expect(getMappedMetric(dashboard)).toMatchObject({ expect(getMappedMetric(dashboard)).toMatchObject({
label: defaultLabel, label: undefined,
}); });
}); });
......
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