Commit c992efda authored by Martin Wortschack's avatar Martin Wortschack Committed by Phil Hughes

Improve error message

- This improves the error message
when a query takes too long
to calculate
parent f5ba8e48
---
title: 'Productivity Analytics: Improve error message when query takes too long to calculate'
merge_request: 39074
author:
type: other
......@@ -71,6 +71,7 @@ export default {
...mapGetters(['getMetricTypes']),
...mapGetters('charts', [
'chartLoading',
'chartErrorCode',
'chartHasData',
'getColumnChartData',
'getColumnChartDatazoomOption',
......@@ -199,6 +200,7 @@ export default {
__('You can filter by \'days to merge\' by clicking on the columns in the chart.')
"
:is-loading="chartLoading(chartKeys.main)"
:error-code="chartErrorCode(chartKeys.main)"
:chart-data="getColumnChartData(chartKeys.main)"
>
<gl-column-chart
......
......@@ -3,6 +3,7 @@ import { isEmpty } from 'lodash';
import { GlDeprecatedDropdown, GlDeprecatedDropdownItem, GlLoadingIcon } from '@gitlab/ui';
import { s__ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
import httpStatusCodes from '~/lib/utils/http_status';
export default {
name: 'MetricChart',
......@@ -28,6 +29,11 @@ export default {
required: false,
default: false,
},
errorCode: {
type: Number,
required: false,
default: null,
},
metricTypes: {
type: Array,
required: false,
......@@ -52,9 +58,23 @@ export default {
const foundMetric = this.metricTypes.find(m => m.key === this.selectedMetric);
return foundMetric ? foundMetric.label : s__('MetricChart|Please select a metric');
},
isServerError() {
return this.errorCode === httpStatusCodes.INTERNAL_SERVER_ERROR;
},
hasChartData() {
return !isEmpty(this.chartData);
},
infoMessage() {
if (this.isServerError) {
return s__(
'MetricChart|There is too much data to calculate. Please change your selection.',
);
} else if (!this.hasChartData) {
return s__('MetricChart|There is no data available. Please change your selection.');
}
return null;
},
},
methods: {
isSelectedMetric(key) {
......@@ -68,8 +88,8 @@ export default {
<h5 v-if="title">{{ title }}</h5>
<gl-loading-icon v-if="isLoading" size="md" class="my-4 py-4" />
<template v-else>
<div v-if="!hasChartData" ref="noData" class="bs-callout bs-callout-info">
{{ __('There is no data available. Please change your selection.') }}
<div v-if="infoMessage" data-testid="infoMessage" class="bs-callout bs-callout-info">
{{ infoMessage }}
</div>
<template v-else>
<gl-deprecated-dropdown
......
......@@ -15,6 +15,8 @@ import { getScatterPlotData, getMedianLineData } from '../../../utils';
export const chartLoading = state => chartKey => state.charts[chartKey].isLoading;
export const chartErrorCode = state => chartKey => state.charts[chartKey].errorCode;
/**
* Creates a series object for the column chart with the given chartKey.
*
......
......@@ -6,6 +6,7 @@ exports[`MetricChart component template when isLoading is false and chart data i
<div
class="bs-callout bs-callout-info"
data-testid="infoMessage"
>
There is no data available. Please change your selection.
......
......@@ -19,6 +19,8 @@ import { GlColumnChart } from '@gitlab/ui/dist/charts';
import * as commonUtils from '~/lib/utils/common_utils';
import * as urlUtils from '~/lib/utils/url_utility';
import UrlSyncMixin from 'ee/analytics/shared/mixins/url_sync_mixin';
import MetricChart from 'ee/analytics/productivity_analytics/components/metric_chart.vue';
import httpStatusCodes from '~/lib/utils/http_status';
const localVue = createLocalVue();
localVue.use(Vuex);
......@@ -42,7 +44,7 @@ describe('ProductivityApp component', () => {
const mainChartData = { 1: 2, 2: 3 };
const createComponent = ({ props = {}, scatterplotEnabled = true } = {}) => {
const createComponent = ({ props = {}, options = {}, scatterplotEnabled = true } = {}) => {
wrapper = shallowMount(ProductivityApp, {
localVue,
store,
......@@ -57,6 +59,7 @@ describe('ProductivityApp component', () => {
provide: {
glFeatures: { productivityAnalyticsScatterplotEnabled: scatterplotEnabled },
},
...options,
});
wrapper.vm.$store.dispatch('setEndpoint', TEST_HOST);
......@@ -499,6 +502,42 @@ describe('ProductivityApp component', () => {
expect(findMrTableSection().exists()).toBe(false);
});
});
describe('with a server error', () => {
beforeEach(() => {
createComponent({
options: {
stubs: {
'metric-chart': MetricChart,
},
},
});
wrapper.vm.$store.dispatch('charts/receiveChartDataError', {
chartKey: chartKeys.main,
error: { response: { status: httpStatusCodes.INTERNAL_SERVER_ERROR } },
});
});
it('sets isLoading=false on the metric chart', () => {
expect(findMainMetricChart().props('isLoading')).toBe(false);
});
it('passes a 500 status code to the metric chart', () => {
expect(findMainMetricChart().props('errorCode')).toBe(
httpStatusCodes.INTERNAL_SERVER_ERROR,
);
});
it('does not render any other charts', () => {
expect(findSecondaryChartsSection().exists()).toBe(false);
});
it('renders the proper info message', () => {
expect(findMainMetricChart().text()).toContain(
'There is too much data to calculate. Please change your selection.',
);
});
});
});
});
});
......
......@@ -2,6 +2,7 @@ import { shallowMount } from '@vue/test-utils';
import MetricChart from 'ee/analytics/productivity_analytics/components/metric_chart.vue';
import { GlLoadingIcon, GlDeprecatedDropdown, GlDeprecatedDropdownItem } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
import httpStatusCodes from '~/lib/utils/http_status';
describe('MetricChart component', () => {
let wrapper;
......@@ -37,7 +38,7 @@ describe('MetricChart component', () => {
});
const findLoadingIndicator = () => wrapper.find(GlLoadingIcon);
const findNoDataSection = () => wrapper.find({ ref: 'noData' });
const findInfoMessage = () => wrapper.find('[data-testid="infoMessage"]');
const findMetricDropdown = () => wrapper.find(GlDeprecatedDropdown);
const findMetricDropdownItems = () => findMetricDropdown().findAll(GlDeprecatedDropdownItem);
const findChartSlot = () => wrapper.find({ ref: 'chart' });
......@@ -98,10 +99,22 @@ describe('MetricChart component', () => {
expect(findChartSlot().exists()).toBe(false);
});
it('shows a "no data" info text', () => {
expect(findNoDataSection().text()).toContain(
'There is no data available. Please change your selection.',
);
describe('and there is no error', () => {
it('shows a "no data" info text', () => {
expect(findInfoMessage().text()).toContain(
'There is no data available. Please change your selection.',
);
});
});
describe('and there is a 500 error', () => {
it('shows a "too much data" info text', () => {
factory({ isLoading, chartData: [], errorCode: httpStatusCodes.INTERNAL_SERVER_ERROR });
expect(findInfoMessage().text()).toContain(
'There is too much data to calculate. Please change your selection.',
);
});
});
});
......
......@@ -15125,6 +15125,12 @@ msgstr ""
msgid "MetricChart|Selected"
msgstr ""
msgid "MetricChart|There is no data available. Please change your selection."
msgstr ""
msgid "MetricChart|There is too much data to calculate. Please change your selection."
msgstr ""
msgid "Metrics"
msgstr ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment