From f21258035f3bdbdab002f4701ff0577a180dad56 Mon Sep 17 00:00:00 2001 From: Jose Vargas Date: Wed, 28 Aug 2019 17:18:03 -0500 Subject: Add heatmap chart support This adds the support to add heatmap charts to the monitoring dashboard, via the use of the custom dashboards feature --- .../monitoring/components/charts/heatmap.vue | 114 +++++++++++++++++++++ .../monitoring/components/panel_type.vue | 7 ++ .../jivanvl-add-support-heatmap-charts.yml | 5 + spec/javascripts/monitoring/charts/heatmap_spec.js | 75 ++++++++++++++ spec/javascripts/monitoring/mock_data.js | 79 ++++++++++++++ 5 files changed, 280 insertions(+) create mode 100644 app/assets/javascripts/monitoring/components/charts/heatmap.vue create mode 100644 changelogs/unreleased/jivanvl-add-support-heatmap-charts.yml create mode 100644 spec/javascripts/monitoring/charts/heatmap_spec.js diff --git a/app/assets/javascripts/monitoring/components/charts/heatmap.vue b/app/assets/javascripts/monitoring/components/charts/heatmap.vue new file mode 100644 index 00000000000..c5d200c7488 --- /dev/null +++ b/app/assets/javascripts/monitoring/components/charts/heatmap.vue @@ -0,0 +1,114 @@ + + diff --git a/app/assets/javascripts/monitoring/components/panel_type.vue b/app/assets/javascripts/monitoring/components/panel_type.vue index 73ff651d510..0a783bd5290 100644 --- a/app/assets/javascripts/monitoring/components/panel_type.vue +++ b/app/assets/javascripts/monitoring/components/panel_type.vue @@ -12,12 +12,14 @@ import { import Icon from '~/vue_shared/components/icon.vue'; import MonitorTimeSeriesChart from './charts/time_series.vue'; import MonitorSingleStatChart from './charts/single_stat.vue'; +import MonitorHeatmapChart from './charts/heatmap.vue'; import MonitorEmptyChart from './charts/empty_chart.vue'; export default { components: { MonitorSingleStatChart, MonitorTimeSeriesChart, + MonitorHeatmapChart, MonitorEmptyChart, Icon, GlDropdown, @@ -92,6 +94,11 @@ export default { v-if="isPanelType('single-stat') && graphDataHasMetrics" :graph-data="graphData" /> + { + let heatmapChart; + let store; + + beforeEach(() => { + heatmapChart = shallowMount(Heatmap, { + propsData: { + graphData: graphDataPrometheusQueryRangeMultiTrack, + containerWidth: 100, + }, + store, + }); + }); + + afterEach(() => { + heatmapChart.destroy(); + }); + + describe('wrapped components', () => { + describe('GitLab UI heatmap chart', () => { + let glHeatmapChart; + + beforeEach(() => { + glHeatmapChart = heatmapChart.find(GlHeatmap); + }); + + it('is a Vue instance', () => { + expect(glHeatmapChart.isVueInstance()).toBe(true); + }); + + it('should display a label on the x axis', () => { + expect(heatmapChart.vm.xAxisName).toBe(graphDataPrometheusQueryRangeMultiTrack.x_label); + }); + + it('should display a label on the y axis', () => { + expect(heatmapChart.vm.yAxisName).toBe(graphDataPrometheusQueryRangeMultiTrack.y_label); + }); + + // According to the echarts docs https://echarts.apache.org/en/option.html#series-heatmap.data + // each row of the heatmap chart is represented by an array inside another parent array + // e.g. [[0, 0, 10]], the format represents the column, the row and finally the value + // corresponding to the cell + + it('should return chartData with a length of x by y, with a length of 3 per array', () => { + const row = heatmapChart.vm.chartData[0]; + const queryResult = graphDataPrometheusQueryRangeMultiTrack.queries[0].result; + const xDim = queryResult.length; + const yDim = queryResult[0].values.length; + const totalLengthArray = xDim * yDim; + + expect(row.length).toBe(3); + expect(heatmapChart.vm.chartData.length).toBe(totalLengthArray); + }); + + it('returns a series of labels for the x axis', () => { + const { xAxisLabels } = heatmapChart.vm; + const queryResult = graphDataPrometheusQueryRangeMultiTrack.queries[0].result; + + expect(xAxisLabels.length).toBe(queryResult.length); + }); + + it('returns a series of labels for the y axis', () => { + const { yAxisLabels } = heatmapChart.vm; + const queryResult = graphDataPrometheusQueryRangeMultiTrack.queries[0].result; + + expect(yAxisLabels.length).toBe(queryResult[0].values.length); + }); + }); + }); +}); diff --git a/spec/javascripts/monitoring/mock_data.js b/spec/javascripts/monitoring/mock_data.js index 17e7314e214..175eb6365aa 100644 --- a/spec/javascripts/monitoring/mock_data.js +++ b/spec/javascripts/monitoring/mock_data.js @@ -1009,3 +1009,82 @@ export const graphDataPrometheusQueryRange = { }, ], }; + +export const graphDataPrometheusQueryRangeMultiTrack = { + title: 'Super Chart A3', + type: 'heatmap', + weight: 3, + x_label: 'Status Code', + y_label: 'Time', + metrics: [], + queries: [ + { + metricId: '1', + id: 'response_metrics_nginx_ingress_throughput_status_code', + query_range: + 'sum(rate(nginx_upstream_responses_total{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[60m])) by (status_code)', + unit: 'req / sec', + label: 'Status Code', + metric_id: 1, + prometheus_endpoint_path: + '/root/rails_nodb/environments/3/prometheus/api/v1/query_range?query=sum%28rate%28nginx_upstream_responses_total%7Bupstream%3D~%22%25%7Bkube_namespace%7D-%25%7Bci_environment_slug%7D-.%2A%22%7D%5B2m%5D%29%29+by+%28status_code%29', + result: [ + { + metric: { status_code: '1xx' }, + values: [ + ['2019-08-30T15:00:00.000Z', 0], + ['2019-08-30T16:00:00.000Z', 2], + ['2019-08-30T17:00:00.000Z', 0], + ['2019-08-30T18:00:00.000Z', 0], + ['2019-08-30T19:00:00.000Z', 0], + ['2019-08-30T20:00:00.000Z', 3], + ], + }, + { + metric: { status_code: '2xx' }, + values: [ + ['2019-08-30T15:00:00.000Z', 1], + ['2019-08-30T16:00:00.000Z', 3], + ['2019-08-30T17:00:00.000Z', 6], + ['2019-08-30T18:00:00.000Z', 10], + ['2019-08-30T19:00:00.000Z', 8], + ['2019-08-30T20:00:00.000Z', 6], + ], + }, + { + metric: { status_code: '3xx' }, + values: [ + ['2019-08-30T15:00:00.000Z', 1], + ['2019-08-30T16:00:00.000Z', 2], + ['2019-08-30T17:00:00.000Z', 3], + ['2019-08-30T18:00:00.000Z', 3], + ['2019-08-30T19:00:00.000Z', 2], + ['2019-08-30T20:00:00.000Z', 1], + ], + }, + { + metric: { status_code: '4xx' }, + values: [ + ['2019-08-30T15:00:00.000Z', 2], + ['2019-08-30T16:00:00.000Z', 0], + ['2019-08-30T17:00:00.000Z', 0], + ['2019-08-30T18:00:00.000Z', 2], + ['2019-08-30T19:00:00.000Z', 0], + ['2019-08-30T20:00:00.000Z', 2], + ], + }, + { + metric: { status_code: '5xx' }, + values: [ + ['2019-08-30T15:00:00.000Z', 0], + ['2019-08-30T16:00:00.000Z', 1], + ['2019-08-30T17:00:00.000Z', 0], + ['2019-08-30T18:00:00.000Z', 0], + ['2019-08-30T19:00:00.000Z', 0], + ['2019-08-30T20:00:00.000Z', 2], + ], + }, + ], + }, + ], +}; -- cgit v1.2.1