module.ts 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. import './graph';
  2. import './series_overrides_ctrl';
  3. import './thresholds_form';
  4. import './time_regions_form';
  5. import template from './template';
  6. import _ from 'lodash';
  7. import { MetricsPanelCtrl } from 'app/plugins/sdk';
  8. import { DataProcessor } from './data_processor';
  9. import { axesEditorComponent } from './axes_editor';
  10. import config from 'app/core/config';
  11. import TimeSeries from 'app/core/time_series2';
  12. import { DataFrame, DataLink, DateTimeInput } from '@grafana/data';
  13. import { getColorFromHexRgbOrName, VariableSuggestion } from '@grafana/ui';
  14. import { getProcessedDataFrames } from 'app/features/dashboard/state/runRequest';
  15. import { GraphContextMenuCtrl } from './GraphContextMenuCtrl';
  16. import { getDataLinksVariableSuggestions } from 'app/features/panel/panellinks/link_srv';
  17. import { auto } from 'angular';
  18. import { AnnotationsSrv } from 'app/features/annotations/all';
  19. class GraphCtrl extends MetricsPanelCtrl {
  20. static template = template;
  21. renderError: boolean;
  22. hiddenSeries: any = {};
  23. seriesList: TimeSeries[] = [];
  24. dataList: DataFrame[] = [];
  25. annotations: any = [];
  26. alertState: any;
  27. annotationsPromise: any;
  28. dataWarning: any;
  29. colors: any = [];
  30. subTabIndex: number;
  31. processor: DataProcessor;
  32. contextMenuCtrl: GraphContextMenuCtrl;
  33. linkVariableSuggestions: VariableSuggestion[] = getDataLinksVariableSuggestions();
  34. panelDefaults: any = {
  35. // datasource name, null = default datasource
  36. datasource: null,
  37. // sets client side (flot) or native graphite png renderer (png)
  38. renderer: 'flot',
  39. yaxes: [
  40. {
  41. label: null,
  42. show: true,
  43. logBase: 1,
  44. min: null,
  45. max: null,
  46. format: 'short',
  47. },
  48. {
  49. label: null,
  50. show: true,
  51. logBase: 1,
  52. min: null,
  53. max: null,
  54. format: 'short',
  55. },
  56. ],
  57. xaxis: {
  58. show: true,
  59. mode: 'time',
  60. name: null,
  61. values: [],
  62. buckets: null,
  63. },
  64. yaxis: {
  65. align: false,
  66. alignLevel: null,
  67. },
  68. // show/hide lines
  69. lines: true,
  70. // fill factor
  71. fill: 1,
  72. // fill factor
  73. fillGradient: 0,
  74. // line width in pixels
  75. linewidth: 1,
  76. // show/hide dashed line
  77. dashes: false,
  78. // length of a dash
  79. dashLength: 10,
  80. // length of space between two dashes
  81. spaceLength: 10,
  82. // show hide points
  83. points: false,
  84. // point radius in pixels
  85. pointradius: 2,
  86. // show hide bars
  87. bars: false,
  88. // enable/disable stacking
  89. stack: false,
  90. // stack percentage mode
  91. percentage: false,
  92. // legend options
  93. legend: {
  94. show: true, // disable/enable legend
  95. values: false, // disable/enable legend values
  96. min: false,
  97. max: false,
  98. current: false,
  99. total: false,
  100. avg: false,
  101. },
  102. // how null points should be handled
  103. nullPointMode: 'null',
  104. // staircase line mode
  105. steppedLine: false,
  106. // tooltip options
  107. tooltip: {
  108. value_type: 'individual',
  109. shared: true,
  110. sort: 0,
  111. },
  112. // time overrides
  113. timeFrom: null,
  114. timeShift: null,
  115. // metric queries
  116. targets: [{}],
  117. // series color overrides
  118. aliasColors: {},
  119. // other style overrides
  120. seriesOverrides: [],
  121. thresholds: [],
  122. timeRegions: [],
  123. options: {
  124. dataLinks: [],
  125. },
  126. };
  127. /** @ngInject */
  128. constructor($scope: any, $injector: auto.IInjectorService, private annotationsSrv: AnnotationsSrv) {
  129. super($scope, $injector);
  130. _.defaults(this.panel, this.panelDefaults);
  131. _.defaults(this.panel.tooltip, this.panelDefaults.tooltip);
  132. _.defaults(this.panel.legend, this.panelDefaults.legend);
  133. _.defaults(this.panel.xaxis, this.panelDefaults.xaxis);
  134. _.defaults(this.panel.options, this.panelDefaults.options);
  135. this.useDataFrames = true;
  136. this.processor = new DataProcessor(this.panel);
  137. this.contextMenuCtrl = new GraphContextMenuCtrl($scope);
  138. this.events.on('render', this.onRender.bind(this));
  139. this.events.on('data-frames-received', this.onDataFramesReceived.bind(this));
  140. this.events.on('data-error', this.onDataError.bind(this));
  141. this.events.on('data-snapshot-load', this.onDataSnapshotLoad.bind(this));
  142. this.events.on('init-edit-mode', this.onInitEditMode.bind(this));
  143. this.events.on('init-panel-actions', this.onInitPanelActions.bind(this));
  144. this.onDataLinksChange = this.onDataLinksChange.bind(this);
  145. }
  146. onInitEditMode() {
  147. this.addEditorTab('Display options', 'public/app/plugins/panel/graph/tab_display.html');
  148. this.addEditorTab('Axes', axesEditorComponent);
  149. this.addEditorTab('Legend', 'public/app/plugins/panel/graph/tab_legend.html');
  150. this.addEditorTab('Thresholds & Time Regions', 'public/app/plugins/panel/graph/tab_thresholds_time_regions.html');
  151. this.addEditorTab('Data links', 'public/app/plugins/panel/graph/tab_drilldown_links.html');
  152. this.subTabIndex = 0;
  153. }
  154. onInitPanelActions(actions: any[]) {
  155. actions.push({ text: 'Export CSV', click: 'ctrl.exportCsv()' });
  156. actions.push({ text: 'Toggle legend', click: 'ctrl.toggleLegend()', shortcut: 'p l' });
  157. }
  158. issueQueries(datasource: any) {
  159. this.annotationsPromise = this.annotationsSrv.getAnnotations({
  160. dashboard: this.dashboard,
  161. panel: this.panel,
  162. range: this.range,
  163. });
  164. /* Wait for annotationSrv requests to get datasources to
  165. * resolve before issuing queries. This allows the annotations
  166. * service to fire annotations queries before graph queries
  167. * (but not wait for completion). This resolves
  168. * issue 11806.
  169. */
  170. return this.annotationsSrv.datasourcePromises.then((r: any) => {
  171. return super.issueQueries(datasource);
  172. });
  173. }
  174. zoomOut(evt: any) {
  175. this.publishAppEvent('zoom-out', 2);
  176. }
  177. onDataSnapshotLoad(snapshotData: any) {
  178. this.annotationsPromise = this.annotationsSrv.getAnnotations({
  179. dashboard: this.dashboard,
  180. panel: this.panel,
  181. range: this.range,
  182. });
  183. const frames = getProcessedDataFrames(snapshotData);
  184. this.onDataFramesReceived(frames);
  185. }
  186. onDataError(err: any) {
  187. this.seriesList = [];
  188. this.annotations = [];
  189. this.render([]);
  190. }
  191. onDataFramesReceived(data: DataFrame[]) {
  192. this.dataList = data;
  193. this.seriesList = this.processor.getSeriesList({
  194. dataList: this.dataList,
  195. range: this.range,
  196. });
  197. this.dataWarning = null;
  198. const datapointsCount = this.seriesList.reduce((prev, series) => {
  199. return prev + series.datapoints.length;
  200. }, 0);
  201. if (datapointsCount === 0) {
  202. this.dataWarning = {
  203. title: 'No data points',
  204. tip: 'No datapoints returned from data query',
  205. };
  206. } else {
  207. for (const series of this.seriesList) {
  208. if (series.isOutsideRange) {
  209. this.dataWarning = {
  210. title: 'Data points outside time range',
  211. tip: 'Can be caused by timezone mismatch or missing time filter in query',
  212. };
  213. break;
  214. }
  215. }
  216. }
  217. this.annotationsPromise.then(
  218. (result: { alertState: any; annotations: any }) => {
  219. this.loading = false;
  220. this.alertState = result.alertState;
  221. this.annotations = result.annotations;
  222. this.render(this.seriesList);
  223. },
  224. () => {
  225. this.loading = false;
  226. this.render(this.seriesList);
  227. }
  228. );
  229. }
  230. onRender() {
  231. if (!this.seriesList) {
  232. return;
  233. }
  234. for (const series of this.seriesList) {
  235. series.applySeriesOverrides(this.panel.seriesOverrides);
  236. if (series.unit) {
  237. this.panel.yaxes[series.yaxis - 1].format = series.unit;
  238. }
  239. }
  240. }
  241. onColorChange = (series: any, color: string) => {
  242. series.setColor(getColorFromHexRgbOrName(color, config.theme.type));
  243. this.panel.aliasColors[series.alias] = color;
  244. this.render();
  245. };
  246. onToggleSeries = (hiddenSeries: any) => {
  247. this.hiddenSeries = hiddenSeries;
  248. this.render();
  249. };
  250. onToggleSort = (sortBy: any, sortDesc: any) => {
  251. this.panel.legend.sort = sortBy;
  252. this.panel.legend.sortDesc = sortDesc;
  253. this.render();
  254. };
  255. onToggleAxis = (info: { alias: any; yaxis: any }) => {
  256. let override: any = _.find(this.panel.seriesOverrides, { alias: info.alias });
  257. if (!override) {
  258. override = { alias: info.alias };
  259. this.panel.seriesOverrides.push(override);
  260. }
  261. override.yaxis = info.yaxis;
  262. this.render();
  263. };
  264. onDataLinksChange(dataLinks: DataLink[]) {
  265. this.panel.updateOptions({
  266. ...this.panel.options,
  267. dataLinks,
  268. });
  269. }
  270. addSeriesOverride(override: any) {
  271. this.panel.seriesOverrides.push(override || {});
  272. }
  273. removeSeriesOverride(override: any) {
  274. this.panel.seriesOverrides = _.without(this.panel.seriesOverrides, override);
  275. this.render();
  276. }
  277. toggleLegend() {
  278. this.panel.legend.show = !this.panel.legend.show;
  279. this.render();
  280. }
  281. legendValuesOptionChanged() {
  282. const legend = this.panel.legend;
  283. legend.values = legend.min || legend.max || legend.avg || legend.current || legend.total;
  284. this.render();
  285. }
  286. exportCsv() {
  287. const scope = this.$scope.$new(true);
  288. scope.seriesList = this.seriesList;
  289. this.publishAppEvent('show-modal', {
  290. templateHtml: '<export-data-modal data="seriesList"></export-data-modal>',
  291. scope,
  292. modalClass: 'modal--narrow',
  293. });
  294. }
  295. onContextMenuClose = () => {
  296. this.contextMenuCtrl.toggleMenu();
  297. };
  298. formatDate = (date: DateTimeInput, format?: string) => {
  299. return this.dashboard.formatDate.apply(this.dashboard, [date, format]);
  300. };
  301. }
  302. export { GraphCtrl, GraphCtrl as PanelCtrl };