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

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

389 lines
11 KiB
Vue
Raw Normal View History

2019-09-30 21:07:59 +05:30
<script>
2019-10-12 21:52:04 +05:30
import {
2020-04-08 14:13:33 +05:30
GlResizeObserverDirective,
2020-04-22 19:07:51 +05:30
GlIcon,
2020-10-24 23:57:45 +05:30
GlLink,
2020-04-22 19:07:51 +05:30
GlLoadingIcon,
2020-11-24 15:15:51 +05:30
GlDropdown,
GlDropdownItem,
GlDropdownDivider,
2019-10-12 21:52:04 +05:30
GlModal,
GlModalDirective,
2020-10-24 23:57:45 +05:30
GlSprintf,
2020-04-08 14:13:33 +05:30
GlTooltip,
2019-10-12 21:52:04 +05:30
GlTooltipDirective,
} from '@gitlab/ui';
2021-03-11 19:13:27 +05:30
import { mapState } from 'vuex';
2020-10-24 23:57:45 +05:30
import { convertToFixedRange } from '~/lib/utils/datetime_range';
2022-08-13 15:12:31 +05:30
import { isSafeURL } from '~/lib/utils/url_utility';
2020-04-08 14:13:33 +05:30
import { __, n__ } from '~/locale';
2021-03-11 19:13:27 +05:30
import TrackEventDirective from '~/vue_shared/directives/track_event';
2020-05-24 23:13:21 +05:30
import { panelTypes } from '../constants';
2021-03-11 19:13:27 +05:30
import { graphDataToCsv } from '../csv_export';
2022-08-13 15:12:31 +05:30
import { downloadCSVOptions, generateLinkToChartOptions } from '../utils';
2019-12-26 22:10:19 +05:30
import MonitorAnomalyChart from './charts/anomaly.vue';
2021-03-11 19:13:27 +05:30
import MonitorBarChart from './charts/bar.vue';
import MonitorColumnChart from './charts/column.vue';
import MonitorEmptyChart from './charts/empty_chart.vue';
2020-10-24 23:57:45 +05:30
import MonitorGaugeChart from './charts/gauge.vue';
2019-12-26 22:10:19 +05:30
import MonitorHeatmapChart from './charts/heatmap.vue';
2021-03-11 19:13:27 +05:30
import MonitorSingleStatChart from './charts/single_stat.vue';
2020-03-13 15:44:24 +05:30
import MonitorStackedColumnChart from './charts/stacked_column.vue';
2021-03-11 19:13:27 +05:30
import MonitorTimeSeriesChart from './charts/time_series.vue';
2019-09-30 21:07:59 +05:30
2020-04-22 19:07:51 +05:30
const events = {
timeRangeZoom: 'timerangezoom',
2020-05-24 23:13:21 +05:30
expand: 'expand',
2020-04-22 19:07:51 +05:30
};
2019-09-30 21:07:59 +05:30
export default {
components: {
2019-10-12 21:52:04 +05:30
MonitorEmptyChart,
2020-04-22 19:07:51 +05:30
GlIcon,
2020-10-24 23:57:45 +05:30
GlLink,
2020-04-22 19:07:51 +05:30
GlLoadingIcon,
2020-04-08 14:13:33 +05:30
GlTooltip,
2019-10-12 21:52:04 +05:30
GlDropdown,
GlDropdownItem,
2020-06-23 00:09:42 +05:30
GlDropdownDivider,
2019-10-12 21:52:04 +05:30
GlModal,
2020-10-24 23:57:45 +05:30
GlSprintf,
2019-10-12 21:52:04 +05:30
},
directives: {
2020-04-08 14:13:33 +05:30
GlResizeObserver: GlResizeObserverDirective,
2019-10-12 21:52:04 +05:30
GlModal: GlModalDirective,
GlTooltip: GlTooltipDirective,
2019-12-21 20:55:43 +05:30
TrackEvent: TrackEventDirective,
2019-09-30 21:07:59 +05:30
},
props: {
2019-10-12 21:52:04 +05:30
clipboardText: {
type: String,
2020-01-01 13:55:28 +05:30
required: false,
default: '',
2019-10-12 21:52:04 +05:30
},
2019-09-30 21:07:59 +05:30
graphData: {
type: Object,
2019-10-12 21:52:04 +05:30
required: false,
2020-05-24 23:13:21 +05:30
default: null,
2019-10-12 21:52:04 +05:30
},
2020-01-01 13:55:28 +05:30
groupId: {
type: String,
required: false,
2020-05-24 23:13:21 +05:30
default: 'dashboard-panel',
2020-01-01 13:55:28 +05:30
},
2020-04-22 19:07:51 +05:30
namespace: {
type: String,
required: false,
default: 'monitoringDashboard',
},
2020-05-24 23:13:21 +05:30
settingsPath: {
type: String,
required: false,
default: null,
},
2019-09-30 21:07:59 +05:30
},
2020-03-13 15:44:24 +05:30
data() {
return {
2020-04-08 14:13:33 +05:30
showTitleTooltip: false,
2020-03-13 15:44:24 +05:30
zoomedTimeRange: null,
2020-05-24 23:13:21 +05:30
expandBtnAvailable: Boolean(this.$listeners[events.expand]),
2020-03-13 15:44:24 +05:30
};
},
2019-09-30 21:07:59 +05:30
computed: {
2020-04-22 19:07:51 +05:30
// Use functions to support dynamic namespaces in mapXXX helpers. Pattern described
// in https://github.com/vuejs/vuex/issues/863#issuecomment-329510765
...mapState({
deploymentData(state) {
return state[this.namespace].deploymentData;
},
annotations(state) {
return state[this.namespace].annotations;
},
projectPath(state) {
return state[this.namespace].projectPath;
},
timeRange(state) {
return state[this.namespace].timeRange;
},
2020-06-23 00:09:42 +05:30
dashboardTimezone(state) {
return state[this.namespace].dashboardTimezone;
},
2020-05-24 23:13:21 +05:30
metricsSavedToDb(state, getters) {
return getters[`${this.namespace}/metricsSavedToDb`];
},
2020-06-23 00:09:42 +05:30
selectedDashboard(state, getters) {
return getters[`${this.namespace}/selectedDashboard`];
},
2020-04-22 19:07:51 +05:30
}),
2020-10-24 23:57:45 +05:30
fixedCurrentTimeRange() {
// convertToFixedRange throws an error if the time range
// is not properly set.
try {
return convertToFixedRange(this.timeRange);
} catch {
return {};
}
},
2020-04-08 14:13:33 +05:30
title() {
2020-05-24 23:13:21 +05:30
return this.graphData?.title || '';
2019-09-30 21:07:59 +05:30
},
2020-04-22 19:07:51 +05:30
graphDataHasResult() {
2020-07-28 23:09:34 +05:30
const metrics = this.graphData?.metrics || [];
return metrics.some(({ result }) => result?.length > 0);
2019-10-12 21:52:04 +05:30
},
2020-04-22 19:07:51 +05:30
graphDataIsLoading() {
2020-05-24 23:13:21 +05:30
const metrics = this.graphData?.metrics || [];
2020-04-22 19:07:51 +05:30
return metrics.some(({ loading }) => loading);
},
2019-10-12 21:52:04 +05:30
csvText() {
2020-10-24 23:57:45 +05:30
if (this.graphData) {
return graphDataToCsv(this.graphData);
}
return null;
2019-10-12 21:52:04 +05:30
},
downloadCsv() {
const data = new Blob([this.csvText], { type: 'text/plain' });
return window.URL.createObjectURL(data);
},
2020-05-24 23:13:21 +05:30
/**
* A chart is "basic" if it doesn't support
* the same features as the TimeSeries based components
* such as "annotations".
*
* @returns Vue Component wrapping a basic visualization
*/
basicChartComponent() {
if (this.isPanelType(panelTypes.SINGLE_STAT)) {
return MonitorSingleStatChart;
}
2020-10-24 23:57:45 +05:30
if (this.isPanelType(panelTypes.GAUGE_CHART)) {
return MonitorGaugeChart;
}
2020-05-24 23:13:21 +05:30
if (this.isPanelType(panelTypes.HEATMAP)) {
return MonitorHeatmapChart;
}
if (this.isPanelType(panelTypes.BAR)) {
return MonitorBarChart;
}
if (this.isPanelType(panelTypes.COLUMN)) {
return MonitorColumnChart;
}
if (this.isPanelType(panelTypes.STACKED_COLUMN)) {
return MonitorStackedColumnChart;
}
if (this.isPanelType(panelTypes.ANOMALY_CHART)) {
return MonitorAnomalyChart;
}
return null;
},
/**
* In monitoring, Time Series charts typically support
* a larger feature set like "annotations", "deployment
2021-12-11 22:18:48 +05:30
* data" and "datazoom".
2020-05-24 23:13:21 +05:30
*
* This is intentional as Time Series are more frequently
* used.
*
* @returns Vue Component wrapping a time series visualization,
* Area Charts are rendered by default.
*/
timeSeriesChartComponent() {
if (this.isPanelType(panelTypes.ANOMALY_CHART)) {
2019-12-26 22:10:19 +05:30
return MonitorAnomalyChart;
}
return MonitorTimeSeriesChart;
},
2020-04-08 14:13:33 +05:30
isContextualMenuShown() {
2020-07-28 23:09:34 +05:30
if (!this.graphDataHasResult) {
return false;
}
// Only a few charts have a contextual menu, support
// for more chart types planned at:
// https://gitlab.com/groups/gitlab-org/-/epics/3573
return (
this.isPanelType(panelTypes.AREA_CHART) ||
this.isPanelType(panelTypes.LINE_CHART) ||
2020-10-24 23:57:45 +05:30
this.isPanelType(panelTypes.SINGLE_STAT) ||
this.isPanelType(panelTypes.GAUGE_CHART)
2020-07-28 23:09:34 +05:30
);
2020-04-08 14:13:33 +05:30
},
editCustomMetricLink() {
2020-05-24 23:13:21 +05:30
if (this.graphData.metrics.length > 1) {
return this.settingsPath;
}
2020-04-08 14:13:33 +05:30
return this.graphData?.metrics[0].edit_path;
},
editCustomMetricLinkText() {
return n__('Metrics|Edit metric', 'Metrics|Edit metrics', this.graphData.metrics.length);
},
2020-05-24 23:13:21 +05:30
hasMetricsInDb() {
const { metrics = [] } = this.graphData;
return metrics.some(({ metricId }) => this.metricsSavedToDb.includes(metricId));
},
2020-04-08 14:13:33 +05:30
},
mounted() {
this.refreshTitleTooltip();
2019-09-30 21:07:59 +05:30
},
methods: {
isPanelType(type) {
2020-05-24 23:13:21 +05:30
return this.graphData?.type === type;
2019-09-30 21:07:59 +05:30
},
2019-10-12 21:52:04 +05:30
showToast() {
2019-12-21 20:55:43 +05:30
this.$toast.show(__('Link copied'));
2019-10-12 21:52:04 +05:30
},
2020-04-08 14:13:33 +05:30
refreshTitleTooltip() {
const { graphTitle } = this.$refs;
this.showTitleTooltip =
Boolean(graphTitle) && graphTitle.scrollWidth > graphTitle.offsetWidth;
},
2019-12-21 20:55:43 +05:30
downloadCSVOptions,
generateLinkToChartOptions,
2020-03-13 15:44:24 +05:30
2020-04-08 14:13:33 +05:30
onResize() {
this.refreshTitleTooltip();
},
2020-03-13 15:44:24 +05:30
onDatazoom({ start, end }) {
this.zoomedTimeRange = { start, end };
2020-04-22 19:07:51 +05:30
this.$emit(events.timeRangeZoom, { start, end });
2020-03-13 15:44:24 +05:30
},
2020-05-24 23:13:21 +05:30
onExpand() {
this.$emit(events.expand);
},
2020-07-28 23:09:34 +05:30
onExpandFromKeyboardShortcut() {
if (this.isContextualMenuShown) {
this.onExpand();
}
},
2020-06-23 00:09:42 +05:30
safeUrl(url) {
return isSafeURL(url) ? url : '#';
},
2020-07-28 23:09:34 +05:30
downloadCsvFromKeyboardShortcut() {
if (this.csvText && this.isContextualMenuShown) {
this.$refs.downloadCsvLink.$el.firstChild.click();
}
},
copyChartLinkFromKeyboardShotcut() {
if (this.clipboardText && this.isContextualMenuShown) {
this.$refs.copyChartLink.$el.firstChild.click();
}
},
2019-09-30 21:07:59 +05:30
},
2020-05-24 23:13:21 +05:30
panelTypes,
2019-09-30 21:07:59 +05:30
};
</script>
<template>
2020-04-08 14:13:33 +05:30
<div v-gl-resize-observer="onResize" class="prometheus-graph">
2020-07-28 23:09:34 +05:30
<div class="d-flex align-items-center">
2021-01-29 00:20:46 +05:30
<slot name="top-left"></slot>
2020-04-08 14:13:33 +05:30
<h5
ref="graphTitle"
2020-06-23 00:09:42 +05:30
class="prometheus-graph-title gl-font-lg font-weight-bold text-truncate gl-mr-3"
2019-10-12 21:52:04 +05:30
>
2020-04-08 14:13:33 +05:30
{{ title }}
</h5>
<gl-tooltip :target="() => $refs.graphTitle" :disabled="!showTitleTooltip">
{{ title }}
</gl-tooltip>
2020-04-22 19:07:51 +05:30
<div class="flex-grow-1"></div>
<div v-if="graphDataIsLoading" class="mx-1 mt-1">
2021-09-30 23:02:18 +05:30
<gl-loading-icon size="sm" />
2020-04-22 19:07:51 +05:30
</div>
2023-03-04 22:38:38 +05:30
<div v-if="isContextualMenuShown" ref="contextualMenu">
2020-07-28 23:09:34 +05:30
<div data-testid="dropdown-wrapper" class="d-flex align-items-center">
2021-02-22 17:27:13 +05:30
<!--
2020-10-24 23:57:45 +05:30
This component should be replaced with a variant developed
as part of https://gitlab.com/gitlab-org/gitlab-ui/-/issues/936
2021-02-22 17:27:13 +05:30
The variant will create a dropdown with an icon, no text and no caret
2020-10-24 23:57:45 +05:30
-->
2020-04-08 14:13:33 +05:30
<gl-dropdown
v-gl-tooltip
2021-10-27 15:23:28 +05:30
icon="ellipsis_v"
:text="__('More actions')"
:text-sr-only="true"
2020-10-24 23:57:45 +05:30
toggle-class="gl-px-3!"
no-caret
2020-04-08 14:13:33 +05:30
right
:title="__('More actions')"
>
2021-10-27 15:23:28 +05:30
<gl-dropdown-item v-if="expandBtnAvailable" ref="expandBtn" @click.prevent="onExpand">
2020-05-24 23:13:21 +05:30
{{ s__('Metrics|Expand panel') }}
</gl-dropdown-item>
2020-04-08 14:13:33 +05:30
<gl-dropdown-item
v-if="editCustomMetricLink"
ref="editMetricLink"
:href="editCustomMetricLink"
>
{{ editCustomMetricLinkText }}
</gl-dropdown-item>
2020-03-13 15:44:24 +05:30
2020-04-08 14:13:33 +05:30
<gl-dropdown-item
v-if="csvText"
ref="downloadCsvLink"
v-track-event="downloadCSVOptions(title)"
:href="downloadCsv"
download="chart_metrics.csv"
>
{{ __('Download CSV') }}
</gl-dropdown-item>
<gl-dropdown-item
v-if="clipboardText"
ref="copyChartLink"
v-track-event="generateLinkToChartOptions(clipboardText)"
:data-clipboard-text="clipboardText"
@click="showToast(clipboardText)"
>
2020-04-22 19:07:51 +05:30
{{ __('Copy link to chart') }}
2020-04-08 14:13:33 +05:30
</gl-dropdown-item>
2020-06-23 00:09:42 +05:30
2020-07-28 23:09:34 +05:30
<template v-if="graphData.links && graphData.links.length">
2020-06-23 00:09:42 +05:30
<gl-dropdown-divider />
<gl-dropdown-item
v-for="(link, index) in graphData.links"
:key="index"
:href="safeUrl(link.url)"
class="text-break"
>{{ link.title }}</gl-dropdown-item
>
</template>
<template v-if="selectedDashboard && selectedDashboard.can_edit">
<gl-dropdown-divider />
<gl-dropdown-item ref="manageLinksItem" :href="selectedDashboard.project_blob_path">{{
s__('Metrics|Manage chart links')
}}</gl-dropdown-item>
</template>
2020-04-08 14:13:33 +05:30
</gl-dropdown>
</div>
</div>
2019-10-12 21:52:04 +05:30
</div>
2020-04-08 14:13:33 +05:30
2020-05-24 23:13:21 +05:30
<monitor-empty-chart v-if="!graphDataHasResult" />
<component
:is="basicChartComponent"
v-else-if="basicChartComponent"
2020-04-08 14:13:33 +05:30
:graph-data="graphData"
2020-06-23 00:09:42 +05:30
:timezone="dashboardTimezone"
2020-05-24 23:13:21 +05:30
v-bind="$attrs"
v-on="$listeners"
2020-04-08 14:13:33 +05:30
/>
<component
2020-05-24 23:13:21 +05:30
:is="timeSeriesChartComponent"
v-else
ref="timeSeriesChart"
2020-04-08 14:13:33 +05:30
:graph-data="graphData"
:deployment-data="deploymentData"
2020-04-22 19:07:51 +05:30
:annotations="annotations"
2020-04-08 14:13:33 +05:30
:project-path="projectPath"
:group-id="groupId"
2020-06-23 00:09:42 +05:30
:timezone="dashboardTimezone"
2020-10-24 23:57:45 +05:30
:time-range="fixedCurrentTimeRange"
2020-05-24 23:13:21 +05:30
v-bind="$attrs"
v-on="$listeners"
2020-04-08 14:13:33 +05:30
@datazoom="onDatazoom"
/>
</div>
2019-09-30 21:07:59 +05:30
</template>