Commit f49b7225 authored by Jose Ivan Vargas's avatar Jose Ivan Vargas Committed by Paul Slaughter

Filter out custom variables from Url

This filters out any custom variables that the
user might send via the URL params to the
monitor dashboard.
parent 15c7001e
......@@ -4,6 +4,7 @@ import Dashboard from '~/monitoring/components/dashboard.vue';
import { parseBoolean } from '~/lib/utils/common_utils';
import { getParameterValues } from '~/lib/utils/url_utility';
import store from './stores';
import { promCustomVariablesFromUrl } from './utils';
Vue.use(GlToast);
......@@ -13,6 +14,8 @@ export default (props = {}) => {
if (el && el.dataset) {
const [currentDashboard] = getParameterValues('dashboard');
store.dispatch('monitoringDashboard/setVariables', promCustomVariablesFromUrl());
// eslint-disable-next-line no-new
new Vue({
el,
......
......@@ -80,6 +80,10 @@ export const setTimeRange = ({ commit }, timeRange) => {
commit(types.SET_TIME_RANGE, timeRange);
};
export const setVariables = ({ commit }, variables) => {
commit(types.SET_PROM_QUERY_VARIABLES, variables);
};
export const filterEnvironments = ({ commit, dispatch }, searchTerm) => {
commit(types.SET_ENVIRONMENTS_FILTER, searchTerm);
dispatch('fetchEnvironmentsData');
......@@ -218,12 +222,16 @@ export const fetchDashboardData = ({ state, dispatch, getters }) => {
*
* @param {metric} metric
*/
export const fetchPrometheusMetric = ({ commit }, { metric, defaultQueryParams }) => {
export const fetchPrometheusMetric = ({ commit, state }, { metric, defaultQueryParams }) => {
const queryParams = { ...defaultQueryParams };
if (metric.step) {
queryParams.step = metric.step;
}
if (state.promVariables.length > 0) {
queryParams.variables = state.promVariables;
}
commit(types.REQUEST_METRIC_RESULT, { metricId: metric.metricId });
return getPrometheusMetricResult(metric.prometheusEndpointPath, queryParams)
......
// Dashboard "skeleton", groups, panels and metrics
// Dashboard "skeleton", groups, panels, metrics, query variables
export const REQUEST_METRICS_DASHBOARD = 'REQUEST_METRICS_DASHBOARD';
export const RECEIVE_METRICS_DASHBOARD_SUCCESS = 'RECEIVE_METRICS_DASHBOARD_SUCCESS';
export const RECEIVE_METRICS_DASHBOARD_FAILURE = 'RECEIVE_METRICS_DASHBOARD_FAILURE';
export const SET_PROM_QUERY_VARIABLES = 'SET_PROM_QUERY_VARIABLES';
// Annotations
export const RECEIVE_ANNOTATIONS_SUCCESS = 'RECEIVE_ANNOTATIONS_SUCCESS';
......
......@@ -51,6 +51,18 @@ const emptyStateFromError = error => {
return metricStates.UNKNOWN_ERROR;
};
/**
* Maps an variables object to an array
* @returns {Array} The custom variables array to be send to the API
* in the format of [variable1, variable1_value]
* @param {Object} variables - Custom variables provided by the user
*/
const transformVariablesObjectArray = variables =>
Object.entries(variables)
.flat()
.map(encodeURIComponent);
export default {
/**
* Dashboard panels structure and global state
......@@ -169,4 +181,7 @@ export default {
state.expandedPanel.group = group;
state.expandedPanel.panel = panel;
},
[types.SET_PROM_QUERY_VARIABLES](state, variables) {
state.promVariables = transformVariablesObjectArray(variables);
},
};
......@@ -33,6 +33,7 @@ export default () => ({
panel: null,
},
allDashboards: [],
promVariables: [],
// Other project data
annotations: [],
......
import { omit } from 'lodash';
import { queryToObject, mergeUrlParams, removeParams } from '~/lib/utils/url_utility';
import {
timeRangeParamNames,
......@@ -5,6 +6,13 @@ import {
timeRangeToParams,
} from '~/lib/utils/datetime_range';
/**
* List of non time range url parameters
* This will be removed once we add support for free text variables
* via the dashboard yaml files in https://gitlab.com/gitlab-org/gitlab/-/issues/215689
*/
export const dashboardParams = ['dashboard', 'group', 'title', 'y_label'];
/**
* This method is used to validate if the graph data format for a chart component
* that needs a time series as a response from a prometheus query (queryRange) is
......@@ -113,6 +121,21 @@ export const timeRangeFromUrl = (search = window.location.search) => {
return timeRangeFromParams(params);
};
/**
* Returns an array with user defined variables from the URL
*
* @returns {Array} The custom variables defined by the
* user in the URL
* @param {String} New URL
*/
export const promCustomVariablesFromUrl = (search = window.location.search) => {
const params = queryToObject(search);
const paramsToRemove = timeRangeParamNames.concat(dashboardParams);
return omit(params, paramsToRemove);
};
/**
* Returns a URL with no time range based on the current URL.
*
......
---
title: In metrics dashboard use custom variables from URL in queries
merge_request: 30560
author:
type: added
......@@ -25,6 +25,7 @@ import {
clearExpandedPanel,
setGettingStartedEmptyState,
duplicateSystemDashboard,
setVariables,
} from '~/monitoring/stores/actions';
import {
gqClient,
......@@ -392,6 +393,29 @@ describe('Monitoring store actions', () => {
);
});
});
describe('setVariables', () => {
let mockedState;
beforeEach(() => {
mockedState = storeState();
});
it('should commit SET_PROM_QUERY_VARIABLES mutation', done => {
testAction(
setVariables,
{ pod: 'POD' },
mockedState,
[
{
type: types.SET_PROM_QUERY_VARIABLES,
payload: { pod: 'POD' },
},
],
[],
done,
);
});
});
describe('fetchDashboard', () => {
let dispatch;
let state;
......
......@@ -364,4 +364,18 @@ describe('Monitoring mutations', () => {
expect(stateCopy.expandedPanel.panel).toEqual(null);
});
});
describe('SET_PROM_QUERY_VARIABLES', () => {
it('stores an empty variables array when no custom variables are given', () => {
mutations[types.SET_PROM_QUERY_VARIABLES](stateCopy, {});
expect(stateCopy.promVariables).toEqual([]);
});
it('stores variables in the key key_value format in the array', () => {
mutations[types.SET_PROM_QUERY_VARIABLES](stateCopy, { pod: 'POD', stage: 'main ops' });
expect(stateCopy.promVariables).toEqual(['pod', 'POD', 'stage', 'main%20ops']);
});
});
});
......@@ -169,6 +169,43 @@ describe('monitoring/utils', () => {
});
});
describe('promCustomVariablesFromUrl', () => {
const { promCustomVariablesFromUrl } = monitoringUtils;
beforeEach(() => {
jest.spyOn(urlUtils, 'queryToObject');
});
afterEach(() => {
urlUtils.queryToObject.mockRestore();
});
it('returns an object with only the custom variables', () => {
urlUtils.queryToObject.mockReturnValueOnce({
dashboard: '.gitlab/dashboards/custom_dashboard.yml',
y_label: 'memory usage',
group: 'kubernetes',
title: 'Kubernetes memory total',
start: '2020-05-06',
end: '2020-05-07',
duration_seconds: '86400',
direction: 'left',
anchor: 'top',
pod: 'POD',
});
expect(promCustomVariablesFromUrl()).toEqual(expect.objectContaining({ pod: 'POD' }));
});
it('returns an empty object when no custom variables are present', () => {
urlUtils.queryToObject.mockReturnValueOnce({
dashboard: '.gitlab/dashboards/custom_dashboard.yml',
});
expect(promCustomVariablesFromUrl()).toStrictEqual({});
});
});
describe('removeTimeRangeParams', () => {
const { removeTimeRangeParams } = monitoringUtils;
......
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