Commit c1c7bfe8 authored by Ezekiel Kigbo's avatar Ezekiel Kigbo

Merge branch 'refactor-variables-for-array' into 'master'

Simplify dashboard variable manipulation by using arrays

See merge request gitlab-org/gitlab!34821
parents 72002e21 43b27ea4
...@@ -164,7 +164,7 @@ export default { ...@@ -164,7 +164,7 @@ export default {
]), ]),
...mapGetters('monitoringDashboard', ['selectedDashboard', 'getMetricStates']), ...mapGetters('monitoringDashboard', ['selectedDashboard', 'getMetricStates']),
shouldShowVariablesSection() { shouldShowVariablesSection() {
return Object.keys(this.variables).length > 0; return Boolean(this.variables.length);
}, },
shouldShowLinksSection() { shouldShowLinksSection() {
return Object.keys(this.links).length > 0; return Object.keys(this.links).length > 0;
......
...@@ -34,7 +34,7 @@ export default { ...@@ -34,7 +34,7 @@ export default {
}, },
methods: { methods: {
onUpdate(value) { onUpdate(value) {
this.$emit('onUpdate', this.name, value); this.$emit('input', value);
}, },
}, },
}; };
......
...@@ -22,7 +22,7 @@ export default { ...@@ -22,7 +22,7 @@ export default {
}, },
methods: { methods: {
onUpdate(event) { onUpdate(event) {
this.$emit('onUpdate', this.name, event.target.value); this.$emit('input', event.target.value);
}, },
}, },
}; };
......
...@@ -16,10 +16,9 @@ export default { ...@@ -16,10 +16,9 @@ export default {
methods: { methods: {
...mapActions('monitoringDashboard', ['updateVariablesAndFetchData']), ...mapActions('monitoringDashboard', ['updateVariablesAndFetchData']),
refreshDashboard(variable, value) { refreshDashboard(variable, value) {
if (this.variables[variable].value !== value) { if (variable.value !== value) {
const changedVariable = { key: variable, value }; this.updateVariablesAndFetchData({ name: variable.name, value });
// update the Vuex store // update the Vuex store
this.updateVariablesAndFetchData(changedVariable);
// the below calls can ideally be moved out of the // the below calls can ideally be moved out of the
// component and into the actions and let the // component and into the actions and let the
// mutation respond directly. // mutation respond directly.
...@@ -39,15 +38,15 @@ export default { ...@@ -39,15 +38,15 @@ export default {
</script> </script>
<template> <template>
<div ref="variablesSection" class="d-sm-flex flex-sm-wrap pt-2 pr-1 pb-0 pl-2 variables-section"> <div ref="variablesSection" class="d-sm-flex flex-sm-wrap pt-2 pr-1 pb-0 pl-2 variables-section">
<div v-for="(variable, key) in variables" :key="key" class="mb-1 pr-2 d-flex d-sm-block"> <div v-for="variable in variables" :key="variable.name" class="mb-1 pr-2 d-flex d-sm-block">
<component <component
:is="variableField(variable.type)" :is="variableField(variable.type)"
class="mb-0 flex-grow-1" class="mb-0 flex-grow-1"
:label="variable.label" :label="variable.label"
:value="variable.value" :value="variable.value"
:name="key" :name="variable.name"
:options="variable.options" :options="variable.options"
@onUpdate="refreshDashboard" @input="refreshDashboard(variable, $event)"
/> />
</div> </div>
</div> </div>
......
...@@ -77,10 +77,6 @@ export const setTimeRange = ({ commit }, timeRange) => { ...@@ -77,10 +77,6 @@ export const setTimeRange = ({ commit }, timeRange) => {
commit(types.SET_TIME_RANGE, timeRange); commit(types.SET_TIME_RANGE, timeRange);
}; };
export const setVariables = ({ commit }, variables) => {
commit(types.SET_VARIABLES, variables);
};
export const filterEnvironments = ({ commit, dispatch }, searchTerm) => { export const filterEnvironments = ({ commit, dispatch }, searchTerm) => {
commit(types.SET_ENVIRONMENTS_FILTER, searchTerm); commit(types.SET_ENVIRONMENTS_FILTER, searchTerm);
dispatch('fetchEnvironmentsData'); dispatch('fetchEnvironmentsData');
...@@ -235,7 +231,7 @@ export const fetchPrometheusMetric = ( ...@@ -235,7 +231,7 @@ export const fetchPrometheusMetric = (
queryParams.step = metric.step; queryParams.step = metric.step;
} }
if (Object.keys(state.variables).length > 0) { if (state.variables.length > 0) {
queryParams = { queryParams = {
...queryParams, ...queryParams,
...getters.getCustomVariablesParams, ...getters.getCustomVariablesParams,
...@@ -480,7 +476,7 @@ export const fetchVariableMetricLabelValues = ({ state, commit }, { defaultQuery ...@@ -480,7 +476,7 @@ export const fetchVariableMetricLabelValues = ({ state, commit }, { defaultQuery
const { start_time, end_time } = defaultQueryParams; const { start_time, end_time } = defaultQueryParams;
const optionsRequests = []; const optionsRequests = [];
Object.entries(state.variables).forEach(([key, variable]) => { state.variables.forEach(variable => {
if (variable.type === VARIABLE_TYPES.metric_label_values) { if (variable.type === VARIABLE_TYPES.metric_label_values) {
const { prometheusEndpointPath, label } = variable.options; const { prometheusEndpointPath, label } = variable.options;
...@@ -496,7 +492,7 @@ export const fetchVariableMetricLabelValues = ({ state, commit }, { defaultQuery ...@@ -496,7 +492,7 @@ export const fetchVariableMetricLabelValues = ({ state, commit }, { defaultQuery
.catch(() => { .catch(() => {
createFlash( createFlash(
sprintf(s__('Metrics|There was an error getting options for variable "%{name}".'), { sprintf(s__('Metrics|There was an error getting options for variable "%{name}".'), {
name: key, name: variable.name,
}), }),
); );
}); });
......
...@@ -133,8 +133,8 @@ export const linksWithMetadata = state => { ...@@ -133,8 +133,8 @@ export const linksWithMetadata = state => {
}; };
/** /**
* Maps an variables object to an array along with stripping * Maps a variables array to an object for replacement in
* the variable prefix. * prometheus queries.
* *
* This method outputs an object in the below format * This method outputs an object in the below format
* *
...@@ -147,14 +147,17 @@ export const linksWithMetadata = state => { ...@@ -147,14 +147,17 @@ export const linksWithMetadata = state => {
* user-defined variables coming through the URL and differentiate * user-defined variables coming through the URL and differentiate
* from other variables used for Prometheus API endpoint. * from other variables used for Prometheus API endpoint.
* *
* @param {Object} variables - Custom variables provided by the user * @param {Object} state - State containing variables provided by the user
* @returns {Array} The custom variables array to be send to the API * @returns {Array} The custom variables object to be send to the API
* in the format of {variables[key1]=value1, variables[key2]=value2} * in the format of {variables[key1]=value1, variables[key2]=value2}
*/ */
export const getCustomVariablesParams = state => export const getCustomVariablesParams = state =>
Object.keys(state.variables).reduce((acc, variable) => { state.variables.reduce((acc, variable) => {
acc[addPrefixToCustomVariableParams(variable)] = state.variables[variable]?.value; const { name, value } = variable;
if (value !== null) {
acc[addPrefixToCustomVariableParams(name)] = value;
}
return acc; return acc;
}, {}); }, {});
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
export const REQUEST_METRICS_DASHBOARD = 'REQUEST_METRICS_DASHBOARD'; export const REQUEST_METRICS_DASHBOARD = 'REQUEST_METRICS_DASHBOARD';
export const RECEIVE_METRICS_DASHBOARD_SUCCESS = 'RECEIVE_METRICS_DASHBOARD_SUCCESS'; export const RECEIVE_METRICS_DASHBOARD_SUCCESS = 'RECEIVE_METRICS_DASHBOARD_SUCCESS';
export const RECEIVE_METRICS_DASHBOARD_FAILURE = 'RECEIVE_METRICS_DASHBOARD_FAILURE'; export const RECEIVE_METRICS_DASHBOARD_FAILURE = 'RECEIVE_METRICS_DASHBOARD_FAILURE';
export const SET_VARIABLES = 'SET_VARIABLES';
export const UPDATE_VARIABLE_VALUE = 'UPDATE_VARIABLE_VALUE'; export const UPDATE_VARIABLE_VALUE = 'UPDATE_VARIABLE_VALUE';
export const UPDATE_VARIABLE_METRIC_LABEL_VALUES = 'UPDATE_VARIABLE_METRIC_LABEL_VALUES'; export const UPDATE_VARIABLE_METRIC_LABEL_VALUES = 'UPDATE_VARIABLE_METRIC_LABEL_VALUES';
......
...@@ -203,14 +203,13 @@ export default { ...@@ -203,14 +203,13 @@ export default {
state.expandedPanel.group = group; state.expandedPanel.group = group;
state.expandedPanel.panel = panel; state.expandedPanel.panel = panel;
}, },
[types.SET_VARIABLES](state, variables) { [types.UPDATE_VARIABLE_VALUE](state, { name, value }) {
state.variables = variables; const variable = state.variables.find(v => v.name === name);
}, if (variable) {
[types.UPDATE_VARIABLE_VALUE](state, { key, value }) { Object.assign(variable, {
Object.assign(state.variables[key], {
...state.variables[key],
value, value,
}); });
}
}, },
[types.UPDATE_VARIABLE_METRIC_LABEL_VALUES](state, { variable, label, data = [] }) { [types.UPDATE_VARIABLE_METRIC_LABEL_VALUES](state, { variable, label, data = [] }) {
const values = optionsFromSeriesData({ label, data }); const values = optionsFromSeriesData({ label, data });
......
...@@ -47,7 +47,7 @@ export default () => ({ ...@@ -47,7 +47,7 @@ export default () => ({
* User-defined custom variables are passed * User-defined custom variables are passed
* via the dashboard yml file. * via the dashboard yml file.
*/ */
variables: {}, variables: [],
/** /**
* User-defined custom links are passed * User-defined custom links are passed
* via the dashboard yml file. * via the dashboard yml file.
......
...@@ -289,7 +289,7 @@ export const mapToDashboardViewModel = ({ ...@@ -289,7 +289,7 @@ export const mapToDashboardViewModel = ({
}) => { }) => {
return { return {
dashboard, dashboard,
variables: mergeURLVariables(parseTemplatingVariables(templating)), variables: mergeURLVariables(parseTemplatingVariables(templating.variables)),
links: links.map(mapLinksToViewModel), links: links.map(mapLinksToViewModel),
panelGroups: panel_groups.map(mapToPanelGroupViewModel), panelGroups: panel_groups.map(mapToPanelGroupViewModel),
}; };
...@@ -453,10 +453,10 @@ export const normalizeQueryResponseData = data => { ...@@ -453,10 +453,10 @@ export const normalizeQueryResponseData = data => {
* *
* This is currently only used by getters/getCustomVariablesParams * This is currently only used by getters/getCustomVariablesParams
* *
* @param {String} key Variable key that needs to be prefixed * @param {String} name Variable key that needs to be prefixed
* @returns {String} * @returns {String}
*/ */
export const addPrefixToCustomVariableParams = key => `variables[${key}]`; export const addPrefixToCustomVariableParams = name => `variables[${name}]`;
/** /**
* Normalize custom dashboard paths. This method helps support * Normalize custom dashboard paths. This method helps support
......
...@@ -46,7 +46,7 @@ const textAdvancedVariableParser = advTextVar => ({ ...@@ -46,7 +46,7 @@ const textAdvancedVariableParser = advTextVar => ({
* @param {Object} custom variable option * @param {Object} custom variable option
* @returns {Object} normalized custom variable options * @returns {Object} normalized custom variable options
*/ */
const normalizeVariableValues = ({ default: defaultOpt = false, text, value }) => ({ const normalizeVariableValues = ({ default: defaultOpt = false, text, value = null }) => ({
default: defaultOpt, default: defaultOpt,
text: text || value, text: text || value,
value, value,
...@@ -68,10 +68,10 @@ const customAdvancedVariableParser = advVariable => { ...@@ -68,10 +68,10 @@ const customAdvancedVariableParser = advVariable => {
return { return {
type: VARIABLE_TYPES.custom, type: VARIABLE_TYPES.custom,
label: advVariable.label, label: advVariable.label,
value: defaultValue?.value,
options: { options: {
values, values,
}, },
value: defaultValue?.value || null,
}; };
}; };
...@@ -100,27 +100,24 @@ const customSimpleVariableParser = simpleVar => { ...@@ -100,27 +100,24 @@ const customSimpleVariableParser = simpleVar => {
const values = (simpleVar || []).map(parseSimpleCustomValues); const values = (simpleVar || []).map(parseSimpleCustomValues);
return { return {
type: VARIABLE_TYPES.custom, type: VARIABLE_TYPES.custom,
value: values[0].value,
label: null, label: null,
value: values[0].value || null,
options: { options: {
values: values.map(normalizeVariableValues), values: values.map(normalizeVariableValues),
}, },
}; };
}; };
const metricLabelValuesVariableParser = variable => { const metricLabelValuesVariableParser = ({ label, options = {} }) => ({
const { label, options = {} } = variable;
return {
type: VARIABLE_TYPES.metric_label_values, type: VARIABLE_TYPES.metric_label_values,
value: null,
label, label,
value: null,
options: { options: {
prometheusEndpointPath: options.prometheus_endpoint_path || '', prometheusEndpointPath: options.prometheus_endpoint_path || '',
label: options.label || null, label: options.label || null,
values: [], // values are initially empty values: [], // values are initially empty
}, },
}; });
};
/** /**
* Utility method to determine if a custom variable is * Utility method to determine if a custom variable is
...@@ -161,29 +158,26 @@ const getVariableParser = variable => { ...@@ -161,29 +158,26 @@ const getVariableParser = variable => {
* for the user to edit. The values from input elements are relayed to * for the user to edit. The values from input elements are relayed to
* backend and eventually Prometheus API. * backend and eventually Prometheus API.
* *
* This method currently is not used anywhere. Once the issue * @param {Object} templating variables from the dashboard yml file
* https://gitlab.com/gitlab-org/gitlab/-/issues/214536 is completed, * @returns {array} An array of variables to display as inputs
* 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
*/ */
export const parseTemplatingVariables = ({ variables = {} } = {}) => export const parseTemplatingVariables = (ymlVariables = {}) =>
Object.entries(variables).reduce((acc, [key, variable]) => { Object.entries(ymlVariables).reduce((acc, [name, ymlVariable]) => {
// get the parser // get the parser
const parser = getVariableParser(variable); const parser = getVariableParser(ymlVariable);
// parse the variable // parse the variable
const parsedVar = parser(variable); const variable = parser(ymlVariable);
// for simple custom variable label is null and it should be // for simple custom variable label is null and it should be
// replace with key instead // replace with key instead
if (parsedVar) { if (variable) {
acc[key] = { acc.push({
...parsedVar, ...variable,
label: parsedVar.label || key, name,
}; label: variable.label || name,
});
} }
return acc; return acc;
}, {}); }, []);
/** /**
* Custom variables are defined in the dashboard yml file * Custom variables are defined in the dashboard yml file
...@@ -201,23 +195,18 @@ export const parseTemplatingVariables = ({ variables = {} } = {}) => ...@@ -201,23 +195,18 @@ export const parseTemplatingVariables = ({ variables = {} } = {}) =>
* This method can be improved further. See the below issue * This method can be improved further. See the below issue
* https://gitlab.com/gitlab-org/gitlab/-/issues/217713 * 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} * @returns {Object}
*/ */
export const mergeURLVariables = (varsFromYML = {}) => { export const mergeURLVariables = (parsedYmlVariables = []) => {
const varsFromURL = templatingVariablesFromUrl(); const varsFromURL = templatingVariablesFromUrl();
const variables = {}; parsedYmlVariables.forEach(variable => {
Object.keys(varsFromYML).forEach(key => { const { name } = variable;
if (Object.prototype.hasOwnProperty.call(varsFromURL, key)) { if (Object.prototype.hasOwnProperty.call(varsFromURL, name)) {
variables[key] = { Object.assign(variable, { value: varsFromURL[name] });
...varsFromYML[key],
value: varsFromURL[key],
};
} else {
variables[key] = varsFromYML[key];
} }
}); });
return variables; return parsedYmlVariables;
}; };
/** /**
......
...@@ -201,8 +201,10 @@ export const removePrefixFromLabel = label => ...@@ -201,8 +201,10 @@ export const removePrefixFromLabel = label =>
* @returns {Object} * @returns {Object}
*/ */
export const convertVariablesForURL = variables => export const convertVariablesForURL = variables =>
Object.keys(variables || {}).reduce((acc, key) => { variables.reduce((acc, { name, value }) => {
acc[addPrefixToLabel(key)] = variables[key]?.value; if (value !== null) {
acc[addPrefixToLabel(name)] = value;
}
return acc; return acc;
}, {}); }, {});
......
...@@ -26,10 +26,9 @@ import { ...@@ -26,10 +26,9 @@ import {
setMetricResult, setMetricResult,
setupStoreWithData, setupStoreWithData,
setupStoreWithDataForPanelCount, setupStoreWithDataForPanelCount,
setupStoreWithVariable,
setupStoreWithLinks, setupStoreWithLinks,
} from '../store_utils'; } from '../store_utils';
import { environmentData, dashboardGitResponse } from '../mock_data'; import { environmentData, dashboardGitResponse, storeVariables } from '../mock_data';
import { import {
metricsDashboardViewModel, metricsDashboardViewModel,
metricsDashboardPanelCount, metricsDashboardPanelCount,
...@@ -604,8 +603,7 @@ describe('Dashboard', () => { ...@@ -604,8 +603,7 @@ describe('Dashboard', () => {
beforeEach(() => { beforeEach(() => {
createShallowWrapper({ hasMetrics: true }); createShallowWrapper({ hasMetrics: true });
setupStoreWithData(store); setupStoreWithData(store);
setupStoreWithVariable(store); store.state.monitoringDashboard.variables = storeVariables;
return wrapper.vm.$nextTick(); return wrapper.vm.$nextTick();
}); });
......
...@@ -59,7 +59,7 @@ describe('Custom variable component', () => { ...@@ -59,7 +59,7 @@ describe('Custom variable component', () => {
.vm.$emit('click'); .vm.$emit('click');
return wrapper.vm.$nextTick(() => { return wrapper.vm.$nextTick(() => {
expect(wrapper.vm.$emit).toHaveBeenCalledWith('onUpdate', 'env', 'canary'); expect(wrapper.vm.$emit).toHaveBeenCalledWith('input', 'canary');
}); });
}); });
}); });
...@@ -40,7 +40,7 @@ describe('Text variable component', () => { ...@@ -40,7 +40,7 @@ describe('Text variable component', () => {
findInput().trigger('keyup.enter'); findInput().trigger('keyup.enter');
return wrapper.vm.$nextTick(() => { return wrapper.vm.$nextTick(() => {
expect(wrapper.vm.$emit).toHaveBeenCalledWith('onUpdate', 'pod', 'prod-pod'); expect(wrapper.vm.$emit).toHaveBeenCalledWith('input', 'prod-pod');
}); });
}); });
...@@ -53,7 +53,7 @@ describe('Text variable component', () => { ...@@ -53,7 +53,7 @@ describe('Text variable component', () => {
findInput().trigger('blur'); findInput().trigger('blur');
return wrapper.vm.$nextTick(() => { return wrapper.vm.$nextTick(() => {
expect(wrapper.vm.$emit).toHaveBeenCalledWith('onUpdate', 'pod', 'canary-pod'); expect(wrapper.vm.$emit).toHaveBeenCalledWith('input', 'canary-pod');
}); });
}); });
}); });
...@@ -6,8 +6,7 @@ import TextField from '~/monitoring/components/variables/text_field.vue'; ...@@ -6,8 +6,7 @@ import TextField from '~/monitoring/components/variables/text_field.vue';
import { updateHistory, mergeUrlParams } from '~/lib/utils/url_utility'; import { updateHistory, mergeUrlParams } from '~/lib/utils/url_utility';
import { createStore } from '~/monitoring/stores'; import { createStore } from '~/monitoring/stores';
import { convertVariablesForURL } from '~/monitoring/utils'; import { convertVariablesForURL } from '~/monitoring/utils';
import * as types from '~/monitoring/stores/mutation_types'; import { storeVariables } from '../mock_data';
import { mockTemplatingDataResponses } from '../mock_data';
jest.mock('~/lib/utils/url_utility', () => ({ jest.mock('~/lib/utils/url_utility', () => ({
updateHistory: jest.fn(), updateHistory: jest.fn(),
...@@ -17,12 +16,6 @@ jest.mock('~/lib/utils/url_utility', () => ({ ...@@ -17,12 +16,6 @@ jest.mock('~/lib/utils/url_utility', () => ({
describe('Metrics dashboard/variables section component', () => { describe('Metrics dashboard/variables section component', () => {
let store; let store;
let wrapper; let wrapper;
const sampleVariables = {
label1: mockTemplatingDataResponses.simpleText.simpleText,
label2: mockTemplatingDataResponses.advText.advText,
label3: mockTemplatingDataResponses.simpleCustom.simpleCustom,
label4: mockTemplatingDataResponses.metricLabelValues.simple,
};
const createShallowWrapper = () => { const createShallowWrapper = () => {
wrapper = shallowMount(VariablesSection, { wrapper = shallowMount(VariablesSection, {
...@@ -48,22 +41,23 @@ describe('Metrics dashboard/variables section component', () => { ...@@ -48,22 +41,23 @@ describe('Metrics dashboard/variables section component', () => {
describe('when variables are set', () => { describe('when variables are set', () => {
beforeEach(() => { beforeEach(() => {
store.state.monitoringDashboard.variables = storeVariables;
createShallowWrapper(); createShallowWrapper();
store.commit(`monitoringDashboard/${types.SET_VARIABLES}`, sampleVariables);
return wrapper.vm.$nextTick; return wrapper.vm.$nextTick;
}); });
it('shows the variables section', () => { it('shows the variables section', () => {
const allInputs = findTextInputs().length + findCustomInputs().length; const allInputs = findTextInputs().length + findCustomInputs().length;
expect(allInputs).toBe(Object.keys(sampleVariables).length); expect(allInputs).toBe(storeVariables.length);
}); });
it('shows the right custom variable inputs', () => { it('shows the right custom variable inputs', () => {
const customInputs = findCustomInputs(); const customInputs = findCustomInputs();
expect(customInputs.at(0).props('name')).toBe('label3'); expect(customInputs.at(0).props('name')).toBe('customSimple');
expect(customInputs.at(1).props('name')).toBe('label4'); expect(customInputs.at(1).props('name')).toBe('customAdvanced');
}); });
}); });
...@@ -77,7 +71,7 @@ describe('Metrics dashboard/variables section component', () => { ...@@ -77,7 +71,7 @@ describe('Metrics dashboard/variables section component', () => {
namespaced: true, namespaced: true,
state: { state: {
showEmptyState: false, showEmptyState: false,
variables: sampleVariables, variables: storeVariables,
}, },
actions: { actions: {
updateVariablesAndFetchData, updateVariablesAndFetchData,
...@@ -92,12 +86,12 @@ describe('Metrics dashboard/variables section component', () => { ...@@ -92,12 +86,12 @@ describe('Metrics dashboard/variables section component', () => {
it('merges the url params and refreshes the dashboard when a text-based variables inputs are updated', () => { it('merges the url params and refreshes the dashboard when a text-based variables inputs are updated', () => {
const firstInput = findTextInputs().at(0); const firstInput = findTextInputs().at(0);
firstInput.vm.$emit('onUpdate', 'label1', 'test'); firstInput.vm.$emit('input', 'test');
return wrapper.vm.$nextTick(() => { return wrapper.vm.$nextTick(() => {
expect(updateVariablesAndFetchData).toHaveBeenCalled(); expect(updateVariablesAndFetchData).toHaveBeenCalled();
expect(mergeUrlParams).toHaveBeenCalledWith( expect(mergeUrlParams).toHaveBeenCalledWith(
convertVariablesForURL(sampleVariables), convertVariablesForURL(storeVariables),
window.location.href, window.location.href,
); );
expect(updateHistory).toHaveBeenCalled(); expect(updateHistory).toHaveBeenCalled();
...@@ -107,12 +101,12 @@ describe('Metrics dashboard/variables section component', () => { ...@@ -107,12 +101,12 @@ describe('Metrics dashboard/variables section component', () => {
it('merges the url params and refreshes the dashboard when a custom-based variables inputs are updated', () => { it('merges the url params and refreshes the dashboard when a custom-based variables inputs are updated', () => {
const firstInput = findCustomInputs().at(0); const firstInput = findCustomInputs().at(0);
firstInput.vm.$emit('onUpdate', 'label1', 'test'); firstInput.vm.$emit('input', 'test');
return wrapper.vm.$nextTick(() => { return wrapper.vm.$nextTick(() => {
expect(updateVariablesAndFetchData).toHaveBeenCalled(); expect(updateVariablesAndFetchData).toHaveBeenCalled();
expect(mergeUrlParams).toHaveBeenCalledWith( expect(mergeUrlParams).toHaveBeenCalledWith(
convertVariablesForURL(sampleVariables), convertVariablesForURL(storeVariables),
window.location.href, window.location.href,
); );
expect(updateHistory).toHaveBeenCalled(); expect(updateHistory).toHaveBeenCalled();
...@@ -122,7 +116,7 @@ describe('Metrics dashboard/variables section component', () => { ...@@ -122,7 +116,7 @@ describe('Metrics dashboard/variables section component', () => {
it('does not merge the url params and refreshes the dashboard if the value entered is not different that is what currently stored', () => { it('does not merge the url params and refreshes the dashboard if the value entered is not different that is what currently stored', () => {
const firstInput = findTextInputs().at(0); const firstInput = findTextInputs().at(0);
firstInput.vm.$emit('onUpdate', 'label1', 'Simple text'); firstInput.vm.$emit('input', 'My default value');
expect(updateVariablesAndFetchData).not.toHaveBeenCalled(); expect(updateVariablesAndFetchData).not.toHaveBeenCalled();
expect(mergeUrlParams).not.toHaveBeenCalled(); expect(mergeUrlParams).not.toHaveBeenCalled();
......
...@@ -627,21 +627,20 @@ export const mockLinks = [ ...@@ -627,21 +627,20 @@ export const mockLinks = [
}, },
]; ];
const templatingVariableTypes = { export const templatingVariablesExamples = {
text: { text: {
simple: 'Simple text', textSimple: 'My default value',
advanced: { textAdvanced: {
label: 'Variable 4', label: 'Advanced text variable',
type: 'text', type: 'text',
options: { options: {
default_value: 'default', default_value: 'A default value',
}, },
}, },
}, },
custom: { custom: {
simple: ['value1', 'value2', 'value3'], customSimple: ['value1', 'value2', 'value3'],
advanced: { customAdvanced: {
normal: {
label: 'Advanced Var', label: 'Advanced Var',
type: 'custom', type: 'custom',
options: { options: {
...@@ -655,11 +654,11 @@ const templatingVariableTypes = { ...@@ -655,11 +654,11 @@ const templatingVariableTypes = {
], ],
}, },
}, },
withoutOpts: { customAdvancedWithoutOpts: {
type: 'custom', type: 'custom',
options: {}, options: {},
}, },
withoutLabel: { customAdvancedWithoutLabel: {
type: 'custom', type: 'custom',
options: { options: {
values: [ values: [
...@@ -672,7 +671,7 @@ const templatingVariableTypes = { ...@@ -672,7 +671,7 @@ const templatingVariableTypes = {
], ],
}, },
}, },
withoutType: { customAdvancedWithoutType: {
label: 'Variable 2', label: 'Variable 2',
options: { options: {
values: [ values: [
...@@ -685,7 +684,7 @@ const templatingVariableTypes = { ...@@ -685,7 +684,7 @@ const templatingVariableTypes = {
], ],
}, },
}, },
withoutOptText: { customAdvancedWithoutOptText: {
label: 'Options without text', label: 'Options without text',
type: 'custom', type: 'custom',
options: { options: {
...@@ -699,9 +698,8 @@ const templatingVariableTypes = { ...@@ -699,9 +698,8 @@ const templatingVariableTypes = {
}, },
}, },
}, },
},
metricLabelValues: { metricLabelValues: {
simple: { metricLabelValuesSimple: {
label: 'Metric Label Values', label: 'Metric Label Values',
type: 'metric_label_values', type: 'metric_label_values',
options: { options: {
...@@ -713,205 +711,92 @@ const templatingVariableTypes = { ...@@ -713,205 +711,92 @@ const templatingVariableTypes = {
}, },
}; };
const generateMockTemplatingData = data => { export const storeTextVariables = [
const vars = data {
? {
variables: {
...data,
},
}
: {};
return {
dashboard: {
templating: vars,
},
};
};
const responseForSimpleTextVariable = {
simpleText: {
label: 'simpleText',
type: 'text', type: 'text',
value: 'Simple text', name: 'textSimple',
label: 'textSimple',
value: 'My default value',
}, },
}; {
const responseForAdvTextVariable = {
advText: {
label: 'Variable 4',
type: 'text', type: 'text',
value: 'default', name: 'textAdvanced',
label: 'Advanced text variable',
value: 'A default value',
}, },
}; ];
const responseForSimpleCustomVariable = { export const storeCustomVariables = [
simpleCustom: { {
label: 'simpleCustom', type: 'custom',
value: 'value1', name: 'customSimple',
label: 'customSimple',
options: { options: {
values: [ values: [
{ { default: false, text: 'value1', value: 'value1' },
default: false, { default: false, text: 'value2', value: 'value2' },
text: 'value1', { default: false, text: 'value3', value: 'value3' },
],
},
value: 'value1', value: 'value1',
}, },
{ {
default: false, type: 'custom',
text: 'value2', name: 'customAdvanced',
label: 'Advanced Var',
options: {
values: [
{ default: false, text: 'Var 1 Option 1', value: 'value1' },
{ default: true, text: 'Var 1 Option 2', value: 'value2' },
],
},
value: 'value2', value: 'value2',
}, },
{ {
default: false,
text: 'value3',
value: 'value3',
},
],
},
type: 'custom', type: 'custom',
name: 'customAdvancedWithoutOpts',
label: 'customAdvancedWithoutOpts',
options: { values: [] },
value: null,
}, },
}; {
const responseForAdvancedCustomVariableWithoutOptions = {
advCustomWithoutOpts: {
label: 'advCustomWithoutOpts',
options: {
values: [],
},
type: 'custom', type: 'custom',
}, name: 'customAdvancedWithoutLabel',
}; label: 'customAdvancedWithoutLabel',
const responseForAdvancedCustomVariableWithoutLabel = {
advCustomWithoutLabel: {
label: 'advCustomWithoutLabel',
value: 'value2', value: 'value2',
options: { options: {
values: [ values: [
{ { default: false, text: 'Var 1 Option 1', value: 'value1' },
default: false, { default: true, text: 'Var 1 Option 2', value: 'value2' },
text: 'Var 1 Option 1',
value: 'value1',
},
{
default: true,
text: 'Var 1 Option 2',
value: 'value2',
},
], ],
}, },
type: 'custom',
}, },
}; {
type: 'custom',
const responseForAdvancedCustomVariableWithoutOptText = { name: 'customAdvancedWithoutOptText',
advCustomWithoutOptText: {
label: 'Options without text', label: 'Options without text',
value: 'value2',
options: { options: {
values: [ values: [
{ { default: false, text: 'value1', value: 'value1' },
default: false, { default: true, text: 'value2', value: 'value2' },
text: 'value1',
value: 'value1',
},
{
default: true,
text: 'value2',
value: 'value2',
},
], ],
}, },
type: 'custom', value: 'value2',
}, },
}; ];
const responseForMetricLabelValues = { export const storeMetricLabelValuesVariables = [
simple: { {
label: 'Metric Label Values',
type: 'metric_label_values', type: 'metric_label_values',
name: 'metricLabelValuesSimple',
label: 'Metric Label Values',
options: { prometheusEndpointPath: '/series', label: 'backend', values: [] },
value: null, value: null,
options: {
prometheusEndpointPath: '/series',
label: 'backend',
values: [],
},
},
};
const responseForAdvancedCustomVariable = {
...responseForSimpleCustomVariable,
advCustomNormal: {
label: 'Advanced Var',
value: 'value2',
options: {
values: [
{
default: false,
text: 'Var 1 Option 1',
value: 'value1',
},
{
default: true,
text: 'Var 1 Option 2',
value: 'value2',
}, },
], ];
},
type: 'custom',
},
};
const responsesForAllVariableTypes = {
...responseForSimpleTextVariable,
...responseForAdvTextVariable,
...responseForSimpleCustomVariable,
...responseForAdvancedCustomVariable,
};
export const mockTemplatingData = {
emptyTemplatingProp: generateMockTemplatingData(),
emptyVariablesProp: generateMockTemplatingData({}),
simpleText: generateMockTemplatingData({ simpleText: templatingVariableTypes.text.simple }),
advText: generateMockTemplatingData({ advText: templatingVariableTypes.text.advanced }),
simpleCustom: generateMockTemplatingData({ simpleCustom: templatingVariableTypes.custom.simple }),
advCustomWithoutOpts: generateMockTemplatingData({
advCustomWithoutOpts: templatingVariableTypes.custom.advanced.withoutOpts,
}),
advCustomWithoutType: generateMockTemplatingData({
advCustomWithoutType: templatingVariableTypes.custom.advanced.withoutType,
}),
advCustomWithoutLabel: generateMockTemplatingData({
advCustomWithoutLabel: templatingVariableTypes.custom.advanced.withoutLabel,
}),
advCustomWithoutOptText: generateMockTemplatingData({
advCustomWithoutOptText: templatingVariableTypes.custom.advanced.withoutOptText,
}),
simpleAndAdv: generateMockTemplatingData({
simpleCustom: templatingVariableTypes.custom.simple,
advCustomNormal: templatingVariableTypes.custom.advanced.normal,
}),
metricLabelValues: generateMockTemplatingData({
simple: templatingVariableTypes.metricLabelValues.simple,
}),
allVariableTypes: generateMockTemplatingData({
simpleText: templatingVariableTypes.text.simple,
advText: templatingVariableTypes.text.advanced,
simpleCustom: templatingVariableTypes.custom.simple,
advCustomNormal: templatingVariableTypes.custom.advanced.normal,
}),
};
export const mockTemplatingDataResponses = { export const storeVariables = [
emptyTemplatingProp: {}, ...storeTextVariables,
emptyVariablesProp: {}, ...storeCustomVariables,
simpleText: responseForSimpleTextVariable, ...storeMetricLabelValuesVariables,
advText: responseForAdvTextVariable, ];
simpleCustom: responseForSimpleCustomVariable,
advCustomWithoutOpts: responseForAdvancedCustomVariableWithoutOptions,
advCustomWithoutType: {},
advCustomWithoutLabel: responseForAdvancedCustomVariableWithoutLabel,
advCustomWithoutOptText: responseForAdvancedCustomVariableWithoutOptText,
simpleAndAdv: responseForAdvancedCustomVariable,
allVariableTypes: responsesForAllVariableTypes,
metricLabelValues: responseForMetricLabelValues,
};
...@@ -44,7 +44,6 @@ import { ...@@ -44,7 +44,6 @@ import {
deploymentData, deploymentData,
environmentData, environmentData,
annotationsData, annotationsData,
mockTemplatingData,
dashboardGitResponse, dashboardGitResponse,
mockDashboardsErrorResponse, mockDashboardsErrorResponse,
} from '../mock_data'; } from '../mock_data';
...@@ -305,32 +304,6 @@ describe('Monitoring store actions', () => { ...@@ -305,32 +304,6 @@ describe('Monitoring store actions', () => {
expect(dispatch).toHaveBeenCalledWith('fetchDashboardData'); expect(dispatch).toHaveBeenCalledWith('fetchDashboardData');
}); });
it('stores templating variables', () => {
const response = {
...metricsDashboardResponse.dashboard,
...mockTemplatingData.allVariableTypes.dashboard,
};
receiveMetricsDashboardSuccess(
{ state, commit, dispatch },
{
response: {
...metricsDashboardResponse,
dashboard: {
...metricsDashboardResponse.dashboard,
...mockTemplatingData.allVariableTypes.dashboard,
},
},
},
);
expect(commit).toHaveBeenCalledWith(
types.RECEIVE_METRICS_DASHBOARD_SUCCESS,
response,
);
});
it('sets the dashboards loaded from the repository', () => { it('sets the dashboards loaded from the repository', () => {
const params = {}; const params = {};
const response = metricsDashboardResponse; const response = metricsDashboardResponse;
...@@ -1144,11 +1117,13 @@ describe('Monitoring store actions', () => { ...@@ -1144,11 +1117,13 @@ describe('Monitoring store actions', () => {
describe('fetchVariableMetricLabelValues', () => { describe('fetchVariableMetricLabelValues', () => {
const variable = { const variable = {
type: 'metric_label_values', type: 'metric_label_values',
name: 'label1',
options: { options: {
prometheusEndpointPath: '/series', prometheusEndpointPath: '/series?match[]=metric_name',
label: 'job', label: 'job',
}, },
}; };
const defaultQueryParams = { const defaultQueryParams = {
start_time: '2019-08-06T12:40:02.184Z', start_time: '2019-08-06T12:40:02.184Z',
end_time: '2019-08-06T20:40:02.184Z', end_time: '2019-08-06T20:40:02.184Z',
...@@ -1158,9 +1133,7 @@ describe('Monitoring store actions', () => { ...@@ -1158,9 +1133,7 @@ describe('Monitoring store actions', () => {
state = { state = {
...state, ...state,
timeRange: defaultTimeRange, timeRange: defaultTimeRange,
variables: { variables: [variable],
label1: variable,
},
}; };
}); });
...@@ -1176,7 +1149,7 @@ describe('Monitoring store actions', () => { ...@@ -1176,7 +1149,7 @@ describe('Monitoring store actions', () => {
}, },
]; ];
mock.onGet('/series').reply(200, { mock.onGet('/series?match[]=metric_name').reply(200, {
status: 'success', status: 'success',
data, data,
}); });
...@@ -1196,7 +1169,7 @@ describe('Monitoring store actions', () => { ...@@ -1196,7 +1169,7 @@ describe('Monitoring store actions', () => {
}); });
it('should notify the user that dynamic options were not loaded', () => { it('should notify the user that dynamic options were not loaded', () => {
mock.onGet('/series').reply(500); mock.onGet('/series?match[]=metric_name').reply(500);
return testAction(fetchVariableMetricLabelValues, { defaultQueryParams }, state, [], []).then( return testAction(fetchVariableMetricLabelValues, { defaultQueryParams }, state, [], []).then(
() => { () => {
......
...@@ -8,7 +8,7 @@ import { ...@@ -8,7 +8,7 @@ import {
environmentData, environmentData,
metricsResult, metricsResult,
dashboardGitResponse, dashboardGitResponse,
mockTemplatingDataResponses, storeVariables,
mockLinks, mockLinks,
} from '../mock_data'; } from '../mock_data';
import { import {
...@@ -344,19 +344,21 @@ describe('Monitoring store Getters', () => { ...@@ -344,19 +344,21 @@ describe('Monitoring store Getters', () => {
}); });
it('transforms the variables object to an array in the [variable, variable_value] format for all variable types', () => { it('transforms the variables object to an array in the [variable, variable_value] format for all variable types', () => {
mutations[types.SET_VARIABLES](state, mockTemplatingDataResponses.allVariableTypes); state.variables = storeVariables;
const variablesArray = getters.getCustomVariablesParams(state); const variablesArray = getters.getCustomVariablesParams(state);
expect(variablesArray).toEqual({ expect(variablesArray).toEqual({
'variables[advCustomNormal]': 'value2', 'variables[textSimple]': 'My default value',
'variables[advText]': 'default', 'variables[textAdvanced]': 'A default value',
'variables[simpleCustom]': 'value1', 'variables[customSimple]': 'value1',
'variables[simpleText]': 'Simple text', 'variables[customAdvanced]': 'value2',
'variables[customAdvancedWithoutLabel]': 'value2',
'variables[customAdvancedWithoutOptText]': 'value2',
}); });
}); });
it('transforms the variables object to an empty array when no keys are present', () => { it('transforms the variables object to an empty array when no keys are present', () => {
mutations[types.SET_VARIABLES](state, {}); state.variables = [];
const variablesArray = getters.getCustomVariablesParams(state); const variablesArray = getters.getCustomVariablesParams(state);
expect(variablesArray).toEqual({}); expect(variablesArray).toEqual({});
......
...@@ -5,7 +5,7 @@ import * as types from '~/monitoring/stores/mutation_types'; ...@@ -5,7 +5,7 @@ import * as types from '~/monitoring/stores/mutation_types';
import state from '~/monitoring/stores/state'; import state from '~/monitoring/stores/state';
import { metricStates } from '~/monitoring/constants'; import { metricStates } from '~/monitoring/constants';
import { deploymentData, dashboardGitResponse } from '../mock_data'; import { deploymentData, dashboardGitResponse, storeTextVariables } from '../mock_data';
import { metricsDashboardPayload } from '../fixture_data'; import { metricsDashboardPayload } from '../fixture_data';
describe('Monitoring mutations', () => { describe('Monitoring mutations', () => {
...@@ -427,30 +427,12 @@ describe('Monitoring mutations', () => { ...@@ -427,30 +427,12 @@ describe('Monitoring mutations', () => {
}); });
}); });
describe('SET_VARIABLES', () => {
it('stores an empty variables array when no custom variables are given', () => {
mutations[types.SET_VARIABLES](stateCopy, {});
expect(stateCopy.variables).toEqual({});
});
it('stores variables in the key key_value format in the array', () => {
mutations[types.SET_VARIABLES](stateCopy, { pod: 'POD', stage: 'main ops' });
expect(stateCopy.variables).toEqual({ pod: 'POD', stage: 'main ops' });
});
});
describe('UPDATE_VARIABLE_VALUE', () => { describe('UPDATE_VARIABLE_VALUE', () => {
afterEach(() => {
mutations[types.SET_VARIABLES](stateCopy, {});
});
it('updates only the value of the variable in variables', () => { it('updates only the value of the variable in variables', () => {
mutations[types.SET_VARIABLES](stateCopy, { environment: { value: 'prod', type: 'text' } }); stateCopy.variables = storeTextVariables;
mutations[types.UPDATE_VARIABLE_VALUE](stateCopy, { key: 'environment', value: 'new prod' }); mutations[types.UPDATE_VARIABLE_VALUE](stateCopy, { name: 'textSimple', value: 'New Value' });
expect(stateCopy.variables).toEqual({ environment: { value: 'new prod', type: 'text' } }); expect(stateCopy.variables[0].value).toEqual('New Value');
}); });
}); });
......
...@@ -22,7 +22,7 @@ describe('mapToDashboardViewModel', () => { ...@@ -22,7 +22,7 @@ describe('mapToDashboardViewModel', () => {
dashboard: '', dashboard: '',
panelGroups: [], panelGroups: [],
links: [], links: [],
variables: {}, variables: [],
}); });
}); });
...@@ -52,7 +52,7 @@ describe('mapToDashboardViewModel', () => { ...@@ -52,7 +52,7 @@ describe('mapToDashboardViewModel', () => {
expect(mapToDashboardViewModel(response)).toEqual({ expect(mapToDashboardViewModel(response)).toEqual({
dashboard: 'Dashboard Name', dashboard: 'Dashboard Name',
links: [], links: [],
variables: {}, variables: [],
panelGroups: [ panelGroups: [
{ {
group: 'Group 1', group: 'Group 1',
...@@ -424,22 +424,20 @@ describe('mapToDashboardViewModel', () => { ...@@ -424,22 +424,20 @@ describe('mapToDashboardViewModel', () => {
urlUtils.queryToObject.mockReturnValueOnce(); urlUtils.queryToObject.mockReturnValueOnce();
expect(mapToDashboardViewModel(response)).toMatchObject({ expect(mapToDashboardViewModel(response).variables).toEqual([
dashboard: 'Dashboard Name', {
links: [], name: 'pod',
variables: {
pod: {
label: 'pod', label: 'pod',
type: 'text', type: 'text',
value: 'kubernetes', value: 'kubernetes',
}, },
pod_2: { {
name: 'pod_2',
label: 'pod_2', label: 'pod_2',
type: 'text', type: 'text',
value: 'kubernetes-2', value: 'kubernetes-2',
}, },
}, ]);
});
}); });
it('sets variables as-is from yml file if URL has no matching variables', () => { it('sets variables as-is from yml file if URL has no matching variables', () => {
...@@ -458,22 +456,20 @@ describe('mapToDashboardViewModel', () => { ...@@ -458,22 +456,20 @@ describe('mapToDashboardViewModel', () => {
'var-environment': 'POD', 'var-environment': 'POD',
}); });
expect(mapToDashboardViewModel(response)).toMatchObject({ expect(mapToDashboardViewModel(response).variables).toEqual([
dashboard: 'Dashboard Name', {
links: [],
variables: {
pod: {
label: 'pod', label: 'pod',
name: 'pod',
type: 'text', type: 'text',
value: 'kubernetes', value: 'kubernetes',
}, },
pod_2: { {
label: 'pod_2', label: 'pod_2',
name: 'pod_2',
type: 'text', type: 'text',
value: 'kubernetes-2', value: 'kubernetes-2',
}, },
}, ]);
});
}); });
it('merges variables from URL with the ones from yml file', () => { it('merges variables from URL with the ones from yml file', () => {
...@@ -494,22 +490,20 @@ describe('mapToDashboardViewModel', () => { ...@@ -494,22 +490,20 @@ describe('mapToDashboardViewModel', () => {
'var-pod_2': 'POD2', 'var-pod_2': 'POD2',
}); });
expect(mapToDashboardViewModel(response)).toMatchObject({ expect(mapToDashboardViewModel(response).variables).toEqual([
dashboard: 'Dashboard Name', {
links: [],
variables: {
pod: {
label: 'pod', label: 'pod',
name: 'pod',
type: 'text', type: 'text',
value: 'POD1', value: 'POD1',
}, },
pod_2: { {
label: 'pod_2', label: 'pod_2',
name: 'pod_2',
type: 'text', type: 'text',
value: 'POD2', value: 'POD2',
}, },
}, ]);
});
}); });
}); });
}); });
......
...@@ -3,29 +3,31 @@ import { ...@@ -3,29 +3,31 @@ import {
mergeURLVariables, mergeURLVariables,
optionsFromSeriesData, optionsFromSeriesData,
} from '~/monitoring/stores/variable_mapping'; } from '~/monitoring/stores/variable_mapping';
import {
templatingVariablesExamples,
storeTextVariables,
storeCustomVariables,
storeMetricLabelValuesVariables,
} from '../mock_data';
import * as urlUtils from '~/lib/utils/url_utility'; import * as urlUtils from '~/lib/utils/url_utility';
import { mockTemplatingData, mockTemplatingDataResponses } from '../mock_data';
describe('Monitoring variable mapping', () => { describe('Monitoring variable mapping', () => {
describe('parseTemplatingVariables', () => { describe('parseTemplatingVariables', () => {
it.each` it.each`
case | input | expected case | input
${'Returns empty object for no dashboard input'} | ${{}} | ${{}} ${'For undefined templating object'} | ${undefined}
${'Returns empty object for empty dashboard input'} | ${{ dashboard: {} }} | ${{}} ${'For empty templating object'} | ${{}}
${'Returns empty object for empty templating prop'} | ${mockTemplatingData.emptyTemplatingProp} | ${{}} `('$case, returns an empty array', ({ input }) => {
${'Returns empty object for empty variables prop'} | ${mockTemplatingData.emptyVariablesProp} | ${{}} expect(parseTemplatingVariables(input)).toEqual([]);
${'Returns parsed object for simple text variable'} | ${mockTemplatingData.simpleText} | ${mockTemplatingDataResponses.simpleText} });
${'Returns parsed object for advanced text variable'} | ${mockTemplatingData.advText} | ${mockTemplatingDataResponses.advText}
${'Returns parsed object for simple custom variable'} | ${mockTemplatingData.simpleCustom} | ${mockTemplatingDataResponses.simpleCustom} it.each`
${'Returns parsed object for advanced custom variable without options'} | ${mockTemplatingData.advCustomWithoutOpts} | ${mockTemplatingDataResponses.advCustomWithoutOpts} case | input | output
${'Returns parsed object for advanced custom variable for option without text'} | ${mockTemplatingData.advCustomWithoutOptText} | ${mockTemplatingDataResponses.advCustomWithoutOptText} ${'Returns parsed object for text variables'} | ${templatingVariablesExamples.text} | ${storeTextVariables}
${'Returns parsed object for advanced custom variable without type'} | ${mockTemplatingData.advCustomWithoutType} | ${{}} ${'Returns parsed object for custom variables'} | ${templatingVariablesExamples.custom} | ${storeCustomVariables}
${'Returns parsed object for advanced custom variable without label'} | ${mockTemplatingData.advCustomWithoutLabel} | ${mockTemplatingDataResponses.advCustomWithoutLabel} ${'Returns parsed object for metric label value variables'} | ${templatingVariablesExamples.metricLabelValues} | ${storeMetricLabelValuesVariables}
${'Returns parsed object for simple and advanced custom variables'} | ${mockTemplatingData.simpleAndAdv} | ${mockTemplatingDataResponses.simpleAndAdv} `('$case, returns an empty array', ({ input, output }) => {
${'Returns parsed object for metricLabelValues'} | ${mockTemplatingData.metricLabelValues} | ${mockTemplatingDataResponses.metricLabelValues} expect(parseTemplatingVariables(input)).toEqual(output);
${'Returns parsed object for all variable types'} | ${mockTemplatingData.allVariableTypes} | ${mockTemplatingDataResponses.allVariableTypes}
`('$case', ({ input, expected }) => {
expect(parseTemplatingVariables(input?.dashboard?.templating)).toEqual(expected);
}); });
}); });
...@@ -41,7 +43,7 @@ describe('Monitoring variable mapping', () => { ...@@ -41,7 +43,7 @@ describe('Monitoring variable mapping', () => {
it('returns empty object if variables are not defined in yml or URL', () => { it('returns empty object if variables are not defined in yml or URL', () => {
urlUtils.queryToObject.mockReturnValueOnce({}); urlUtils.queryToObject.mockReturnValueOnce({});
expect(mergeURLVariables({})).toEqual({}); expect(mergeURLVariables([])).toEqual([]);
}); });
it('returns empty object if variables are defined in URL but not in yml', () => { it('returns empty object if variables are defined in URL but not in yml', () => {
...@@ -50,18 +52,24 @@ describe('Monitoring variable mapping', () => { ...@@ -50,18 +52,24 @@ describe('Monitoring variable mapping', () => {
'var-instance': 'localhost', 'var-instance': 'localhost',
}); });
expect(mergeURLVariables({})).toEqual({}); expect(mergeURLVariables([])).toEqual([]);
}); });
it('returns yml variables if variables defined in yml but not in the URL', () => { it('returns yml variables if variables defined in yml but not in the URL', () => {
urlUtils.queryToObject.mockReturnValueOnce({}); urlUtils.queryToObject.mockReturnValueOnce({});
const params = { const variables = [
env: 'one', {
instance: 'localhost', name: 'env',
}; value: 'one',
},
{
name: 'instance',
value: 'localhost',
},
];
expect(mergeURLVariables(params)).toEqual(params); expect(mergeURLVariables(variables)).toEqual(variables);
}); });
it('returns yml variables if variables defined in URL do not match with yml variables', () => { it('returns yml variables if variables defined in URL do not match with yml variables', () => {
...@@ -69,13 +77,19 @@ describe('Monitoring variable mapping', () => { ...@@ -69,13 +77,19 @@ describe('Monitoring variable mapping', () => {
'var-env': 'one', 'var-env': 'one',
'var-instance': 'localhost', 'var-instance': 'localhost',
}; };
const ymlParams = { const variables = [
pod: { value: 'one' }, {
service: { value: 'database' }, name: 'env',
}; value: 'one',
},
{
name: 'service',
value: 'database',
},
];
urlUtils.queryToObject.mockReturnValueOnce(urlParams); urlUtils.queryToObject.mockReturnValueOnce(urlParams);
expect(mergeURLVariables(ymlParams)).toEqual(ymlParams); expect(mergeURLVariables(variables)).toEqual(variables);
}); });
it('returns merged yml and URL variables if there is some match', () => { it('returns merged yml and URL variables if there is some match', () => {
...@@ -83,19 +97,29 @@ describe('Monitoring variable mapping', () => { ...@@ -83,19 +97,29 @@ describe('Monitoring variable mapping', () => {
'var-env': 'one', 'var-env': 'one',
'var-instance': 'localhost:8080', 'var-instance': 'localhost:8080',
}; };
const ymlParams = { const variables = [
instance: { value: 'localhost' }, {
service: { value: 'database' }, name: 'instance',
}; value: 'localhost',
},
const merged = { {
instance: { value: 'localhost:8080' }, name: 'service',
service: { value: 'database' }, value: 'database',
}; },
];
urlUtils.queryToObject.mockReturnValueOnce(urlParams); urlUtils.queryToObject.mockReturnValueOnce(urlParams);
expect(mergeURLVariables(ymlParams)).toEqual(merged); expect(mergeURLVariables(variables)).toEqual([
{
name: 'instance',
value: 'localhost:8080',
},
{
name: 'service',
value: 'database',
},
]);
}); });
}); });
......
...@@ -35,12 +35,6 @@ export const setupStoreWithDashboard = store => { ...@@ -35,12 +35,6 @@ export const setupStoreWithDashboard = store => {
); );
}; };
export const setupStoreWithVariable = store => {
store.commit(`monitoringDashboard/${types.SET_VARIABLES}`, {
label1: 'pod',
});
};
export const setupStoreWithLinks = store => { export const setupStoreWithLinks = store => {
store.commit(`monitoringDashboard/${types.RECEIVE_METRICS_DASHBOARD_SUCCESS}`, { store.commit(`monitoringDashboard/${types.RECEIVE_METRICS_DASHBOARD_SUCCESS}`, {
...metricsDashboardPayload, ...metricsDashboardPayload,
......
...@@ -430,13 +430,40 @@ describe('monitoring/utils', () => { ...@@ -430,13 +430,40 @@ describe('monitoring/utils', () => {
describe('convertVariablesForURL', () => { describe('convertVariablesForURL', () => {
it.each` it.each`
input | expected input | expected
${undefined} | ${{}} ${[]} | ${{}}
${null} | ${{}} ${[{ name: 'env', value: 'prod' }]} | ${{ 'var-env': 'prod' }}
${{}} | ${{}} ${[{ name: 'env1', value: 'prod' }, { name: 'env2', value: null }]} | ${{ 'var-env1': 'prod' }}
${{ env: { value: 'prod' } }} | ${{ 'var-env': 'prod' }} ${[{ name: 'var-env', value: 'prod' }]} | ${{ 'var-var-env': 'prod' }}
${{ 'var-env': { value: 'prod' } }} | ${{ 'var-var-env': 'prod' }}
`('convertVariablesForURL returns $expected with input $input', ({ input, expected }) => { `('convertVariablesForURL returns $expected with input $input', ({ input, expected }) => {
expect(monitoringUtils.convertVariablesForURL(input)).toEqual(expected); expect(monitoringUtils.convertVariablesForURL(input)).toEqual(expected);
}); });
}); });
describe('setCustomVariablesFromUrl', () => {
beforeEach(() => {
jest.spyOn(urlUtils, 'updateHistory');
});
afterEach(() => {
urlUtils.updateHistory.mockRestore();
});
it.each`
input | urlParams
${[]} | ${''}
${[{ name: 'env', value: 'prod' }]} | ${'?var-env=prod'}
${[{ name: 'env1', value: 'prod' }, { name: 'env2', value: null }]} | ${'?var-env=prod&var-env1=prod'}
`(
'setCustomVariablesFromUrl updates history with query "$urlParams" with input $input',
({ input, urlParams }) => {
monitoringUtils.setCustomVariablesFromUrl(input);
expect(urlUtils.updateHistory).toHaveBeenCalledTimes(1);
expect(urlUtils.updateHistory).toHaveBeenCalledWith({
url: `http://localhost/${urlParams}`,
title: '',
});
},
);
});
}); });
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