Commit 23f5dcf6 authored by Fatih Acet's avatar Fatih Acet

Merge branch '59974-multidash' into 'master'

Load dashboards from project's git repository

Closes #59974

See merge request gitlab-org/gitlab-ce!29348
parents 871d0699 27afe549
...@@ -106,17 +106,24 @@ export default { ...@@ -106,17 +106,24 @@ export default {
}, },
customMetricsPath: { customMetricsPath: {
type: String, type: String,
required: true, required: false,
default: invalidUrl,
}, },
validateQueryPath: { validateQueryPath: {
type: String, type: String,
required: true, required: false,
default: invalidUrl,
}, },
dashboardEndpoint: { dashboardEndpoint: {
type: String, type: String,
required: false, required: false,
default: invalidUrl, default: invalidUrl,
}, },
currentDashboard: {
type: String,
required: false,
default: '',
},
}, },
data() { data() {
return { return {
...@@ -139,10 +146,15 @@ export default { ...@@ -139,10 +146,15 @@ export default {
'deploymentData', 'deploymentData',
'metricsWithData', 'metricsWithData',
'useDashboardEndpoint', 'useDashboardEndpoint',
'allDashboards',
'multipleDashboardsEnabled',
]), ]),
groupsWithData() { groupsWithData() {
return this.groups.filter(group => this.chartsWithData(group.metrics).length > 0); return this.groups.filter(group => this.chartsWithData(group.metrics).length > 0);
}, },
selectedDashboardText() {
return this.currentDashboard || (this.allDashboards[0] && this.allDashboards[0].display_name);
},
}, },
created() { created() {
this.setEndpoints({ this.setEndpoints({
...@@ -150,6 +162,7 @@ export default { ...@@ -150,6 +162,7 @@ export default {
environmentsEndpoint: this.environmentsEndpoint, environmentsEndpoint: this.environmentsEndpoint,
deploymentsEndpoint: this.deploymentsEndpoint, deploymentsEndpoint: this.deploymentsEndpoint,
dashboardEndpoint: this.dashboardEndpoint, dashboardEndpoint: this.dashboardEndpoint,
currentDashboard: this.currentDashboard,
}); });
this.timeWindows = timeWindows; this.timeWindows = timeWindows;
...@@ -240,6 +253,24 @@ export default { ...@@ -240,6 +253,24 @@ export default {
v-if="environmentsEndpoint" v-if="environmentsEndpoint"
class="dropdowns d-flex align-items-center justify-content-between" class="dropdowns d-flex align-items-center justify-content-between"
> >
<div v-if="multipleDashboardsEnabled" class="d-flex align-items-center">
<label class="mb-0">{{ __('Dashboard') }}</label>
<gl-dropdown
class="ml-2 mr-3 js-dashboards-dropdown"
toggle-class="dropdown-menu-toggle"
:text="selectedDashboardText"
>
<gl-dropdown-item
v-for="dashboard in allDashboards"
:key="dashboard.path"
:active="dashboard.path === currentDashboard"
active-class="is-active"
:href="`?dashboard=${dashboard.path}`"
>
{{ dashboard.display_name || dashboard.path }}
</gl-dropdown-item>
</gl-dropdown>
</div>
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<strong>{{ s__('Metrics|Environment') }}</strong> <strong>{{ s__('Metrics|Environment') }}</strong>
<gl-dropdown <gl-dropdown
......
import Vue from 'vue'; import Vue from 'vue';
import { parseBoolean } from '~/lib/utils/common_utils'; import { parseBoolean } from '~/lib/utils/common_utils';
import { getParameterValues } from '~/lib/utils/url_utility';
import Dashboard from 'ee_else_ce/monitoring/components/dashboard.vue'; import Dashboard from 'ee_else_ce/monitoring/components/dashboard.vue';
import store from './stores'; import store from './stores';
...@@ -7,10 +8,12 @@ export default (props = {}) => { ...@@ -7,10 +8,12 @@ export default (props = {}) => {
const el = document.getElementById('prometheus-graphs'); const el = document.getElementById('prometheus-graphs');
if (el && el.dataset) { if (el && el.dataset) {
store.dispatch( store.dispatch('monitoringDashboard/setFeatureFlags', {
'monitoringDashboard/setDashboardEnabled', prometheusEndpointEnabled: gon.features.environmentMetricsUsePrometheusEndpoint,
gon.features.environmentMetricsUsePrometheusEndpoint, multipleDashboardsEnabled: gon.features.environmentMetricsShowMultipleDashboards,
); });
const [currentDashboard] = getParameterValues('dashboard');
// eslint-disable-next-line no-new // eslint-disable-next-line no-new
new Vue({ new Vue({
...@@ -20,6 +23,7 @@ export default (props = {}) => { ...@@ -20,6 +23,7 @@ export default (props = {}) => {
return createElement(Dashboard, { return createElement(Dashboard, {
props: { props: {
...el.dataset, ...el.dataset,
currentDashboard,
hasMetrics: parseBoolean(el.dataset.hasMetrics), hasMetrics: parseBoolean(el.dataset.hasMetrics),
...props, ...props,
}, },
......
...@@ -35,14 +35,24 @@ export const setEndpoints = ({ commit }, endpoints) => { ...@@ -35,14 +35,24 @@ export const setEndpoints = ({ commit }, endpoints) => {
commit(types.SET_ENDPOINTS, endpoints); commit(types.SET_ENDPOINTS, endpoints);
}; };
export const setDashboardEnabled = ({ commit }, enabled) => { export const setFeatureFlags = (
commit(types.SET_DASHBOARD_ENABLED, enabled); { commit },
{ prometheusEndpointEnabled, multipleDashboardsEnabled },
) => {
commit(types.SET_DASHBOARD_ENABLED, prometheusEndpointEnabled);
commit(types.SET_MULTIPLE_DASHBOARDS_ENABLED, multipleDashboardsEnabled);
}; };
export const requestMetricsDashboard = ({ commit }) => { export const requestMetricsDashboard = ({ commit }) => {
commit(types.REQUEST_METRICS_DATA); commit(types.REQUEST_METRICS_DATA);
}; };
export const receiveMetricsDashboardSuccess = ({ commit, dispatch }, { response, params }) => { export const receiveMetricsDashboardSuccess = (
{ state, commit, dispatch },
{ response, params },
) => {
if (state.multipleDashboardsEnabled) {
commit(types.SET_ALL_DASHBOARDS, response.all_dashboards);
}
commit(types.RECEIVE_METRICS_DATA_SUCCESS, response.dashboard.panel_groups); commit(types.RECEIVE_METRICS_DATA_SUCCESS, response.dashboard.panel_groups);
dispatch('fetchPrometheusMetrics', params); dispatch('fetchPrometheusMetrics', params);
}; };
...@@ -95,6 +105,11 @@ export const fetchMetricsData = ({ state, dispatch }, params) => { ...@@ -95,6 +105,11 @@ export const fetchMetricsData = ({ state, dispatch }, params) => {
export const fetchDashboard = ({ state, dispatch }, params) => { export const fetchDashboard = ({ state, dispatch }, params) => {
dispatch('requestMetricsDashboard'); dispatch('requestMetricsDashboard');
if (state.currentDashboard) {
// eslint-disable-next-line no-param-reassign
params.dashboard = state.currentDashboard;
}
return axios return axios
.get(state.dashboardEndpoint, { params }) .get(state.dashboardEndpoint, { params })
.then(resp => resp.data) .then(resp => resp.data)
......
...@@ -10,6 +10,8 @@ export const RECEIVE_ENVIRONMENTS_DATA_FAILURE = 'RECEIVE_ENVIRONMENTS_DATA_FAIL ...@@ -10,6 +10,8 @@ export const RECEIVE_ENVIRONMENTS_DATA_FAILURE = 'RECEIVE_ENVIRONMENTS_DATA_FAIL
export const SET_QUERY_RESULT = 'SET_QUERY_RESULT'; export const SET_QUERY_RESULT = 'SET_QUERY_RESULT';
export const SET_TIME_WINDOW = 'SET_TIME_WINDOW'; export const SET_TIME_WINDOW = 'SET_TIME_WINDOW';
export const SET_DASHBOARD_ENABLED = 'SET_DASHBOARD_ENABLED'; export const SET_DASHBOARD_ENABLED = 'SET_DASHBOARD_ENABLED';
export const SET_MULTIPLE_DASHBOARDS_ENABLED = 'SET_MULTIPLE_DASHBOARDS_ENABLED';
export const SET_ALL_DASHBOARDS = 'SET_ALL_DASHBOARDS';
export const SET_ENDPOINTS = 'SET_ENDPOINTS'; export const SET_ENDPOINTS = 'SET_ENDPOINTS';
export const SET_GETTING_STARTED_EMPTY_STATE = 'SET_GETTING_STARTED_EMPTY_STATE'; export const SET_GETTING_STARTED_EMPTY_STATE = 'SET_GETTING_STARTED_EMPTY_STATE';
export const SET_NO_DATA_EMPTY_STATE = 'SET_NO_DATA_EMPTY_STATE'; export const SET_NO_DATA_EMPTY_STATE = 'SET_NO_DATA_EMPTY_STATE';
...@@ -74,10 +74,14 @@ export default { ...@@ -74,10 +74,14 @@ export default {
state.environmentsEndpoint = endpoints.environmentsEndpoint; state.environmentsEndpoint = endpoints.environmentsEndpoint;
state.deploymentsEndpoint = endpoints.deploymentsEndpoint; state.deploymentsEndpoint = endpoints.deploymentsEndpoint;
state.dashboardEndpoint = endpoints.dashboardEndpoint; state.dashboardEndpoint = endpoints.dashboardEndpoint;
state.currentDashboard = endpoints.currentDashboard;
}, },
[types.SET_DASHBOARD_ENABLED](state, enabled) { [types.SET_DASHBOARD_ENABLED](state, enabled) {
state.useDashboardEndpoint = enabled; state.useDashboardEndpoint = enabled;
}, },
[types.SET_MULTIPLE_DASHBOARDS_ENABLED](state, enabled) {
state.multipleDashboardsEnabled = enabled;
},
[types.SET_GETTING_STARTED_EMPTY_STATE](state) { [types.SET_GETTING_STARTED_EMPTY_STATE](state) {
state.emptyState = 'gettingStarted'; state.emptyState = 'gettingStarted';
}, },
...@@ -85,4 +89,7 @@ export default { ...@@ -85,4 +89,7 @@ export default {
state.showEmptyState = true; state.showEmptyState = true;
state.emptyState = 'noData'; state.emptyState = 'noData';
}, },
[types.SET_ALL_DASHBOARDS](state, dashboards) {
state.allDashboards = dashboards;
},
}; };
...@@ -8,10 +8,13 @@ export default () => ({ ...@@ -8,10 +8,13 @@ export default () => ({
deploymentsEndpoint: null, deploymentsEndpoint: null,
dashboardEndpoint: invalidUrl, dashboardEndpoint: invalidUrl,
useDashboardEndpoint: false, useDashboardEndpoint: false,
multipleDashboardsEnabled: false,
emptyState: 'gettingStarted', emptyState: 'gettingStarted',
showEmptyState: true, showEmptyState: true,
groups: [], groups: [],
deploymentData: [], deploymentData: [],
environments: [], environments: [],
metricsWithData: [], metricsWithData: [],
allDashboards: [],
currentDashboard: null,
}); });
...@@ -10,6 +10,7 @@ import { ...@@ -10,6 +10,7 @@ import {
mockApiEndpoint, mockApiEndpoint,
environmentData, environmentData,
singleGroupResponse, singleGroupResponse,
dashboardGitResponse,
} from './mock_data'; } from './mock_data';
const propsData = { const propsData = {
...@@ -308,10 +309,6 @@ describe('Dashboard', () => { ...@@ -308,10 +309,6 @@ describe('Dashboard', () => {
spyOn(component.$store, 'dispatch').and.stub(); spyOn(component.$store, 'dispatch').and.stub();
const getTimeDiffSpy = spyOnDependency(Dashboard, 'getTimeDiff'); const getTimeDiffSpy = spyOnDependency(Dashboard, 'getTimeDiff');
component.$store.commit(
`monitoringDashboard/${types.SET_ENVIRONMENTS_ENDPOINT}`,
'/environments',
);
component.$store.commit( component.$store.commit(
`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
environmentData, environmentData,
...@@ -430,4 +427,49 @@ describe('Dashboard', () => { ...@@ -430,4 +427,49 @@ describe('Dashboard', () => {
}); });
}); });
}); });
describe('Dashboard dropdown', () => {
beforeEach(() => {
mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData: {
...propsData,
hasMetrics: true,
showPanels: false,
},
store,
});
component.$store.dispatch('monitoringDashboard/setFeatureFlags', {
prometheusEndpoint: false,
multipleDashboardsEnabled: true,
});
component.$store.commit(
`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
environmentData,
);
component.$store.commit(
`monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
singleGroupResponse,
);
component.$store.commit(
`monitoringDashboard/${types.SET_ALL_DASHBOARDS}`,
dashboardGitResponse,
);
});
it('shows the dashboard dropdown', done => {
setTimeout(() => {
const dashboardDropdown = component.$el.querySelector('.js-dashboards-dropdown');
expect(dashboardDropdown).not.toEqual(null);
done();
});
});
});
}); });
...@@ -922,3 +922,16 @@ export const metricsDashboardResponse = { ...@@ -922,3 +922,16 @@ export const metricsDashboardResponse = {
}, },
status: 'success', status: 'success',
}; };
export const dashboardGitResponse = [
{
path: 'config/prometheus/common_metrics.yml',
display_name: 'Common Metrics',
default: true,
},
{
path: '.gitlab/dashboards/super.yml',
display_name: 'Custom Dashboard 1',
default: false,
},
];
...@@ -22,6 +22,7 @@ import { ...@@ -22,6 +22,7 @@ import {
environmentData, environmentData,
metricsDashboardResponse, metricsDashboardResponse,
metricsGroupsAPIResponse, metricsGroupsAPIResponse,
dashboardGitResponse,
} from '../mock_data'; } from '../mock_data';
describe('Monitoring store actions', () => { describe('Monitoring store actions', () => {
...@@ -212,17 +213,19 @@ describe('Monitoring store actions', () => { ...@@ -212,17 +213,19 @@ describe('Monitoring store actions', () => {
describe('receiveMetricsDashboardSuccess', () => { describe('receiveMetricsDashboardSuccess', () => {
let commit; let commit;
let dispatch; let dispatch;
let state;
beforeEach(() => { beforeEach(() => {
commit = jasmine.createSpy(); commit = jasmine.createSpy();
dispatch = jasmine.createSpy(); dispatch = jasmine.createSpy();
state = storeState();
}); });
it('stores groups ', () => { it('stores groups ', () => {
const params = {}; const params = {};
const response = metricsDashboardResponse; const response = metricsDashboardResponse;
receiveMetricsDashboardSuccess({ commit, dispatch }, { response, params }); receiveMetricsDashboardSuccess({ state, commit, dispatch }, { response, params });
expect(commit).toHaveBeenCalledWith( expect(commit).toHaveBeenCalledWith(
types.RECEIVE_METRICS_DATA_SUCCESS, types.RECEIVE_METRICS_DATA_SUCCESS,
...@@ -231,6 +234,18 @@ describe('Monitoring store actions', () => { ...@@ -231,6 +234,18 @@ describe('Monitoring store actions', () => {
expect(dispatch).toHaveBeenCalledWith('fetchPrometheusMetrics', params); expect(dispatch).toHaveBeenCalledWith('fetchPrometheusMetrics', params);
}); });
it('sets the dashboards loaded from the repository', () => {
const params = {};
const response = metricsDashboardResponse;
response.all_dashboards = dashboardGitResponse;
state.multipleDashboardsEnabled = true;
receiveMetricsDashboardSuccess({ state, commit, dispatch }, { response, params });
expect(commit).toHaveBeenCalledWith(types.SET_ALL_DASHBOARDS, dashboardGitResponse);
});
}); });
describe('receiveMetricsDashboardFailure', () => { describe('receiveMetricsDashboardFailure', () => {
......
import mutations from '~/monitoring/stores/mutations'; import mutations from '~/monitoring/stores/mutations';
import * as types from '~/monitoring/stores/mutation_types'; import * as types from '~/monitoring/stores/mutation_types';
import state from '~/monitoring/stores/state'; import state from '~/monitoring/stores/state';
import { metricsGroupsAPIResponse, deploymentData, metricsDashboardResponse } from '../mock_data'; import {
metricsGroupsAPIResponse,
deploymentData,
metricsDashboardResponse,
dashboardGitResponse,
} from '../mock_data';
describe('Monitoring mutations', () => { describe('Monitoring mutations', () => {
let stateCopy; let stateCopy;
...@@ -156,4 +161,12 @@ describe('Monitoring mutations', () => { ...@@ -156,4 +161,12 @@ describe('Monitoring mutations', () => {
expect(stateCopy.metricsWithData).toEqual([]); expect(stateCopy.metricsWithData).toEqual([]);
}); });
}); });
describe('SET_ALL_DASHBOARDS', () => {
it('stores the dashboards loaded from the git repository', () => {
mutations[types.SET_ALL_DASHBOARDS](stateCopy, dashboardGitResponse);
expect(stateCopy.allDashboards).toEqual(dashboardGitResponse);
});
});
}); });
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