grafana/public/app/features/dashboard/panel_model.ts

276 lines
5.9 KiB
TypeScript
Raw Normal View History

2019-01-17 10:59:47 -06:00
// Libraries
2017-12-20 05:33:33 -06:00
import _ from 'lodash';
2019-01-17 10:59:47 -06:00
// Types
import { Emitter } from 'app/core/utils/emitter';
import { PANEL_OPTIONS_KEY_PREFIX } from 'app/core/constants';
2019-01-30 06:43:17 -06:00
import { DataQuery, TimeSeries } from '@grafana/ui';
2017-10-10 02:34:14 -05:00
export interface GridPos {
2017-10-10 02:34:14 -05:00
x: number;
y: number;
w: number;
h: number;
2017-10-16 02:55:55 -05:00
static?: boolean;
}
const notPersistedProperties: { [str: string]: boolean } = {
events: true,
fullscreen: true,
2017-12-20 05:33:33 -06:00
isEditing: true,
hasRefreshed: true,
cachedPluginOptions: true,
};
// For angular panels we need to clean up properties when changing type
// To make sure the change happens without strange bugs happening when panels use same
// named property with different type / value expectations
// This is not required for react panels
const mustKeepProps: { [str: string]: boolean } = {
id: true,
gridPos: true,
type: true,
title: true,
scopedVars: true,
repeat: true,
repeatIteration: true,
repeatPanelId: true,
repeatDirection: true,
repeatedByRow: true,
minSpan: true,
collapsed: true,
panels: true,
targets: true,
datasource: true,
timeFrom: true,
timeShift: true,
hideTimeOverride: true,
maxDataPoints: true,
interval: true,
description: true,
links: true,
fullscreen: true,
isEditing: true,
hasRefreshed: true,
events: true,
cacheTimeout: true,
cachedPluginOptions: true,
2018-12-06 03:34:27 -06:00
transparent: true,
};
const defaults: any = {
gridPos: { x: 0, y: 0, h: 3, w: 6 },
datasource: null,
targets: [{ refId: 'A' }],
cachedPluginOptions: {},
2018-12-06 03:34:27 -06:00
transparent: false,
};
export class PanelModel {
id: number;
gridPos: GridPos;
2017-10-10 02:34:14 -05:00
type: string;
title: string;
alert?: any;
2017-10-12 12:01:02 -05:00
scopedVars?: any;
repeat?: string;
repeatIteration?: number;
repeatPanelId?: number;
repeatDirection?: string;
repeatedByRow?: boolean;
maxPerRow?: number;
2017-10-17 07:53:52 -05:00
collapsed?: boolean;
2017-10-17 05:04:18 -05:00
panels?: any;
2017-11-28 02:38:28 -06:00
soloMode?: boolean;
targets: DataQuery[];
2018-06-26 09:32:01 -05:00
datasource: string;
thresholds?: any;
2019-01-30 06:43:17 -06:00
snapshotData?: TimeSeries[];
timeFrom?: any;
timeShift?: any;
hideTimeOverride?: any;
maxDataPoints?: number;
interval?: string;
description?: string;
links?: [];
2018-12-06 03:34:27 -06:00
transparent: boolean;
// non persisted
fullscreen: boolean;
isEditing: boolean;
hasRefreshed: boolean;
events: Emitter;
cacheTimeout?: any;
// cache props between plugins
cachedPluginOptions?: any;
2017-10-10 10:57:53 -05:00
constructor(model) {
this.events = new Emitter();
// copy properties from persisted model
2018-08-29 07:26:50 -05:00
for (const property in model) {
this[property] = model[property];
}
2017-10-12 14:37:27 -05:00
2018-06-26 09:32:01 -05:00
// defaults
_.defaultsDeep(this, _.cloneDeep(defaults));
// queries must have refId
this.ensureQueryIds();
}
ensureQueryIds() {
if (this.targets) {
for (const query of this.targets) {
if (!query.refId) {
query.refId = this.getNextQueryLetter();
}
}
}
}
2018-11-20 10:01:58 -06:00
getOptions(panelDefaults) {
return _.defaultsDeep(this[this.getOptionsKey()] || {}, panelDefaults);
2018-11-05 10:46:09 -06:00
}
updateOptions(options: object) {
const update: any = {};
update[this.getOptionsKey()] = options;
Object.assign(this, update);
this.render();
}
private getOptionsKey() {
return PANEL_OPTIONS_KEY_PREFIX + this.type;
2018-11-05 10:46:09 -06:00
}
getSaveModel() {
2017-10-10 10:57:53 -05:00
const model: any = {};
2018-08-29 07:26:50 -05:00
for (const property in this) {
if (notPersistedProperties[property] || !this.hasOwnProperty(property)) {
continue;
}
if (_.isEqual(this[property], defaults[property])) {
continue;
}
2017-10-12 14:37:27 -05:00
model[property] = _.cloneDeep(this[property]);
}
2017-10-10 10:57:53 -05:00
return model;
}
setViewMode(fullscreen: boolean, isEditing: boolean) {
this.fullscreen = fullscreen;
this.isEditing = isEditing;
this.events.emit('view-mode-changed');
}
updateGridPos(newPos: GridPos) {
let sizeChanged = false;
if (this.gridPos.w !== newPos.w || this.gridPos.h !== newPos.h) {
sizeChanged = true;
}
this.gridPos.x = newPos.x;
this.gridPos.y = newPos.y;
this.gridPos.w = newPos.w;
this.gridPos.h = newPos.h;
if (sizeChanged) {
2017-12-20 05:33:33 -06:00
this.events.emit('panel-size-changed');
}
}
2017-10-11 14:36:03 -05:00
resizeDone() {
2017-12-20 05:33:33 -06:00
this.events.emit('panel-size-changed');
2017-10-11 14:36:03 -05:00
}
2017-10-16 09:09:23 -05:00
refresh() {
this.hasRefreshed = true;
this.events.emit('refresh');
}
render() {
if (!this.hasRefreshed) {
this.refresh();
} else {
this.events.emit('render');
}
}
initialized() {
this.events.emit('panel-initialized');
}
private getOptionsToRemember() {
return Object.keys(this).reduce((acc, property) => {
if (notPersistedProperties[property] || mustKeepProps[property]) {
return acc;
}
return {
...acc,
[property]: this[property],
};
}, {});
}
private saveCurrentPanelOptions() {
this.cachedPluginOptions[this.type] = this.getOptionsToRemember();
}
private restorePanelOptions(pluginId: string) {
const prevOptions = this.cachedPluginOptions[pluginId] || {};
Object.keys(prevOptions).map(property => {
this[property] = prevOptions[property];
});
}
changeType(pluginId: string, fromAngularPanel: boolean) {
this.saveCurrentPanelOptions();
this.type = pluginId;
// for angular panels only we need to remove all events and let angular panels do some cleanup
if (fromAngularPanel) {
this.destroy();
for (const key of _.keys(this)) {
if (mustKeepProps[key]) {
continue;
}
delete this[key];
}
}
this.restorePanelOptions(pluginId);
2018-07-09 11:17:51 -05:00
}
2018-12-12 01:54:12 -06:00
addQuery(query?: Partial<DataQuery>) {
2018-12-11 06:36:44 -06:00
query = query || { refId: 'A' };
2018-12-12 01:54:12 -06:00
query.refId = this.getNextQueryLetter();
this.targets.push(query as DataQuery);
2018-12-11 06:36:44 -06:00
}
getNextQueryLetter(): string {
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
return _.find(letters, refId => {
return _.every(this.targets, other => {
return other.refId !== refId;
});
});
}
2017-10-16 09:09:23 -05:00
destroy() {
this.events.emit('panel-teardown');
2017-10-16 09:09:23 -05:00
this.events.removeAllListeners();
}
2017-10-10 02:34:14 -05:00
}