Merge branch '64950-move-download-csv-button-functionality-in-metrics-dashboard-cards-into-the-dropdown' into 'master'
Move download CSV functionality to dropdown in metrics Closes #64950 See merge request gitlab-org/gitlab-ce!31679
This commit is contained in:
commit
c8dfb11b28
12 changed files with 83 additions and 73 deletions
|
@ -1,7 +1,6 @@
|
|||
<script>
|
||||
import { __ } from '~/locale';
|
||||
import { mapState } from 'vuex';
|
||||
import { GlLink, GlButton } from '@gitlab/ui';
|
||||
import { GlLink } from '@gitlab/ui';
|
||||
import { GlAreaChart, GlChartSeriesLabel } from '@gitlab/ui/dist/charts';
|
||||
import dateFormat from 'dateformat';
|
||||
import { debounceByAnimationFrame, roundOffFloat } from '~/lib/utils/common_utils';
|
||||
|
@ -16,7 +15,6 @@ let debouncedResize;
|
|||
export default {
|
||||
components: {
|
||||
GlAreaChart,
|
||||
GlButton,
|
||||
GlChartSeriesLabel,
|
||||
GlLink,
|
||||
Icon,
|
||||
|
@ -69,7 +67,6 @@ export default {
|
|||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState('monitoringDashboard', ['exportMetricsToCsvEnabled']),
|
||||
chartData() {
|
||||
// Transforms & supplements query data to render appropriate labels & styles
|
||||
// Input: [{ queryAttributes1 }, { queryAttributes2 }]
|
||||
|
@ -179,18 +176,6 @@ export default {
|
|||
yAxisLabel() {
|
||||
return `${this.graphData.y_label}`;
|
||||
},
|
||||
csvText() {
|
||||
const chartData = this.chartData[0].data;
|
||||
const header = `timestamp,${this.graphData.y_label}\r\n`; // eslint-disable-line @gitlab/i18n/no-non-i18n-strings
|
||||
return chartData.reduce((csv, data) => {
|
||||
const row = data.join(',');
|
||||
return `${csv}${row}\r\n`;
|
||||
}, header);
|
||||
},
|
||||
downloadLink() {
|
||||
const data = new Blob([this.csvText], { type: 'text/plain' });
|
||||
return window.URL.createObjectURL(data);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
containerWidth: 'onResize',
|
||||
|
@ -259,16 +244,6 @@ export default {
|
|||
<div :class="{ 'prometheus-graph-embed w-100 p-3': showBorder }">
|
||||
<div class="prometheus-graph-header">
|
||||
<h5 ref="graphTitle" class="prometheus-graph-title">{{ graphData.title }}</h5>
|
||||
<gl-button
|
||||
v-if="exportMetricsToCsvEnabled"
|
||||
:href="downloadLink"
|
||||
:title="__('Download CSV')"
|
||||
:aria-label="__('Download CSV')"
|
||||
style="margin-left: 200px;"
|
||||
download="chart_metrics.csv"
|
||||
>
|
||||
{{ __('Download CSV') }}
|
||||
</gl-button>
|
||||
<div ref="graphWidgets" class="prometheus-graph-widgets"><slot></slot></div>
|
||||
</div>
|
||||
<gl-area-chart
|
||||
|
|
|
@ -235,6 +235,19 @@ export default {
|
|||
chart.metrics.some(metric => this.metricsWithData.includes(metric.metric_id)),
|
||||
);
|
||||
},
|
||||
csvText(graphData) {
|
||||
const chartData = graphData.queries[0].result[0].values;
|
||||
const yLabel = graphData.y_label;
|
||||
const header = `timestamp,${yLabel}\r\n`; // eslint-disable-line @gitlab/i18n/no-non-i18n-strings
|
||||
return chartData.reduce((csv, data) => {
|
||||
const row = data.join(',');
|
||||
return `${csv}${row}\r\n`;
|
||||
}, header);
|
||||
},
|
||||
downloadCsv(graphData) {
|
||||
const data = new Blob([this.csvText(graphData)], { type: 'text/plain' });
|
||||
return window.URL.createObjectURL(data);
|
||||
},
|
||||
// TODO: BEGIN, Duplicated code with panel_type until feature flag is removed
|
||||
// Issue number: https://gitlab.com/gitlab-org/gitlab-ce/issues/63845
|
||||
getGraphAlerts(queries) {
|
||||
|
@ -448,7 +461,6 @@ export default {
|
|||
@setAlerts="setAlerts"
|
||||
/>
|
||||
<gl-dropdown
|
||||
v-if="alertWidgetAvailable"
|
||||
v-gl-tooltip
|
||||
class="mx-2"
|
||||
toggle-class="btn btn-transparent border-0"
|
||||
|
@ -459,6 +471,9 @@ export default {
|
|||
<template slot="button-content">
|
||||
<icon name="ellipsis_v" class="text-secondary" />
|
||||
</template>
|
||||
<gl-dropdown-item :href="downloadCsv(graphData)" download="chart_metrics.csv">
|
||||
{{ __('Download CSV') }}
|
||||
</gl-dropdown-item>
|
||||
<gl-dropdown-item
|
||||
v-if="alertWidgetAvailable"
|
||||
v-gl-modal="`alert-modal-${index}-${graphIndex}`"
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
<script>
|
||||
import { mapState } from 'vuex';
|
||||
import _ from 'underscore';
|
||||
import { GlDropdown, GlDropdownItem, GlModal, GlModalDirective } from '@gitlab/ui';
|
||||
import {
|
||||
GlDropdown,
|
||||
GlDropdownItem,
|
||||
GlModal,
|
||||
GlModalDirective,
|
||||
GlTooltipDirective,
|
||||
} from '@gitlab/ui';
|
||||
import Icon from '~/vue_shared/components/icon.vue';
|
||||
import MonitorAreaChart from './charts/area.vue';
|
||||
import MonitorSingleStatChart from './charts/single_stat.vue';
|
||||
import MonitorEmptyChart from './charts/empty_chart.vue';
|
||||
|
@ -11,12 +18,14 @@ export default {
|
|||
MonitorAreaChart,
|
||||
MonitorSingleStatChart,
|
||||
MonitorEmptyChart,
|
||||
Icon,
|
||||
GlDropdown,
|
||||
GlDropdownItem,
|
||||
GlModal,
|
||||
},
|
||||
directives: {
|
||||
GlModal: GlModalDirective,
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
props: {
|
||||
graphData: {
|
||||
|
@ -41,6 +50,19 @@ export default {
|
|||
graphDataHasMetrics() {
|
||||
return this.graphData.queries[0].result.length > 0;
|
||||
},
|
||||
csvText() {
|
||||
const chartData = this.graphData.queries[0].result[0].values;
|
||||
const yLabel = this.graphData.y_label;
|
||||
const header = `timestamp,${yLabel}\r\n`; // eslint-disable-line @gitlab/i18n/no-non-i18n-strings
|
||||
return chartData.reduce((csv, data) => {
|
||||
const row = data.join(',');
|
||||
return `${csv}${row}\r\n`;
|
||||
}, header);
|
||||
},
|
||||
downloadCsv() {
|
||||
const data = new Blob([this.csvText], { type: 'text/plain' });
|
||||
return window.URL.createObjectURL(data);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getGraphAlerts(queries) {
|
||||
|
@ -81,7 +103,6 @@ export default {
|
|||
@setAlerts="setAlerts"
|
||||
/>
|
||||
<gl-dropdown
|
||||
v-if="alertWidgetAvailable"
|
||||
v-gl-tooltip
|
||||
class="mx-2"
|
||||
toggle-class="btn btn-transparent border-0"
|
||||
|
@ -92,6 +113,9 @@ export default {
|
|||
<template slot="button-content">
|
||||
<icon name="ellipsis_v" class="text-secondary" />
|
||||
</template>
|
||||
<gl-dropdown-item :href="downloadCsv" download="chart_metrics.csv">
|
||||
{{ __('Download CSV') }}
|
||||
</gl-dropdown-item>
|
||||
<gl-dropdown-item v-if="alertWidgetAvailable" v-gl-modal="`alert-modal-${index}`">
|
||||
{{ __('Alerts') }}
|
||||
</gl-dropdown-item>
|
||||
|
|
|
@ -13,7 +13,6 @@ export default (props = {}) => {
|
|||
prometheusEndpointEnabled: gon.features.environmentMetricsUsePrometheusEndpoint,
|
||||
multipleDashboardsEnabled: gon.features.environmentMetricsShowMultipleDashboards,
|
||||
additionalPanelTypesEnabled: gon.features.environmentMetricsAdditionalPanelTypes,
|
||||
exportMetricsToCsvEnabled: gon.features.exportMetricsToCsvEnabled,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -37,17 +37,11 @@ export const setEndpoints = ({ commit }, endpoints) => {
|
|||
|
||||
export const setFeatureFlags = (
|
||||
{ commit },
|
||||
{
|
||||
prometheusEndpointEnabled,
|
||||
multipleDashboardsEnabled,
|
||||
additionalPanelTypesEnabled,
|
||||
exportMetricsToCsvEnabled,
|
||||
},
|
||||
{ prometheusEndpointEnabled, multipleDashboardsEnabled, additionalPanelTypesEnabled },
|
||||
) => {
|
||||
commit(types.SET_DASHBOARD_ENABLED, prometheusEndpointEnabled);
|
||||
commit(types.SET_MULTIPLE_DASHBOARDS_ENABLED, multipleDashboardsEnabled);
|
||||
commit(types.SET_ADDITIONAL_PANEL_TYPES_ENABLED, additionalPanelTypesEnabled);
|
||||
commit(types.SET_EXPORT_METRICS_TO_CSV_ENABLED, exportMetricsToCsvEnabled);
|
||||
};
|
||||
|
||||
export const setShowErrorBanner = ({ commit }, enabled) => {
|
||||
|
|
|
@ -17,4 +17,3 @@ export const SET_ENDPOINTS = 'SET_ENDPOINTS';
|
|||
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_SHOW_ERROR_BANNER = 'SET_SHOW_ERROR_BANNER';
|
||||
export const SET_EXPORT_METRICS_TO_CSV_ENABLED = 'SET_EXPORT_METRICS_TO_CSV_ENABLED';
|
||||
|
|
|
@ -99,7 +99,4 @@ export default {
|
|||
[types.SET_SHOW_ERROR_BANNER](state, enabled) {
|
||||
state.showErrorBanner = enabled;
|
||||
},
|
||||
[types.SET_EXPORT_METRICS_TO_CSV_ENABLED](state, enabled) {
|
||||
state.exportMetricsToCsvEnabled = enabled;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -10,7 +10,6 @@ export default () => ({
|
|||
useDashboardEndpoint: false,
|
||||
multipleDashboardsEnabled: false,
|
||||
additionalPanelTypesEnabled: false,
|
||||
exportMetricsToCsvEnabled: false,
|
||||
emptyState: 'gettingStarted',
|
||||
showEmptyState: true,
|
||||
showErrorBanner: true,
|
||||
|
|
|
@ -15,7 +15,6 @@ class Projects::EnvironmentsController < Projects::ApplicationController
|
|||
push_frontend_feature_flag(:environment_metrics_show_multiple_dashboards)
|
||||
push_frontend_feature_flag(:environment_metrics_additional_panel_types)
|
||||
push_frontend_feature_flag(:prometheus_computed_alerts)
|
||||
push_frontend_feature_flag(:export_metrics_to_csv_enabled)
|
||||
end
|
||||
|
||||
def index
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: 'feat: adds a download to csv functionality to the dropdown in prometheus metrics'
|
||||
merge_request: 31679
|
||||
author:
|
||||
type: changed
|
|
@ -24,7 +24,6 @@ describe('Area component', () => {
|
|||
store.commit(`monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`, MonitoringMock.data);
|
||||
store.commit(`monitoringDashboard/${types.RECEIVE_DEPLOYMENTS_DATA_SUCCESS}`, deploymentData);
|
||||
|
||||
store.dispatch('monitoringDashboard/setFeatureFlags', { exportMetricsToCsvEnabled: true });
|
||||
[mockGraphData] = store.state.monitoringDashboard.groups[0].metrics;
|
||||
|
||||
areaChart = shallowMount(Area, {
|
||||
|
@ -109,16 +108,6 @@ describe('Area component', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('when exportMetricsToCsvEnabled is disabled', () => {
|
||||
beforeEach(() => {
|
||||
store.dispatch('monitoringDashboard/setFeatureFlags', { exportMetricsToCsvEnabled: false });
|
||||
});
|
||||
|
||||
it('does not render the Download CSV button', () => {
|
||||
expect(areaChart.contains('glbutton-stub')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('methods', () => {
|
||||
describe('formatTooltipText', () => {
|
||||
const mockDate = deploymentData[0].created_at;
|
||||
|
@ -264,23 +253,5 @@ describe('Area component', () => {
|
|||
expect(areaChart.vm.yAxisLabel).toBe('CPU');
|
||||
});
|
||||
});
|
||||
|
||||
describe('csvText', () => {
|
||||
it('converts data from json to csv', () => {
|
||||
const header = `timestamp,${mockGraphData.y_label}`;
|
||||
const data = mockGraphData.queries[0].result[0].values;
|
||||
const firstRow = `${data[0][0]},${data[0][1]}`;
|
||||
|
||||
expect(areaChart.vm.csvText).toMatch(`^${header}\r\n${firstRow}`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('downloadLink', () => {
|
||||
it('produces a link to download metrics as csv', () => {
|
||||
const link = areaChart.vm.downloadLink;
|
||||
|
||||
expect(link).toContain('blob:');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@ import { timeWindows, timeWindowsKeyNames } from '~/monitoring/constants';
|
|||
import * as types from '~/monitoring/stores/mutation_types';
|
||||
import { createStore } from '~/monitoring/stores';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import {
|
||||
import MonitoringMock, {
|
||||
metricsGroupsAPIResponse,
|
||||
mockApiEndpoint,
|
||||
environmentData,
|
||||
|
@ -40,6 +40,7 @@ describe('Dashboard', () => {
|
|||
let mock;
|
||||
let store;
|
||||
let component;
|
||||
let mockGraphData;
|
||||
|
||||
beforeEach(() => {
|
||||
setFixtures(`
|
||||
|
@ -482,4 +483,36 @@ describe('Dashboard', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when downloading metrics data as CSV', () => {
|
||||
beforeEach(() => {
|
||||
component = new DashboardComponent({
|
||||
propsData: {
|
||||
...propsData,
|
||||
},
|
||||
store,
|
||||
});
|
||||
store.commit(
|
||||
`monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
|
||||
MonitoringMock.data,
|
||||
);
|
||||
[mockGraphData] = component.$store.state.monitoringDashboard.groups[0].metrics;
|
||||
});
|
||||
|
||||
describe('csvText', () => {
|
||||
it('converts metrics data from json to csv', () => {
|
||||
const header = `timestamp,${mockGraphData.y_label}`;
|
||||
const data = mockGraphData.queries[0].result[0].values;
|
||||
const firstRow = `${data[0][0]},${data[0][1]}`;
|
||||
|
||||
expect(component.csvText(mockGraphData)).toMatch(`^${header}\r\n${firstRow}`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('downloadCsv', () => {
|
||||
it('produces a link with a Blob', () => {
|
||||
expect(component.downloadCsv(mockGraphData)).toContain(`blob:`);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue