migrations.ts 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. import { FieldConfigSource, PanelModel, PanelTypeChangedHandler } from '@grafana/data';
  2. import { AxisPlacement, ScaleDistribution, VisibilityMode } from '@grafana/schema';
  3. import {
  4. HeatmapCellLayout,
  5. HeatmapCalculationMode,
  6. HeatmapCalculationOptions,
  7. } from 'app/features/transformers/calculateHeatmap/models.gen';
  8. import { PanelOptions, defaultPanelOptions, HeatmapColorMode } from './models.gen';
  9. import { colorSchemes } from './palettes';
  10. /** Called when the version number changes */
  11. export const heatmapMigrationHandler = (panel: PanelModel): Partial<PanelOptions> => {
  12. // Migrating from angular
  13. if (Object.keys(panel.options).length === 0) {
  14. return heatmapChangedHandler(panel, 'heatmap', { angular: panel }, panel.fieldConfig);
  15. }
  16. return panel.options;
  17. };
  18. /**
  19. * This is called when the panel changes from another panel
  20. */
  21. export const heatmapChangedHandler: PanelTypeChangedHandler = (panel, prevPluginId, prevOptions, prevFieldConfig) => {
  22. if (prevPluginId === 'heatmap' && prevOptions.angular) {
  23. const { fieldConfig, options } = angularToReactHeatmap({
  24. ...prevOptions.angular,
  25. fieldConfig: prevFieldConfig,
  26. });
  27. panel.fieldConfig = fieldConfig; // Mutates the incoming panel
  28. return options;
  29. }
  30. // alpha for 8.5+, then beta at 9.0.1
  31. if (prevPluginId === 'heatmap-new') {
  32. const { bucketFrame, ...options } = panel.options;
  33. if (bucketFrame) {
  34. return { ...options, rowsFrame: bucketFrame };
  35. }
  36. return panel.options;
  37. }
  38. return {};
  39. };
  40. export function angularToReactHeatmap(angular: any): { fieldConfig: FieldConfigSource; options: PanelOptions } {
  41. const fieldConfig: FieldConfigSource = {
  42. defaults: {},
  43. overrides: [],
  44. };
  45. const calculate = angular.dataFormat === 'tsbuckets' ? false : true;
  46. const calculation: HeatmapCalculationOptions = {
  47. ...defaultPanelOptions.calculation,
  48. };
  49. const oldYAxis = { logBase: 1, ...angular.yAxis };
  50. if (calculate) {
  51. if (angular.xBucketSize) {
  52. calculation.xBuckets = { mode: HeatmapCalculationMode.Size, value: `${angular.xBucketSize}` };
  53. } else if (angular.xBucketNumber) {
  54. calculation.xBuckets = { mode: HeatmapCalculationMode.Count, value: `${angular.xBucketNumber}` };
  55. }
  56. if (angular.yBucketSize) {
  57. calculation.yBuckets = { mode: HeatmapCalculationMode.Size, value: `${angular.yBucketSize}` };
  58. } else if (angular.xBucketNumber) {
  59. calculation.yBuckets = { mode: HeatmapCalculationMode.Count, value: `${angular.yBucketNumber}` };
  60. }
  61. if (oldYAxis.logBase > 1) {
  62. calculation.yBuckets = {
  63. mode: HeatmapCalculationMode.Count,
  64. value: +oldYAxis.splitFactor > 0 ? `${oldYAxis.splitFactor}` : undefined,
  65. scale: {
  66. type: ScaleDistribution.Log,
  67. log: oldYAxis.logBase,
  68. },
  69. };
  70. }
  71. }
  72. const options: PanelOptions = {
  73. calculate,
  74. calculation,
  75. color: {
  76. ...defaultPanelOptions.color,
  77. steps: 128, // best match with existing colors
  78. },
  79. cellGap: asNumber(angular.cards?.cardPadding, 2),
  80. cellRadius: asNumber(angular.cards?.cardRound), // just to keep it
  81. yAxis: {
  82. axisPlacement: oldYAxis.show === false ? AxisPlacement.Hidden : AxisPlacement.Left,
  83. reverse: Boolean(angular.reverseYBuckets),
  84. axisWidth: asNumber(oldYAxis.width),
  85. min: oldYAxis.min,
  86. max: oldYAxis.max,
  87. unit: oldYAxis.format,
  88. decimals: oldYAxis.decimals,
  89. },
  90. cellValues: {
  91. decimals: asNumber(angular.tooltipDecimals),
  92. },
  93. rowsFrame: {
  94. layout: getHeatmapCellLayout(angular.yBucketBound),
  95. },
  96. legend: {
  97. show: Boolean(angular.legend.show),
  98. },
  99. showValue: VisibilityMode.Never,
  100. tooltip: {
  101. show: Boolean(angular.tooltip?.show),
  102. yHistogram: Boolean(angular.tooltip?.showHistogram),
  103. },
  104. exemplars: {
  105. ...defaultPanelOptions.exemplars,
  106. },
  107. };
  108. if (angular.hideZeroBuckets) {
  109. options.filterValues = { ...defaultPanelOptions.filterValues }; // min: 1e-9
  110. }
  111. // Migrate color options
  112. const color = angular.color;
  113. switch (color?.mode) {
  114. case 'spectrum': {
  115. options.color.mode = HeatmapColorMode.Scheme;
  116. const current = color.colorScheme as string;
  117. let scheme = colorSchemes.find((v) => v.name === current);
  118. if (!scheme) {
  119. scheme = colorSchemes.find((v) => current.indexOf(v.name) >= 0);
  120. }
  121. options.color.scheme = scheme ? scheme.name : defaultPanelOptions.color.scheme;
  122. break;
  123. }
  124. case 'opacity': {
  125. options.color.mode = HeatmapColorMode.Opacity;
  126. options.color.scale = color.scale;
  127. break;
  128. }
  129. }
  130. options.color.min = color.min;
  131. options.color.max = color.max;
  132. return { fieldConfig, options };
  133. }
  134. function getHeatmapCellLayout(v?: string): HeatmapCellLayout {
  135. switch (v) {
  136. case 'upper':
  137. return HeatmapCellLayout.ge;
  138. case 'lower':
  139. return HeatmapCellLayout.le;
  140. case 'middle':
  141. return HeatmapCellLayout.unknown;
  142. }
  143. return HeatmapCellLayout.auto;
  144. }
  145. function asNumber(v: any, defaultValue?: number): number | undefined {
  146. if (v == null || v === '') {
  147. return defaultValue;
  148. }
  149. const num = +v;
  150. return isNaN(num) ? defaultValue : num;
  151. }