Commit 27afe549 authored by Simon Knox's avatar Simon Knox Committed by Fatih Acet

Add feature flag and dashboard endpoint

First part of FE for Prometheus API
Dashboard endpoint fetches all info except for chart results
Renders empty groups after loading
parent 871d0699
...@@ -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