You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

171 lines
5.6 KiB

import { PanelOptions, Aggregation, Threshold, Icon, IconPosition, Condition } from 'types';
import { filterMetricListByAlias, getAggregatedValueFromSerie } from '../../utils';
import { getValueFormat } from '@grafana/data';
import _ from 'lodash';
// Convert Grafana options into Chartwerk Gauge options
export class GaugeOptions {
private minValue: number | undefined;
private maxValue: number | undefined;
private thresholds: Array<{ value: number; color: string }> = [];
private icons: Array<{ src: string; position: string; size: number }> = [];
constructor(private grafanaSeriesList: any[], private grafanaOptions: PanelOptions) {
this._setMin();
this._setMax();
this._setThresholds();
this._setIcons();
}
private _setMin(): void {
if (!this.grafanaOptions.gauge.min) {
throw new Error(`Min Config is not selected: [See options: Extremum -> Min]`);
}
if (!this.grafanaOptions.gauge.min?.useMetric) {
this.minValue = this.grafanaOptions.gauge.min.value;
return;
}
const aggregatedValue = this.getLastValueFromMetrics(this.grafanaOptions.gauge.min.metricName, 'Min');
this.minValue = aggregatedValue ? aggregatedValue : undefined;
}
private _setMax(): void {
if (!this.grafanaOptions.gauge.max) {
throw new Error(`Max Config is not selected: [See options: Extremum -> Max]`);
}
if (!this.grafanaOptions.gauge.max?.useMetric) {
this.maxValue = this.grafanaOptions.gauge.max.value;
return;
}
const aggregatedValue = this.getLastValueFromMetrics(this.grafanaOptions.gauge.max.metricName, 'Max');
this.maxValue = aggregatedValue ? aggregatedValue : undefined;
}
private _setThresholds(): void {
if (_.isEmpty(this.grafanaOptions.gauge.thresholds.thresholds)) {
return;
}
for (let [idx, threshold] of this.grafanaOptions.gauge.thresholds.thresholds.entries()) {
this._setThreshold(threshold, idx);
}
}
private _setThreshold(threshold: Threshold, idx: number): void {
const value = threshold.useMetric
? this.getLastValueFromMetrics(threshold.metricName, `Threshold ${idx + 1}`)
: threshold.value;
if (value === null || value === undefined) {
// TODO: may be throw an error
return;
}
this.thresholds.push({
value,
color: threshold.color,
});
}
private _valueFormatter(value: number): string {
const suffix = getValueFormat(this.grafanaOptions.gauge.unit)(0)?.suffix || '';
const decimals = _.isNumber(this.grafanaOptions.gauge.decimals) ? this.grafanaOptions.gauge.decimals : 2;
return `${value.toFixed(decimals)} ${suffix}`;
}
private _setIcons(): void {
if (_.isEmpty(this.grafanaOptions.gauge.icons)) {
return;
}
for (let [idx, icon] of this.grafanaOptions.gauge.icons.entries()) {
this._setIcon(icon, idx);
}
}
private _setIcon(icon: Icon, idx: number): void {
if (!this._areIconConditionsFulfilled(icon, idx)) {
return;
}
this.icons.push({
src: icon.url,
size: icon.size,
position: this._getChartwerkIconPosition(icon.position),
});
}
private _areIconConditionsFulfilled(icon: Icon, iconIdx: number): boolean {
if (_.isEmpty(icon.metrics)) {
return true;
}
// check each condition and return false if something goes wrong
for (let [conditionIdx, metric] of icon.metrics.entries()) {
const value = this.getLastValueFromMetrics(metric, `Icon ${iconIdx + 1}, Condition ${conditionIdx + 1}`);
if (value === null || value === undefined) {
// TODO: may be throw an error
return false;
}
if (!this.checkIconCondition(value, icon.values[conditionIdx], icon.conditions[conditionIdx])) {
return false;
}
}
return true;
}
private checkIconCondition(metricValue: number, inputValue: number, condition: Condition): boolean {
if (inputValue === undefined || inputValue === null) {
return true;
}
switch (condition) {
case Condition.EQUAL:
return metricValue === inputValue;
case Condition.GREATER:
return metricValue > inputValue;
case Condition.GREATER_OR_EQUAL:
return metricValue >= inputValue;
case Condition.LESS:
return metricValue < inputValue;
case Condition.LESS_OR_EQUAL:
return metricValue <= inputValue;
default:
throw new Error(`Unknown condition: ${condition}`);
}
}
private _getChartwerkIconPosition(position: IconPosition): string {
// TODO: use chartwerk types
switch (position) {
case IconPosition.MIDDLE:
return 'middle';
case IconPosition.UPPER_LEFT:
return 'left';
case IconPosition.UPPER_RIGHT:
return 'right';
default:
throw new Error(`Unknown Icon Position ${position}`);
}
}
getChartwerkOptions(): any {
return {
maxValue: this.maxValue,
minValue: this.minValue,
valueFormatter: (val: number) => this._valueFormatter(val),
defaultColor: this.grafanaOptions.gauge.thresholds.defaultColor,
valueArcBackgroundColor: this.grafanaOptions.gauge.thresholds.arcBackground,
reversed: this.grafanaOptions.gauge.reversed,
stops: this.thresholds,
valueFontSize: this.grafanaOptions.gauge.valueSize,
icons: this.icons,
};
}
getLastValueFromMetrics(metricName: string | undefined, optionName: string): number | null {
// optionName -> helper in Error, mb use option path instead
const filteredSeries = filterMetricListByAlias(this.grafanaSeriesList, metricName, optionName);
const serie = filteredSeries[0];
// Last value for now
return getAggregatedValueFromSerie(serie, Aggregation.LAST);
}
}