summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/monitoring/components/charts
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/monitoring/components/charts')
-rw-r--r--app/assets/javascripts/monitoring/components/charts/gauge.vue122
-rw-r--r--app/assets/javascripts/monitoring/components/charts/heatmap.vue2
-rw-r--r--app/assets/javascripts/monitoring/components/charts/options.js64
-rw-r--r--app/assets/javascripts/monitoring/components/charts/single_stat.vue2
-rw-r--r--app/assets/javascripts/monitoring/components/charts/time_series.vue17
5 files changed, 202 insertions, 5 deletions
diff --git a/app/assets/javascripts/monitoring/components/charts/gauge.vue b/app/assets/javascripts/monitoring/components/charts/gauge.vue
new file mode 100644
index 00000000000..63fa60bbdf0
--- /dev/null
+++ b/app/assets/javascripts/monitoring/components/charts/gauge.vue
@@ -0,0 +1,122 @@
+<script>
+import { GlResizeObserverDirective } from '@gitlab/ui';
+import { GlGaugeChart } from '@gitlab/ui/dist/charts';
+import { isFinite, isArray, isInteger } from 'lodash';
+import { graphDataValidatorForValues } from '../../utils';
+import { getValidThresholds } from './options';
+import { getFormatter, SUPPORTED_FORMATS } from '~/lib/utils/unit_format';
+
+export default {
+ components: {
+ GlGaugeChart,
+ },
+ directives: {
+ GlResizeObserverDirective,
+ },
+ props: {
+ graphData: {
+ type: Object,
+ required: true,
+ validator: graphDataValidatorForValues.bind(null, true),
+ },
+ },
+ data() {
+ return {
+ width: 0,
+ };
+ },
+ computed: {
+ rangeValues() {
+ let min = 0;
+ let max = 100;
+
+ const { minValue, maxValue } = this.graphData;
+
+ const isValidMinMax = () => {
+ return isFinite(minValue) && isFinite(maxValue) && minValue < maxValue;
+ };
+
+ if (isValidMinMax()) {
+ min = minValue;
+ max = maxValue;
+ }
+
+ return {
+ min,
+ max,
+ };
+ },
+ validThresholds() {
+ const { mode, values } = this.graphData?.thresholds || {};
+ const range = this.rangeValues;
+
+ if (!isArray(values)) {
+ return [];
+ }
+
+ return getValidThresholds({ mode, range, values });
+ },
+ queryResult() {
+ return this.graphData?.metrics[0]?.result[0]?.value[1];
+ },
+ splitValue() {
+ const { split } = this.graphData;
+ const defaultValue = 10;
+
+ return isInteger(split) && split > 0 ? split : defaultValue;
+ },
+ textValue() {
+ const formatFromPanel = this.graphData.format;
+ const defaultFormat = SUPPORTED_FORMATS.engineering;
+ const format = SUPPORTED_FORMATS[formatFromPanel] ?? defaultFormat;
+ const { queryResult } = this;
+
+ const formatter = getFormatter(format);
+
+ return isFinite(queryResult) ? formatter(queryResult) : '--';
+ },
+ thresholdsValue() {
+ /**
+ * If there are no valid thresholds, a default threshold
+ * will be set at 90% of the gauge arcs' max value
+ */
+ const { min, max } = this.rangeValues;
+
+ const defaultThresholdValue = [(max - min) * 0.95];
+ return this.validThresholds.length ? this.validThresholds : defaultThresholdValue;
+ },
+ value() {
+ /**
+ * The gauge chart gitlab-ui component expects a value
+ * of type number.
+ *
+ * So, if the query result is undefined,
+ * we pass the gauge chart a value of NaN.
+ */
+ return this.queryResult || NaN;
+ },
+ },
+ methods: {
+ onResize() {
+ if (!this.$refs.gaugeChart) return;
+ const { width } = this.$refs.gaugeChart.$el.getBoundingClientRect();
+ this.width = width;
+ },
+ },
+};
+</script>
+<template>
+ <div v-gl-resize-observer-directive="onResize">
+ <gl-gauge-chart
+ ref="gaugeChart"
+ v-bind="$attrs"
+ :value="value"
+ :min="rangeValues.min"
+ :max="rangeValues.max"
+ :thresholds="thresholdsValue"
+ :text="textValue"
+ :split-number="splitValue"
+ :width="width"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/monitoring/components/charts/heatmap.vue b/app/assets/javascripts/monitoring/components/charts/heatmap.vue
index ddb44f7b1be..7003e2d37cf 100644
--- a/app/assets/javascripts/monitoring/components/charts/heatmap.vue
+++ b/app/assets/javascripts/monitoring/components/charts/heatmap.vue
@@ -36,7 +36,7 @@ export default {
);
},
xAxisName() {
- return this.graphData.x_label || '';
+ return this.graphData.xLabel || '';
},
yAxisName() {
return this.graphData.y_label || '';
diff --git a/app/assets/javascripts/monitoring/components/charts/options.js b/app/assets/javascripts/monitoring/components/charts/options.js
index 42252dd5897..0cd4a02311c 100644
--- a/app/assets/javascripts/monitoring/components/charts/options.js
+++ b/app/assets/javascripts/monitoring/components/charts/options.js
@@ -1,6 +1,8 @@
+import { isFinite, uniq, sortBy, includes } from 'lodash';
import { SUPPORTED_FORMATS, getFormatter } from '~/lib/utils/unit_format';
import { __, s__ } from '~/locale';
import { formatDate, timezones, formats } from '../../format_date';
+import { thresholdModeTypes } from '../../constants';
const yAxisBoundaryGap = [0.1, 0.1];
/**
@@ -109,3 +111,65 @@ export const getTooltipFormatter = ({
const formatter = getFormatter(format);
return num => formatter(num, precision);
};
+
+// Thresholds
+
+/**
+ *
+ * Used to find valid thresholds for the gauge chart
+ *
+ * An array of thresholds values is
+ * - duplicate values are removed;
+ * - filtered for invalid values;
+ * - sorted in ascending order;
+ * - only first two values are used.
+ */
+export const getValidThresholds = ({ mode, range = {}, values = [] } = {}) => {
+ const supportedModes = [thresholdModeTypes.ABSOLUTE, thresholdModeTypes.PERCENTAGE];
+ const { min, max } = range;
+
+ /**
+ * return early if min and max have invalid values
+ * or mode has invalid value
+ */
+ if (!isFinite(min) || !isFinite(max) || min >= max || !includes(supportedModes, mode)) {
+ return [];
+ }
+
+ const uniqueThresholds = uniq(values);
+
+ const numberThresholds = uniqueThresholds.filter(threshold => isFinite(threshold));
+
+ const validThresholds = numberThresholds.filter(threshold => {
+ let isValid;
+
+ if (mode === thresholdModeTypes.PERCENTAGE) {
+ isValid = threshold > 0 && threshold < 100;
+ } else if (mode === thresholdModeTypes.ABSOLUTE) {
+ isValid = threshold > min && threshold < max;
+ }
+
+ return isValid;
+ });
+
+ const transformedThresholds = validThresholds.map(threshold => {
+ let transformedThreshold;
+
+ if (mode === 'percentage') {
+ transformedThreshold = (threshold / 100) * (max - min);
+ } else {
+ transformedThreshold = threshold;
+ }
+
+ return transformedThreshold;
+ });
+
+ const sortedThresholds = sortBy(transformedThresholds);
+
+ const reducedThresholdsArray =
+ sortedThresholds.length > 2
+ ? [sortedThresholds[0], sortedThresholds[1]]
+ : [...sortedThresholds];
+
+ return reducedThresholdsArray;
+};
diff --git a/app/assets/javascripts/monitoring/components/charts/single_stat.vue b/app/assets/javascripts/monitoring/components/charts/single_stat.vue
index 106c76a97dc..a8ab41ebf26 100644
--- a/app/assets/javascripts/monitoring/components/charts/single_stat.vue
+++ b/app/assets/javascripts/monitoring/components/charts/single_stat.vue
@@ -50,7 +50,7 @@ export default {
}
formatter = getFormatter(SUPPORTED_FORMATS.number);
- return `${formatter(this.queryResult, defaultPrecision)}${this.queryInfo.unit}`;
+ return `${formatter(this.queryResult, defaultPrecision)}${this.queryInfo.unit ?? ''}`;
},
graphTitle() {
return this.queryInfo.label;
diff --git a/app/assets/javascripts/monitoring/components/charts/time_series.vue b/app/assets/javascripts/monitoring/components/charts/time_series.vue
index f2add429a80..054111c203e 100644
--- a/app/assets/javascripts/monitoring/components/charts/time_series.vue
+++ b/app/assets/javascripts/monitoring/components/charts/time_series.vue
@@ -1,6 +1,6 @@
<script>
-import { omit, throttle } from 'lodash';
-import { GlLink, GlDeprecatedButton, GlTooltip, GlResizeObserverDirective } from '@gitlab/ui';
+import { isEmpty, omit, throttle } from 'lodash';
+import { GlLink, GlTooltip, GlResizeObserverDirective } from '@gitlab/ui';
import { GlAreaChart, GlLineChart, GlChartSeriesLabel } from '@gitlab/ui/dist/charts';
import { s__ } from '~/locale';
import { getSvgIconPathContent } from '~/lib/utils/icon_utils';
@@ -25,7 +25,6 @@ export default {
GlAreaChart,
GlLineChart,
GlTooltip,
- GlDeprecatedButton,
GlChartSeriesLabel,
GlLink,
Icon,
@@ -45,6 +44,11 @@ export default {
required: false,
default: () => ({}),
},
+ timeRange: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
seriesConfig: {
type: Object,
required: false,
@@ -174,10 +178,17 @@ export default {
chartOptions() {
const { yAxis, xAxis } = this.option;
const option = omit(this.option, ['series', 'yAxis', 'xAxis']);
+ const xAxisBounds = isEmpty(this.timeRange)
+ ? {}
+ : {
+ min: this.timeRange.start,
+ max: this.timeRange.end,
+ };
const timeXAxis = {
...getTimeAxisOptions({ timezone: this.timezone }),
...xAxis,
+ ...xAxisBounds,
};
const dataYAxis = {