DashboardExporter.ts 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. import config from 'app/core/config';
  2. import _ from 'lodash';
  3. import { DashboardModel } from '../../state/DashboardModel';
  4. export class DashboardExporter {
  5. constructor(private datasourceSrv) {}
  6. makeExportable(dashboard: DashboardModel) {
  7. // clean up repeated rows and panels,
  8. // this is done on the live real dashboard instance, not on a clone
  9. // so we need to undo this
  10. // this is pretty hacky and needs to be changed
  11. dashboard.cleanUpRepeats();
  12. const saveModel = dashboard.getSaveModelClone();
  13. saveModel.id = null;
  14. // undo repeat cleanup
  15. dashboard.processRepeats();
  16. const inputs = [];
  17. const requires = {};
  18. const datasources = {};
  19. const promises = [];
  20. const variableLookup: any = {};
  21. for (const variable of saveModel.templating.list) {
  22. variableLookup[variable.name] = variable;
  23. }
  24. const templateizeDatasourceUsage = obj => {
  25. let datasource = obj.datasource;
  26. let datasourceVariable = null;
  27. // ignore data source properties that contain a variable
  28. if (datasource && datasource.indexOf('$') === 0) {
  29. datasourceVariable = variableLookup[datasource.substring(1)];
  30. if (datasourceVariable && datasourceVariable.current) {
  31. datasource = datasourceVariable.current.value;
  32. }
  33. }
  34. promises.push(
  35. this.datasourceSrv.get(datasource).then(ds => {
  36. if (ds.meta.builtIn) {
  37. return;
  38. }
  39. // add data source type to require list
  40. requires['datasource' + ds.meta.id] = {
  41. type: 'datasource',
  42. id: ds.meta.id,
  43. name: ds.meta.name,
  44. version: ds.meta.info.version || '1.0.0',
  45. };
  46. // if used via variable we can skip templatizing usage
  47. if (datasourceVariable) {
  48. return;
  49. }
  50. const refName = 'DS_' + ds.name.replace(' ', '_').toUpperCase();
  51. datasources[refName] = {
  52. name: refName,
  53. label: ds.name,
  54. description: '',
  55. type: 'datasource',
  56. pluginId: ds.meta.id,
  57. pluginName: ds.meta.name,
  58. };
  59. obj.datasource = '${' + refName + '}';
  60. })
  61. );
  62. };
  63. const processPanel = panel => {
  64. if (panel.datasource !== undefined) {
  65. templateizeDatasourceUsage(panel);
  66. }
  67. if (panel.targets) {
  68. for (const target of panel.targets) {
  69. if (target.datasource !== undefined) {
  70. templateizeDatasourceUsage(target);
  71. }
  72. }
  73. }
  74. const panelDef = config.panels[panel.type];
  75. if (panelDef) {
  76. requires['panel' + panelDef.id] = {
  77. type: 'panel',
  78. id: panelDef.id,
  79. name: panelDef.name,
  80. version: panelDef.info.version,
  81. };
  82. }
  83. };
  84. // check up panel data sources
  85. for (const panel of saveModel.panels) {
  86. processPanel(panel);
  87. // handle collapsed rows
  88. if (panel.collapsed !== undefined && panel.collapsed === true && panel.panels) {
  89. for (const rowPanel of panel.panels) {
  90. processPanel(rowPanel);
  91. }
  92. }
  93. }
  94. // templatize template vars
  95. for (const variable of saveModel.templating.list) {
  96. if (variable.type === 'query') {
  97. templateizeDatasourceUsage(variable);
  98. variable.options = [];
  99. variable.current = {};
  100. variable.refresh = variable.refresh > 0 ? variable.refresh : 1;
  101. }
  102. }
  103. // templatize annotations vars
  104. for (const annotationDef of saveModel.annotations.list) {
  105. templateizeDatasourceUsage(annotationDef);
  106. }
  107. // add grafana version
  108. requires['grafana'] = {
  109. type: 'grafana',
  110. id: 'grafana',
  111. name: 'Grafana',
  112. version: config.buildInfo.version,
  113. };
  114. return Promise.all(promises)
  115. .then(() => {
  116. _.each(datasources, (value, key) => {
  117. inputs.push(value);
  118. });
  119. // templatize constants
  120. for (const variable of saveModel.templating.list) {
  121. if (variable.type === 'constant') {
  122. const refName = 'VAR_' + variable.name.replace(' ', '_').toUpperCase();
  123. inputs.push({
  124. name: refName,
  125. type: 'constant',
  126. label: variable.label || variable.name,
  127. value: variable.current.value,
  128. description: '',
  129. });
  130. // update current and option
  131. variable.query = '${' + refName + '}';
  132. variable.options[0] = variable.current = {
  133. value: variable.query,
  134. text: variable.query,
  135. };
  136. }
  137. }
  138. // make inputs and requires a top thing
  139. const newObj = {};
  140. newObj['__inputs'] = inputs;
  141. newObj['__requires'] = _.sortBy(requires, ['id']);
  142. _.defaults(newObj, saveModel);
  143. return newObj;
  144. })
  145. .catch(err => {
  146. console.log('Export failed:', err);
  147. return {
  148. error: err,
  149. };
  150. });
  151. }
  152. }