diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2019-12-11 09:08:12 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2019-12-11 09:08:12 +0000 |
commit | 6b8040dc25fdc5fe614c3796a147517dd50bc7d8 (patch) | |
tree | 1930c21748fc632a7900659a71fcb7248097879f /app/assets/javascripts/monitoring | |
parent | 7b875aa3fd1645e2e881997256ba94c6cb73ab3d (diff) | |
download | gitlab-ce-6b8040dc25fdc5fe614c3796a147517dd50bc7d8.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/monitoring')
9 files changed, 149 insertions, 69 deletions
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue index fb6f5dc73b8..2a9321f6733 100644 --- a/app/assets/javascripts/monitoring/components/dashboard.vue +++ b/app/assets/javascripts/monitoring/components/dashboard.vue @@ -1,6 +1,6 @@ <script> import _ from 'underscore'; -import { mapActions, mapState } from 'vuex'; +import { mapActions, mapState, mapGetters } from 'vuex'; import VueDraggable from 'vuedraggable'; import { GlButton, @@ -99,6 +99,10 @@ export default { type: String, required: true, }, + emptyNoDataSmallSvgPath: { + type: String, + required: true, + }, emptyUnableToConnectSvgPath: { type: String, required: true, @@ -176,11 +180,11 @@ export default { 'showEmptyState', 'environments', 'deploymentData', - 'metricsWithData', 'useDashboardEndpoint', 'allDashboards', 'additionalPanelTypesEnabled', ]), + ...mapGetters('monitoringDashboard', ['metricsWithData']), firstDashboard() { return this.environmentsEndpoint.length > 0 && this.allDashboards.length > 0 ? this.allDashboards[0] @@ -280,13 +284,8 @@ export default { submitCustomMetricsForm() { this.$refs.customMetricsForm.submit(); }, - chartsWithData(panels) { - return panels.filter(panel => - panel.metrics.some(metric => this.metricsWithData.includes(metric.metric_id)), - ); - }, groupHasData(group) { - return this.chartsWithData(group.panels).length > 0; + return this.metricsWithData(group.key).length > 0; }, onDateTimePickerApply(timeWindowUrlParams) { return redirectTo(mergeUrlParams(timeWindowUrlParams, window.location.href)); @@ -447,42 +446,61 @@ export default { :key="`${groupData.group}.${groupData.priority}`" :name="groupData.group" :show-panels="showPanels" - :collapse-group="groupHasData(groupData)" + :collapse-group="!groupHasData(groupData)" > - <vue-draggable - :value="groupData.panels" - group="metrics-dashboard" - :component-data="{ attrs: { class: 'row mx-0 w-100' } }" - :disabled="!isRearrangingPanels" - @input="updatePanels(groupData.key, $event)" - > - <div - v-for="(graphData, graphIndex) in groupData.panels" - :key="`panel-type-${graphIndex}`" - class="col-12 col-lg-6 px-2 mb-2 draggable" - :class="{ 'draggable-enabled': isRearrangingPanels }" + <div v-if="groupHasData(groupData)"> + <vue-draggable + :value="groupData.panels" + group="metrics-dashboard" + :component-data="{ attrs: { class: 'row mx-0 w-100' } }" + :disabled="!isRearrangingPanels" + @input="updatePanels(groupData.key, $event)" > - <div class="position-relative draggable-panel js-draggable-panel"> - <div - v-if="isRearrangingPanels" - class="draggable-remove js-draggable-remove p-2 w-100 position-absolute d-flex justify-content-end" - @click="removePanel(groupData.key, groupData.panels, graphIndex)" - > - <a class="mx-2 p-2 draggable-remove-link" :aria-label="__('Remove')" - ><icon name="close" - /></a> - </div> + <div + v-for="(graphData, graphIndex) in groupData.panels" + :key="`panel-type-${graphIndex}`" + class="col-12 col-lg-6 px-2 mb-2 draggable" + :class="{ 'draggable-enabled': isRearrangingPanels }" + > + <div class="position-relative draggable-panel js-draggable-panel"> + <div + v-if="isRearrangingPanels" + class="draggable-remove js-draggable-remove p-2 w-100 position-absolute d-flex justify-content-end" + @click="removePanel(groupData.key, groupData.panels, graphIndex)" + > + <a class="mx-2 p-2 draggable-remove-link" :aria-label="__('Remove')" + ><icon name="close" + /></a> + </div> - <panel-type - :clipboard-text="generateLink(groupData.group, graphData.title, graphData.y_label)" - :graph-data="graphData" - :alerts-endpoint="alertsEndpoint" - :prometheus-alerts-available="prometheusAlertsAvailable" - :index="`${index}-${graphIndex}`" - /> + <panel-type + :clipboard-text=" + generateLink(groupData.group, graphData.title, graphData.y_label) + " + :graph-data="graphData" + :alerts-endpoint="alertsEndpoint" + :prometheus-alerts-available="prometheusAlertsAvailable" + :index="`${index}-${graphIndex}`" + /> + </div> </div> - </div> - </vue-draggable> + </vue-draggable> + </div> + <div v-else class="py-5 col col-sm-10 col-md-8 col-lg-7 col-xl-6"> + <empty-state + ref="empty-group" + selected-state="noDataGroup" + :documentation-path="documentationPath" + :settings-path="settingsPath" + :clusters-path="clustersPath" + :empty-getting-started-svg-path="emptyGettingStartedSvgPath" + :empty-loading-svg-path="emptyLoadingSvgPath" + :empty-no-data-svg-path="emptyNoDataSvgPath" + :empty-no-data-small-svg-path="emptyNoDataSmallSvgPath" + :empty-unable-to-connect-svg-path="emptyUnableToConnectSvgPath" + :compact="true" + /> + </div> </graph-group> </div> <empty-state @@ -494,6 +512,7 @@ export default { :empty-getting-started-svg-path="emptyGettingStartedSvgPath" :empty-loading-svg-path="emptyLoadingSvgPath" :empty-no-data-svg-path="emptyNoDataSvgPath" + :empty-no-data-small-svg-path="emptyNoDataSmallSvgPath" :empty-unable-to-connect-svg-path="emptyUnableToConnectSvgPath" :compact="smallEmptyState" /> diff --git a/app/assets/javascripts/monitoring/components/embed.vue b/app/assets/javascripts/monitoring/components/embed.vue index a5c933a0071..dae1fbad547 100644 --- a/app/assets/javascripts/monitoring/components/embed.vue +++ b/app/assets/javascripts/monitoring/components/embed.vue @@ -1,5 +1,5 @@ <script> -import { mapActions, mapState } from 'vuex'; +import { mapActions, mapState, mapGetters } from 'vuex'; import { getParameterValues, removeParams } from '~/lib/utils/url_utility'; import PanelType from 'ee_else_ce/monitoring/components/panel_type.vue'; import GraphGroup from './graph_group.vue'; @@ -35,7 +35,8 @@ export default { }; }, computed: { - ...mapState('monitoringDashboard', ['dashboard', 'metricsWithData']), + ...mapState('monitoringDashboard', ['dashboard']), + ...mapGetters('monitoringDashboard', ['metricsWithData']), charts() { if (!this.dashboard || !this.dashboard.panel_groups) { return []; @@ -73,7 +74,7 @@ export default { 'setShowErrorBanner', ]), chartHasData(chart) { - return chart.metrics.some(metric => this.metricsWithData.includes(metric.metric_id)); + return chart.metrics.some(metric => this.metricsWithData().includes(metric.metric_id)); }, onSidebarMutation() { setTimeout(() => { diff --git a/app/assets/javascripts/monitoring/components/empty_state.vue b/app/assets/javascripts/monitoring/components/empty_state.vue index 1bb40447a3e..ab8c9712ce4 100644 --- a/app/assets/javascripts/monitoring/components/empty_state.vue +++ b/app/assets/javascripts/monitoring/components/empty_state.vue @@ -37,6 +37,10 @@ export default { type: String, required: true, }, + emptyNoDataSmallSvgPath: { + type: String, + required: true, + }, emptyUnableToConnectSvgPath: { type: String, required: true, @@ -80,6 +84,11 @@ export default { secondaryButtonText: '', secondaryButtonPath: '', }, + noDataGroup: { + svgUrl: this.emptyNoDataSmallSvgPath, + title: __('No data to display'), + description: __('The data source is connected, but there is no data to display.'), + }, unableToConnect: { svgUrl: this.emptyUnableToConnectSvgPath, title: __('Unable to connect to Prometheus server'), diff --git a/app/assets/javascripts/monitoring/components/graph_group.vue b/app/assets/javascripts/monitoring/components/graph_group.vue index e01324372a7..5a7981b6534 100644 --- a/app/assets/javascripts/monitoring/components/graph_group.vue +++ b/app/assets/javascripts/monitoring/components/graph_group.vue @@ -15,31 +15,44 @@ export default { required: false, default: true, }, + /** + * Initial value of collapse on mount. + */ collapseGroup: { type: Boolean, - required: true, + required: false, + default: false, }, }, data() { return { - showGroup: true, + isCollapsed: this.collapseGroup, }; }, computed: { caretIcon() { - return this.collapseGroup && this.showGroup ? 'angle-down' : 'angle-right'; + return this.isCollapsed ? 'angle-right' : 'angle-down'; + }, + }, + watch: { + collapseGroup(val) { + // Respond to changes in collapseGroup but do not + // collapse it once was opened by the user. + if (this.showPanels && !val) { + this.isCollapsed = false; + } }, }, methods: { collapse() { - this.showGroup = !this.showGroup; + this.isCollapsed = !this.isCollapsed; }, }, }; </script> <template> - <div v-if="showPanels" class="card prometheus-panel"> + <div v-if="showPanels" ref="graph-group" class="card prometheus-panel"> <div class="card-header d-flex align-items-center"> <h4 class="flex-grow-1">{{ name }}</h4> <a role="button" class="js-graph-group-toggle" @click="collapse"> @@ -47,12 +60,12 @@ export default { </a> </div> <div - v-if="collapseGroup" - v-show="collapseGroup && showGroup" + v-show="!isCollapsed" + ref="graph-group-content" class="card-body prometheus-graph-group p-0" > <slot></slot> </div> </div> - <div v-else class="prometheus-graph-group"><slot></slot></div> + <div v-else ref="graph-group-content" class="prometheus-graph-group"><slot></slot></div> </template> diff --git a/app/assets/javascripts/monitoring/stores/actions.js b/app/assets/javascripts/monitoring/stores/actions.js index 3612e4d173f..268d9d636b1 100644 --- a/app/assets/javascripts/monitoring/stores/actions.js +++ b/app/assets/javascripts/monitoring/stores/actions.js @@ -4,7 +4,7 @@ import createFlash from '~/flash'; import trackDashboardLoad from '../monitoring_tracking_helper'; import statusCodes from '../../lib/utils/http_status'; import { backOff } from '../../lib/utils/common_utils'; -import { s__ } from '../../locale'; +import { s__, sprintf } from '../../locale'; const TWO_MINUTES = 120000; @@ -74,17 +74,21 @@ export const fetchDashboard = ({ state, dispatch }, params) => { return backOffRequest(() => axios.get(state.dashboardEndpoint, { params })) .then(resp => resp.data) .then(response => dispatch('receiveMetricsDashboardSuccess', { response, params })) - .then(() => { - const dashboardType = state.currentDashboard === '' ? 'default' : 'custom'; - return trackDashboardLoad({ - label: `${dashboardType}_metrics_dashboard`, - value: state.metricsWithData.length, - }); - }) - .catch(error => { - dispatch('receiveMetricsDashboardFailure', error); - if (state.setShowErrorBanner) { - createFlash(s__('Metrics|There was an error while retrieving metrics')); + .catch(e => { + dispatch('receiveMetricsDashboardFailure', e); + if (state.showErrorBanner) { + if (e.response.data && e.response.data.message) { + const { message } = e.response.data; + createFlash( + sprintf( + s__('Metrics|There was an error while retrieving metrics. %{message}'), + { message }, + false, + ), + ); + } else { + createFlash(s__('Metrics|There was an error while retrieving metrics')); + } } }); }; @@ -126,7 +130,7 @@ export const fetchPrometheusMetric = ({ commit }, { metric, params }) => { }); }; -export const fetchPrometheusMetrics = ({ state, commit, dispatch }, params) => { +export const fetchPrometheusMetrics = ({ state, commit, dispatch, getters }, params) => { commit(types.REQUEST_METRICS_DATA); const promises = []; @@ -140,9 +144,11 @@ export const fetchPrometheusMetrics = ({ state, commit, dispatch }, params) => { return Promise.all(promises) .then(() => { - if (state.metricsWithData.length === 0) { - commit(types.SET_NO_DATA_EMPTY_STATE); - } + const dashboardType = state.currentDashboard === '' ? 'default' : 'custom'; + trackDashboardLoad({ + label: `${dashboardType}_metrics_dashboard`, + value: getters.metricsWithData().length, + }); }) .catch(() => { createFlash(s__(`Metrics|There was an error while retrieving metrics`), 'warning'); diff --git a/app/assets/javascripts/monitoring/stores/getters.js b/app/assets/javascripts/monitoring/stores/getters.js new file mode 100644 index 00000000000..3eddd52705d --- /dev/null +++ b/app/assets/javascripts/monitoring/stores/getters.js @@ -0,0 +1,32 @@ +const metricsIdsInPanel = panel => + panel.metrics.filter(metric => metric.metricId && metric.result).map(metric => metric.metricId); + +/** + * Getter to obtain the list of metric ids that have data + * + * Useful to understand which parts of the dashboard should + * be displayed. It is a Vuex Method-Style Access getter. + * + * @param {Object} state + * @returns {Function} A function that returns an array of + * metrics in the dashboard that contain results, optionally + * filtered by group key. + */ +export const metricsWithData = state => groupKey => { + let groups = state.dashboard.panel_groups; + if (groupKey) { + groups = groups.filter(group => group.key === groupKey); + } + + const res = []; + groups.forEach(group => { + group.panels.forEach(panel => { + res.push(...metricsIdsInPanel(panel)); + }); + }); + + return res; +}; + +// prevent babel-plugin-rewire from generating an invalid default during karma tests +export default () => {}; diff --git a/app/assets/javascripts/monitoring/stores/index.js b/app/assets/javascripts/monitoring/stores/index.js index d58398c54ae..c1c466b7cf0 100644 --- a/app/assets/javascripts/monitoring/stores/index.js +++ b/app/assets/javascripts/monitoring/stores/index.js @@ -1,6 +1,7 @@ import Vue from 'vue'; import Vuex from 'vuex'; import * as actions from './actions'; +import * as getters from './getters'; import mutations from './mutations'; import state from './state'; @@ -12,6 +13,7 @@ export const createStore = () => monitoringDashboard: { namespaced: true, actions, + getters, mutations, state, }, diff --git a/app/assets/javascripts/monitoring/stores/mutations.js b/app/assets/javascripts/monitoring/stores/mutations.js index bfa76aa7cea..db5ec4e9e2b 100644 --- a/app/assets/javascripts/monitoring/stores/mutations.js +++ b/app/assets/javascripts/monitoring/stores/mutations.js @@ -67,7 +67,6 @@ export default { group.panels.forEach(panel => { panel.metrics.forEach(metric => { if (metric.metric_id === metricId) { - state.metricsWithData.push(metricId); // ensure dates/numbers are correctly formatted for charts const normalizedResults = result.map(normalizeQueryResult); Vue.set(metric, 'result', Object.freeze(normalizedResults)); diff --git a/app/assets/javascripts/monitoring/stores/state.js b/app/assets/javascripts/monitoring/stores/state.js index e3300967022..88f333aeb80 100644 --- a/app/assets/javascripts/monitoring/stores/state.js +++ b/app/assets/javascripts/monitoring/stores/state.js @@ -13,7 +13,6 @@ export default () => ({ }, deploymentData: [], environments: [], - metricsWithData: [], allDashboards: [], currentDashboard: null, projectPath: null, |