debian-mirror-gitlab/app/assets/javascripts/monitoring/components/dashboard.vue

422 lines
12 KiB
Vue
Raw Normal View History

2018-03-17 18:26:18 +05:30
<script>
2019-09-04 21:01:54 +05:30
import { GlButton, GlDropdown, GlDropdownItem, GlModal, GlModalDirective } from '@gitlab/ui';
2019-07-07 11:18:12 +05:30
import _ from 'underscore';
2019-09-04 21:01:54 +05:30
import { mapActions, mapState } from 'vuex';
2018-11-08 19:23:39 +05:30
import { s__ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
2019-07-31 22:56:46 +05:30
import { getParameterValues } from '~/lib/utils/url_utility';
2019-09-04 21:01:54 +05:30
import invalidUrl from '~/lib/utils/invalid_url';
2019-02-15 15:39:39 +05:30
import MonitorAreaChart from './charts/area.vue';
2019-09-30 21:07:59 +05:30
import MonitorSingleStatChart from './charts/single_stat.vue';
import PanelType from './panel_type.vue';
2018-05-09 12:01:36 +05:30
import GraphGroup from './graph_group.vue';
import EmptyState from './empty_state.vue';
2019-07-31 22:56:46 +05:30
import { timeWindows, timeWindowsKeyNames } from '../constants';
2019-07-07 11:18:12 +05:30
import { getTimeDiff } from '../utils';
2019-03-02 22:35:43 +05:30
const sidebarAnimationDuration = 150;
let sidebarMutationObserver;
2018-03-17 18:26:18 +05:30
2018-05-09 12:01:36 +05:30
export default {
components: {
2019-02-15 15:39:39 +05:30
MonitorAreaChart,
2019-09-30 21:07:59 +05:30
MonitorSingleStatChart,
PanelType,
2018-05-09 12:01:36 +05:30
GraphGroup,
EmptyState,
2018-11-08 19:23:39 +05:30
Icon,
2019-07-31 22:56:46 +05:30
GlButton,
2019-07-07 11:18:12 +05:30
GlDropdown,
GlDropdownItem,
2019-07-31 22:56:46 +05:30
GlModal,
},
directives: {
GlModalDirective,
2018-05-09 12:01:36 +05:30
},
props: {
2019-09-04 21:01:54 +05:30
externalDashboardUrl: {
2019-07-31 22:56:46 +05:30
type: String,
required: false,
default: '',
},
2018-05-09 12:01:36 +05:30
hasMetrics: {
type: Boolean,
required: false,
default: true,
2018-03-17 18:26:18 +05:30
},
2018-05-09 12:01:36 +05:30
showPanels: {
type: Boolean,
required: false,
default: true,
2018-03-17 18:26:18 +05:30
},
2018-05-09 12:01:36 +05:30
documentationPath: {
type: String,
required: true,
2018-03-17 18:26:18 +05:30
},
2018-05-09 12:01:36 +05:30
settingsPath: {
type: String,
required: true,
},
clustersPath: {
type: String,
required: true,
},
tagsPath: {
type: String,
required: true,
},
projectPath: {
type: String,
required: true,
},
metricsEndpoint: {
type: String,
required: true,
},
2019-09-04 21:01:54 +05:30
deploymentsEndpoint: {
2018-05-09 12:01:36 +05:30
type: String,
required: false,
default: null,
},
emptyGettingStartedSvgPath: {
type: String,
required: true,
},
emptyLoadingSvgPath: {
type: String,
required: true,
},
emptyNoDataSvgPath: {
type: String,
required: true,
},
emptyUnableToConnectSvgPath: {
type: String,
required: true,
},
2018-11-08 19:23:39 +05:30
environmentsEndpoint: {
type: String,
required: true,
},
currentEnvironmentName: {
type: String,
required: true,
},
2019-07-31 22:56:46 +05:30
customMetricsAvailable: {
type: Boolean,
required: false,
default: false,
},
customMetricsPath: {
type: String,
2019-09-30 21:07:59 +05:30
required: false,
default: invalidUrl,
2019-07-31 22:56:46 +05:30
},
validateQueryPath: {
type: String,
2019-09-30 21:07:59 +05:30
required: false,
default: invalidUrl,
2019-07-31 22:56:46 +05:30
},
2019-09-04 21:01:54 +05:30
dashboardEndpoint: {
type: String,
required: false,
default: invalidUrl,
},
2019-09-30 21:07:59 +05:30
currentDashboard: {
type: String,
required: false,
default: '',
},
smallEmptyState: {
type: Boolean,
required: false,
default: false,
},
2018-05-09 12:01:36 +05:30
},
data() {
return {
state: 'gettingStarted',
2018-12-05 23:21:45 +05:30
elWidth: 0,
2019-07-07 11:18:12 +05:30
selectedTimeWindow: '',
2019-07-31 22:56:46 +05:30
selectedTimeWindowKey: '',
formIsValid: null,
2018-05-09 12:01:36 +05:30
};
},
2019-07-31 22:56:46 +05:30
computed: {
canAddMetrics() {
return this.customMetricsAvailable && this.customMetricsPath.length;
},
2019-09-04 21:01:54 +05:30
...mapState('monitoringDashboard', [
'groups',
'emptyState',
'showEmptyState',
'environments',
'deploymentData',
'metricsWithData',
'useDashboardEndpoint',
2019-09-30 21:07:59 +05:30
'allDashboards',
'multipleDashboardsEnabled',
'additionalPanelTypesEnabled',
2019-09-04 21:01:54 +05:30
]),
groupsWithData() {
return this.groups.filter(group => this.chartsWithData(group.metrics).length > 0);
},
2019-09-30 21:07:59 +05:30
selectedDashboardText() {
return this.currentDashboard || (this.allDashboards[0] && this.allDashboards[0].display_name);
},
addingMetricsAvailable() {
return IS_EE && this.canAddMetrics && !this.showEmptyState;
},
alertWidgetAvailable() {
return IS_EE && this.prometheusAlertsAvailable && this.alertsEndpoint;
},
2019-07-31 22:56:46 +05:30
},
2018-05-09 12:01:36 +05:30
created() {
2019-09-04 21:01:54 +05:30
this.setEndpoints({
2018-05-09 12:01:36 +05:30
metricsEndpoint: this.metricsEndpoint,
2018-11-08 19:23:39 +05:30
environmentsEndpoint: this.environmentsEndpoint,
2019-09-04 21:01:54 +05:30
deploymentsEndpoint: this.deploymentsEndpoint,
dashboardEndpoint: this.dashboardEndpoint,
2019-09-30 21:07:59 +05:30
currentDashboard: this.currentDashboard,
projectPath: this.projectPath,
2018-05-09 12:01:36 +05:30
});
2019-09-04 21:01:54 +05:30
2019-07-07 11:18:12 +05:30
this.timeWindows = timeWindows;
2019-07-31 22:56:46 +05:30
this.selectedTimeWindowKey =
_.escape(getParameterValues('time_window')[0]) || timeWindowsKeyNames.eightHours;
// Set default time window if the selectedTimeWindowKey is bogus
if (!Object.keys(this.timeWindows).includes(this.selectedTimeWindowKey)) {
this.selectedTimeWindowKey = timeWindowsKeyNames.eightHours;
}
this.selectedTimeWindow = this.timeWindows[this.selectedTimeWindowKey];
2018-05-09 12:01:36 +05:30
},
beforeDestroy() {
2019-03-02 22:35:43 +05:30
if (sidebarMutationObserver) {
sidebarMutationObserver.disconnect();
}
2018-05-09 12:01:36 +05:30
},
mounted() {
if (!this.hasMetrics) {
2019-09-04 21:01:54 +05:30
this.setGettingStartedEmptyState();
2018-05-09 12:01:36 +05:30
} else {
2019-09-04 21:01:54 +05:30
this.fetchData(getTimeDiff(this.selectedTimeWindow));
2019-03-02 22:35:43 +05:30
sidebarMutationObserver = new MutationObserver(this.onSidebarMutation);
sidebarMutationObserver.observe(document.querySelector('.layout-page'), {
attributes: true,
childList: false,
subtree: false,
});
2018-05-09 12:01:36 +05:30
}
},
methods: {
2019-09-04 21:01:54 +05:30
...mapActions('monitoringDashboard', [
'fetchData',
'setGettingStartedEmptyState',
'setEndpoints',
'setDashboardEnabled',
]),
chartsWithData(charts) {
if (!this.useDashboardEndpoint) {
return charts;
}
return charts.filter(chart =>
chart.metrics.some(metric => this.metricsWithData.includes(metric.metric_id)),
);
},
2019-09-30 21:07:59 +05:30
// TODO: BEGIN, Duplicated code with panel_type until feature flag is removed
// Issue number: https://gitlab.com/gitlab-org/gitlab-ce/issues/63845
2019-07-07 11:18:12 +05:30
getGraphAlerts(queries) {
if (!this.allAlerts) return {};
const metricIdsForChart = queries.map(q => q.metricId);
return _.pick(this.allAlerts, alert => metricIdsForChart.includes(alert.metricId));
},
getGraphAlertValues(queries) {
return Object.values(this.getGraphAlerts(queries));
2019-02-15 15:39:39 +05:30
},
2019-09-30 21:07:59 +05:30
// TODO: END
2019-07-31 22:56:46 +05:30
hideAddMetricModal() {
this.$refs.addMetricModal.hide();
2019-07-07 11:18:12 +05:30
},
2019-03-02 22:35:43 +05:30
onSidebarMutation() {
setTimeout(() => {
this.elWidth = this.$el.clientWidth;
}, sidebarAnimationDuration);
2018-03-17 18:26:18 +05:30
},
2019-07-31 22:56:46 +05:30
setFormValidity(isValid) {
this.formIsValid = isValid;
},
submitCustomMetricsForm() {
this.$refs.customMetricsForm.submit();
},
2019-07-07 11:18:12 +05:30
activeTimeWindow(key) {
return this.timeWindows[key] === this.selectedTimeWindow;
},
2019-07-31 22:56:46 +05:30
setTimeWindowParameter(key) {
return `?time_window=${key}`;
},
},
addMetric: {
title: s__('Metrics|Add metric'),
modalId: 'add-metric',
2018-05-09 12:01:36 +05:30
},
};
2018-03-17 18:26:18 +05:30
</script>
<template>
2019-09-30 21:07:59 +05:30
<div class="prometheus-graphs">
2019-07-31 22:56:46 +05:30
<div class="gl-p-3 border-bottom bg-gray-light d-flex justify-content-between">
<div
v-if="environmentsEndpoint"
class="dropdowns d-flex align-items-center justify-content-between"
>
2019-09-30 21:07:59 +05:30
<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>
2019-07-31 22:56:46 +05:30
<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"
2019-09-04 21:01:54 +05:30
:disabled="environments.length === 0"
2019-07-07 11:18:12 +05:30
>
2019-07-31 22:56:46 +05:30
<gl-dropdown-item
2019-09-04 21:01:54 +05:30
v-for="environment in environments"
2019-07-31 22:56:46 +05:30
:key="environment.id"
:active="environment.name === currentEnvironmentName"
active-class="is-active"
2019-09-30 21:07:59 +05:30
:href="environment.metrics_path"
2019-07-31 22:56:46 +05:30
>{{ environment.name }}</gl-dropdown-item
>
</gl-dropdown>
</div>
2019-09-30 21:07:59 +05:30
<div v-if="!showEmptyState" class="d-flex align-items-center prepend-left-8">
2019-07-31 22:56:46 +05:30
<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-item
v-for="(value, key) in timeWindows"
:key="key"
:active="activeTimeWindow(key)"
2019-09-04 21:01:54 +05:30
:href="setTimeWindowParameter(key)"
active-class="active"
>{{ value }}</gl-dropdown-item
2019-07-31 22:56:46 +05:30
>
</gl-dropdown>
</div>
2019-07-07 11:18:12 +05:30
</div>
2019-07-31 22:56:46 +05:30
<div class="d-flex">
2019-09-30 21:07:59 +05:30
<div v-if="addingMetricsAvailable">
2019-07-31 22:56:46 +05:30
<gl-button
v-gl-modal-directive="$options.addMetric.modalId"
class="js-add-metric-button text-success border-success"
2019-09-04 21:01:54 +05:30
>{{ $options.addMetric.title }}</gl-button
2019-07-07 11:18:12 +05:30
>
2019-07-31 22:56:46 +05:30
<gl-modal
ref="addMetricModal"
:modal-id="$options.addMetric.modalId"
:title="$options.addMetric.title"
>
<form ref="customMetricsForm" :action="customMetricsPath" method="post">
<custom-metrics-form-fields
:validate-query-path="validateQueryPath"
form-operation="post"
@formValidation="setFormValidity"
/>
</form>
<div slot="modal-footer">
2019-09-04 21:01:54 +05:30
<gl-button @click="hideAddMetricModal">{{ __('Cancel') }}</gl-button>
2019-07-31 22:56:46 +05:30
<gl-button
:disabled="!formIsValid"
variant="success"
@click="submitCustomMetricsForm"
2019-09-04 21:01:54 +05:30
>{{ __('Save changes') }}</gl-button
2019-07-31 22:56:46 +05:30
>
</div>
</gl-modal>
</div>
<gl-button
2019-09-04 21:01:54 +05:30
v-if="externalDashboardUrl.length"
2019-07-31 22:56:46 +05:30
class="js-external-dashboard-link prepend-left-8"
variant="primary"
2019-09-04 21:01:54 +05:30
:href="externalDashboardUrl"
target="_blank"
2019-07-31 22:56:46 +05:30
>
{{ __('View full dashboard') }}
<icon name="external-link" />
</gl-button>
2018-11-08 19:23:39 +05:30
</div>
</div>
2019-09-30 21:07:59 +05:30
<div v-if="!showEmptyState">
<graph-group
v-for="(groupData, index) in groupsWithData"
:key="index"
:name="groupData.group"
:show-panels="showPanels"
2019-07-07 11:18:12 +05:30
>
2019-09-30 21:07:59 +05:30
<template v-if="additionalPanelTypesEnabled">
<panel-type
v-for="(graphData, graphIndex) in chartsWithData(groupData.metrics)"
:key="`panel-type-${graphIndex}`"
:graph-data="graphData"
:dashboard-width="elWidth"
/>
</template>
<template v-else>
<monitor-area-chart
v-for="(graphData, graphIndex) in chartsWithData(groupData.metrics)"
:key="graphIndex"
:graph-data="graphData"
:deployment-data="deploymentData"
:thresholds="getGraphAlertValues(graphData.queries)"
:container-width="elWidth"
:project-path="projectPath"
group-id="monitor-area-chart"
>
<alert-widget
v-if="alertWidgetAvailable && graphData"
:alerts-endpoint="alertsEndpoint"
:relevant-queries="graphData.queries"
:alerts-to-manage="getGraphAlerts(graphData.queries)"
@setAlerts="setAlerts"
/>
</monitor-area-chart>
</template>
</graph-group>
</div>
<empty-state
v-else
:selected-state="emptyState"
: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-unable-to-connect-svg-path="emptyUnableToConnectSvgPath"
:compact="smallEmptyState"
/>
2018-03-17 18:26:18 +05:30
</div>
</template>