Adds download CSV functionality to dropdown in metrics

This MR adds the functionality to download metrics data
as CSV. It also removes the exportMetricsToCsvEnabled feature
flag which was used before the dropdown was implemented to
display a button.
This commit is contained in:
Laura Montemayor 2019-08-14 22:57:33 +00:00 committed by Clement Ho
parent 0b43c1027f
commit 9e75711aea
12 changed files with 83 additions and 73 deletions

View file

@ -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

View file

@ -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}`"

View file

@ -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>

View file

@ -13,7 +13,6 @@ export default (props = {}) => {
prometheusEndpointEnabled: gon.features.environmentMetricsUsePrometheusEndpoint,
multipleDashboardsEnabled: gon.features.environmentMetricsShowMultipleDashboards,
additionalPanelTypesEnabled: gon.features.environmentMetricsAdditionalPanelTypes,
exportMetricsToCsvEnabled: gon.features.exportMetricsToCsvEnabled,
});
}

View file

@ -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) => {

View file

@ -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';

View file

@ -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;
},
};

View file

@ -10,7 +10,6 @@ export default () => ({
useDashboardEndpoint: false,
multipleDashboardsEnabled: false,
additionalPanelTypesEnabled: false,
exportMetricsToCsvEnabled: false,
emptyState: 'gettingStarted',
showEmptyState: true,
showErrorBanner: true,

View file

@ -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

View file

@ -0,0 +1,5 @@
---
title: 'feat: adds a download to csv functionality to the dropdown in prometheus metrics'
merge_request: 31679
author:
type: changed

View file

@ -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:');
});
});
});
});

View file

@ -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:`);
});
});
});
});