summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClement Ho <clemmakesapps@gmail.com>2019-04-05 22:18:11 +0000
committerClement Ho <clemmakesapps@gmail.com>2019-04-05 22:18:11 +0000
commit23df40f99f24ce15297c8fa9a9a90156883e8d0a (patch)
tree956ef544d4c0cc00faa21a01d29a3d2efb7cebec
parent7ffd794140ce22b7366d558bde1c97c502104ea3 (diff)
parent976f1feb28265bfc427a00e7031275f49aa7f877 (diff)
downloadgitlab-ce-23df40f99f24ce15297c8fa9a9a90156883e8d0a.tar.gz
Merge branch '31368-support-different-time-windows-for-performance-dashboard' into 'master'
Resolve "Support different time windows for performance dashboard" Closes #31368 See merge request gitlab-org/gitlab-ce!26047
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard.vue80
-rw-r--r--app/assets/javascripts/monitoring/constants.js13
-rw-r--r--app/assets/javascripts/monitoring/monitoring_bundle.js1
-rw-r--r--app/assets/javascripts/monitoring/services/monitoring_service.js6
-rw-r--r--app/assets/javascripts/monitoring/utils.js34
-rw-r--r--app/assets/stylesheets/pages/environments.scss2
-rw-r--r--locale/gitlab.pot24
-rw-r--r--spec/javascripts/monitoring/dashboard_spec.js93
8 files changed, 226 insertions, 27 deletions
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue
index f5019bc627e..f2bd4150b6d 100644
--- a/app/assets/javascripts/monitoring/components/dashboard.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard.vue
@@ -10,6 +10,8 @@ import MonitorAreaChart from './charts/area.vue';
import GraphGroup from './graph_group.vue';
import EmptyState from './empty_state.vue';
import MonitoringStore from '../stores/monitoring_store';
+import { timeWindows } from '../constants';
+import { getTimeDiff } from '../utils';
const sidebarAnimationDuration = 150;
let sidebarMutationObserver;
@@ -88,6 +90,10 @@ export default {
type: String,
required: true,
},
+ showTimeWindowDropdown: {
+ type: Boolean,
+ required: true,
+ },
},
data() {
return {
@@ -95,6 +101,7 @@ export default {
state: 'gettingStarted',
showEmptyState: true,
elWidth: 0,
+ selectedTimeWindow: '',
};
},
created() {
@@ -103,6 +110,9 @@ export default {
deploymentEndpoint: this.deploymentEndpoint,
environmentsEndpoint: this.environmentsEndpoint,
});
+
+ this.timeWindows = timeWindows;
+ this.selectedTimeWindow = this.timeWindows.eightHours;
},
beforeDestroy() {
if (sidebarMutationObserver) {
@@ -166,33 +176,73 @@ export default {
this.state = 'unableToConnect';
});
},
+ getGraphsDataWithTime(timeFrame) {
+ this.state = 'loading';
+ this.showEmptyState = true;
+ this.service
+ .getGraphsData(getTimeDiff(this.timeWindows[timeFrame]))
+ .then(data => {
+ this.store.storeMetrics(data);
+ this.selectedTimeWindow = this.timeWindows[timeFrame];
+ })
+ .catch(() => {
+ Flash(s__('Metrics|Not enough data to display'));
+ })
+ .finally(() => {
+ this.showEmptyState = false;
+ });
+ },
onSidebarMutation() {
setTimeout(() => {
this.elWidth = this.$el.clientWidth;
}, sidebarAnimationDuration);
},
+ activeTimeWindow(key) {
+ return this.timeWindows[key] === this.selectedTimeWindow;
+ },
},
};
</script>
<template>
<div v-if="!showEmptyState" class="prometheus-graphs prepend-top-default">
- <div v-if="environmentsEndpoint" class="environments d-flex align-items-center">
- <strong>{{ s__('Metrics|Environment') }}</strong>
- <gl-dropdown
- class="prepend-left-10 js-environments-dropdown"
- toggle-class="dropdown-menu-toggle"
- :text="currentEnvironmentName"
- :disabled="store.environmentsData.length === 0"
- >
- <gl-dropdown-item
- v-for="environment in store.environmentsData"
- :key="environment.id"
- :active="environment.name === currentEnvironmentName"
- active-class="is-active"
- >{{ environment.name }}</gl-dropdown-item
+ <div
+ v-if="environmentsEndpoint"
+ class="dropdowns d-flex align-items-center justify-content-between"
+ >
+ <div class="d-flex align-items-center">
+ <strong>{{ s__('Metrics|Environment') }}</strong>
+ <gl-dropdown
+ class="prepend-left-10 js-environments-dropdown"
+ toggle-class="dropdown-menu-toggle"
+ :text="currentEnvironmentName"
+ :disabled="store.environmentsData.length === 0"
+ >
+ <gl-dropdown-item
+ v-for="environment in store.environmentsData"
+ :key="environment.id"
+ :active="environment.name === currentEnvironmentName"
+ active-class="is-active"
+ >{{ environment.name }}</gl-dropdown-item
+ >
+ </gl-dropdown>
+ </div>
+ <div v-if="showTimeWindowDropdown" class="d-flex align-items-center">
+ <strong>{{ s__('Metrics|Show last') }}</strong>
+ <gl-dropdown
+ class="prepend-left-10 js-time-window-dropdown"
+ toggle-class="dropdown-menu-toggle"
+ :text="selectedTimeWindow"
>
- </gl-dropdown>
+ <gl-dropdown-item
+ v-for="(value, key) in timeWindows"
+ :key="key"
+ :active="activeTimeWindow(key)"
+ @click="getGraphsDataWithTime(key)"
+ >{{ value }}</gl-dropdown-item
+ >
+ </gl-dropdown>
+ </div>
</div>
<graph-group
v-for="(groupData, index) in store.groups"
diff --git a/app/assets/javascripts/monitoring/constants.js b/app/assets/javascripts/monitoring/constants.js
index 55ecf3b5334..9e5d0d0fd28 100644
--- a/app/assets/javascripts/monitoring/constants.js
+++ b/app/assets/javascripts/monitoring/constants.js
@@ -1,3 +1,5 @@
+import { __ } from '~/locale';
+
export const chartHeight = 300;
export const graphTypes = {
@@ -7,3 +9,14 @@ export const graphTypes = {
export const lineTypes = {
default: 'solid',
};
+
+export const timeWindows = {
+ thirtyMinutes: __('30 minutes'),
+ threeHours: __('3 hours'),
+ eightHours: __('8 hours'),
+ oneDay: __('1 day'),
+ threeDays: __('3 days'),
+ oneWeek: __('1 week'),
+};
+
+export const msPerMinute = 60000;
diff --git a/app/assets/javascripts/monitoring/monitoring_bundle.js b/app/assets/javascripts/monitoring/monitoring_bundle.js
index 9d78b5ea110..2b4ddd7afbc 100644
--- a/app/assets/javascripts/monitoring/monitoring_bundle.js
+++ b/app/assets/javascripts/monitoring/monitoring_bundle.js
@@ -14,6 +14,7 @@ export default () => {
props: {
...el.dataset,
hasMetrics: parseBoolean(el.dataset.hasMetrics),
+ showTimeWindowDropdown: gon.features.metricsTimeWindow,
},
});
},
diff --git a/app/assets/javascripts/monitoring/services/monitoring_service.js b/app/assets/javascripts/monitoring/services/monitoring_service.js
index 24b4acaf6da..5fcc2c8cfac 100644
--- a/app/assets/javascripts/monitoring/services/monitoring_service.js
+++ b/app/assets/javascripts/monitoring/services/monitoring_service.js
@@ -32,11 +32,11 @@ export default class MonitoringService {
this.environmentsEndpoint = environmentsEndpoint;
}
- getGraphsData() {
- return backOffRequest(() => axios.get(this.metricsEndpoint))
+ getGraphsData(params = {}) {
+ return backOffRequest(() => axios.get(this.metricsEndpoint, { params }))
.then(resp => resp.data)
.then(response => {
- if (!response || !response.data) {
+ if (!response || !response.data || !response.success) {
throw new Error(s__('Metrics|Unexpected metrics data response from prometheus endpoint'));
}
return response.data;
diff --git a/app/assets/javascripts/monitoring/utils.js b/app/assets/javascripts/monitoring/utils.js
new file mode 100644
index 00000000000..e379827b769
--- /dev/null
+++ b/app/assets/javascripts/monitoring/utils.js
@@ -0,0 +1,34 @@
+import { timeWindows, msPerMinute } from './constants';
+
+/**
+ * method that converts a predetermined time window to minutes
+ * defaults to 8 hours as the default option
+ * @param {String} timeWindow - The time window to convert to minutes
+ * @returns {number} The time window in minutes
+ */
+const getTimeDifferenceMinutes = timeWindow => {
+ switch (timeWindow) {
+ case timeWindows.thirtyMinutes:
+ return 30;
+ case timeWindows.threeHours:
+ return 60 * 3;
+ case timeWindows.oneDay:
+ return 60 * 24 * 1;
+ case timeWindows.threeDays:
+ return 60 * 24 * 3;
+ case timeWindows.oneWeek:
+ return 60 * 24 * 7 * 1;
+ default:
+ return 60 * 8;
+ }
+};
+
+export const getTimeDiff = selectedTimeWindow => {
+ const end = Date.now();
+ const timeDifferenceMinutes = getTimeDifferenceMinutes(selectedTimeWindow);
+ const start = new Date(end - timeDifferenceMinutes * msPerMinute).getTime();
+
+ return { start, end };
+};
+
+export default {};
diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss
index 0eb854ecf98..8e1ee51628d 100644
--- a/app/assets/stylesheets/pages/environments.scss
+++ b/app/assets/stylesheets/pages/environments.scss
@@ -204,7 +204,7 @@
}
.prometheus-graphs {
- .environments {
+ .dropdowns {
.dropdown-menu-toggle {
svg {
position: absolute;
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 1166134347c..139272d5f7b 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -238,6 +238,9 @@ msgid_plural "%d closed merge requests"
msgstr[0] ""
msgstr[1] ""
+msgid "1 day"
+msgstr ""
+
msgid "1 merged merge request"
msgid_plural "%d merged merge requests"
msgstr[0] ""
@@ -258,6 +261,9 @@ msgid_plural "%d pipelines"
msgstr[0] ""
msgstr[1] ""
+msgid "1 week"
+msgstr ""
+
msgid "1st contribution!"
msgstr ""
@@ -267,6 +273,15 @@ msgstr ""
msgid "2FA enabled"
msgstr ""
+msgid "3 days"
+msgstr ""
+
+msgid "3 hours"
+msgstr ""
+
+msgid "30 minutes"
+msgstr ""
+
msgid "403|Please contact your GitLab administrator to get permission."
msgstr ""
@@ -282,6 +297,9 @@ msgstr ""
msgid "404|Please contact your GitLab administrator if you think this is a mistake."
msgstr ""
+msgid "8 hours"
+msgstr ""
+
msgid "<code>\"johnsmith@example.com\": \"@johnsmith\"</code> will add \"By <a href=\"#\">@johnsmith</a>\" to all issues and comments originally created by johnsmith@example.com, and will set <a href=\"#\">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com."
msgstr ""
@@ -5109,6 +5127,12 @@ msgstr ""
msgid "Metrics|No deployed environments"
msgstr ""
+msgid "Metrics|Not enough data to display"
+msgstr ""
+
+msgid "Metrics|Show last"
+msgstr ""
+
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
diff --git a/spec/javascripts/monitoring/dashboard_spec.js b/spec/javascripts/monitoring/dashboard_spec.js
index 454777fa912..ce2c6c43c0f 100644
--- a/spec/javascripts/monitoring/dashboard_spec.js
+++ b/spec/javascripts/monitoring/dashboard_spec.js
@@ -1,6 +1,7 @@
import Vue from 'vue';
import MockAdapter from 'axios-mock-adapter';
import Dashboard from '~/monitoring/components/dashboard.vue';
+import { timeWindows } from '~/monitoring/constants';
import axios from '~/lib/utils/axios_utils';
import { metricsGroupsAPIResponse, mockApiEndpoint, environmentData } from './mock_data';
@@ -50,7 +51,7 @@ describe('Dashboard', () => {
it('shows a getting started empty state when no metrics are present', () => {
const component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
- propsData,
+ propsData: { ...propsData, showTimeWindowDropdown: false },
});
expect(component.$el.querySelector('.prometheus-graphs')).toBe(null);
@@ -66,7 +67,7 @@ describe('Dashboard', () => {
it('shows up a loading state', done => {
const component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
- propsData: { ...propsData, hasMetrics: true },
+ propsData: { ...propsData, hasMetrics: true, showTimeWindowDropdown: false },
});
Vue.nextTick(() => {
@@ -78,7 +79,12 @@ describe('Dashboard', () => {
it('hides the legend when showLegend is false', done => {
const component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
- propsData: { ...propsData, hasMetrics: true, showLegend: false },
+ propsData: {
+ ...propsData,
+ hasMetrics: true,
+ showLegend: false,
+ showTimeWindowDropdown: false,
+ },
});
setTimeout(() => {
@@ -92,7 +98,12 @@ describe('Dashboard', () => {
it('hides the group panels when showPanels is false', done => {
const component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
- propsData: { ...propsData, hasMetrics: true, showPanels: false },
+ propsData: {
+ ...propsData,
+ hasMetrics: true,
+ showPanels: false,
+ showTimeWindowDropdown: false,
+ },
});
setTimeout(() => {
@@ -106,7 +117,12 @@ describe('Dashboard', () => {
it('renders the environments dropdown with a number of environments', done => {
const component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
- propsData: { ...propsData, hasMetrics: true, showPanels: false },
+ propsData: {
+ ...propsData,
+ hasMetrics: true,
+ showPanels: false,
+ showTimeWindowDropdown: false,
+ },
});
component.store.storeEnvironmentsData(environmentData);
@@ -124,7 +140,12 @@ describe('Dashboard', () => {
it('hides the environments dropdown list when there is no environments', done => {
const component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
- propsData: { ...propsData, hasMetrics: true, showPanels: false },
+ propsData: {
+ ...propsData,
+ hasMetrics: true,
+ showPanels: false,
+ showTimeWindowDropdown: false,
+ },
});
component.store.storeEnvironmentsData([]);
@@ -142,7 +163,12 @@ describe('Dashboard', () => {
it('renders the environments dropdown with a single is-active element', done => {
const component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
- propsData: { ...propsData, hasMetrics: true, showPanels: false },
+ propsData: {
+ ...propsData,
+ hasMetrics: true,
+ showPanels: false,
+ showTimeWindowDropdown: false,
+ },
});
component.store.storeEnvironmentsData(environmentData);
@@ -166,6 +192,7 @@ describe('Dashboard', () => {
hasMetrics: true,
showPanels: false,
environmentsEndpoint: '',
+ showTimeWindowDropdown: false,
},
});
@@ -176,6 +203,51 @@ describe('Dashboard', () => {
done();
});
});
+
+ it('does not show the time window dropdown when the feature flag is not set', done => {
+ const component = new DashboardComponent({
+ el: document.querySelector('.prometheus-graphs'),
+ propsData: {
+ ...propsData,
+ hasMetrics: true,
+ showPanels: false,
+ showTimeWindowDropdown: false,
+ },
+ });
+
+ setTimeout(() => {
+ const timeWindowDropdown = component.$el.querySelector('.js-time-window-dropdown');
+
+ expect(timeWindowDropdown).toBeNull();
+
+ done();
+ });
+ });
+
+ it('renders the time window dropdown with a set of options', done => {
+ const component = new DashboardComponent({
+ el: document.querySelector('.prometheus-graphs'),
+ propsData: {
+ ...propsData,
+ hasMetrics: true,
+ showPanels: false,
+ showTimeWindowDropdown: true,
+ },
+ });
+ const numberOfTimeWindows = Object.keys(timeWindows).length;
+
+ setTimeout(() => {
+ const timeWindowDropdown = component.$el.querySelector('.js-time-window-dropdown');
+ const timeWindowDropdownEls = component.$el.querySelectorAll(
+ '.js-time-window-dropdown .dropdown-item',
+ );
+
+ expect(timeWindowDropdown).not.toBeNull();
+ expect(timeWindowDropdownEls.length).toEqual(numberOfTimeWindows);
+
+ done();
+ });
+ });
});
describe('when the window resizes', () => {
@@ -191,7 +263,12 @@ describe('Dashboard', () => {
it('sets elWidth to page width when the sidebar is resized', done => {
const component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
- propsData: { ...propsData, hasMetrics: true, showPanels: false },
+ propsData: {
+ ...propsData,
+ hasMetrics: true,
+ showPanels: false,
+ showTimeWindowDropdown: false,
+ },
});
expect(component.elWidth).toEqual(0);