exportDashboard.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import cloneDeep from 'lodash/cloneDeep';
  2. import {addErrorMessage} from 'sentry/actionCreators/indicator';
  3. import {DashboardDetails} from './types';
  4. const deleteProperties = [
  5. 'createdBy',
  6. 'dateCreated',
  7. 'id',
  8. 'dashboardId',
  9. 'widgetId',
  10. ] as const;
  11. async function exportDashboard() {
  12. try {
  13. const structure = {
  14. base_url: null,
  15. dashboard_id: null,
  16. };
  17. const params = getAPIParams(structure);
  18. const apiUrl = `https://${params.base_url}/api/0/organizations/testorg-az/dashboards/${params.dashboard_id}/`;
  19. const response = await fetch(apiUrl);
  20. const jsonData = await response.json();
  21. const normalized = normalizeData(jsonData);
  22. normalized.projects = [];
  23. downloadObjectAsJson(normalized, cleanTitle(normalized.title));
  24. } catch (error) {
  25. addErrorMessage(
  26. 'Could not export dashboard. Please wait or try again with a different dashboard'
  27. );
  28. }
  29. }
  30. function getAPIParams(structure) {
  31. const url = window.location.href;
  32. const regex = {
  33. base_url: /(\/\/)(.*?)(\/)/,
  34. dashboard_id: /(dashboard\/)(.*?)(\/)/,
  35. };
  36. for (const attr in regex) {
  37. const match = url.match(regex[attr]);
  38. if (match?.length) {
  39. structure[attr] = match.length >= 3 ? match[2] : null;
  40. }
  41. }
  42. return structure;
  43. }
  44. function normalizeData(
  45. source: DashboardDetails
  46. ): Omit<DashboardDetails, (typeof deleteProperties)[number]> {
  47. const payload: Omit<DashboardDetails, (typeof deleteProperties)[number]> = {
  48. title: '',
  49. filters: {},
  50. projects: [],
  51. widgets: [],
  52. environment: [],
  53. };
  54. for (const property in payload) {
  55. if (property in source) {
  56. let data: any[] = [];
  57. // if there is a nested object with properties that should be deleted
  58. if (['widgets'].includes(property)) {
  59. // get the object properties so that we can loop through them
  60. const type = getPropertyStructure(property);
  61. data = normalizeNestedObject(source[property], type);
  62. } else {
  63. data = source[property];
  64. }
  65. payload[property] = data;
  66. }
  67. }
  68. return payload;
  69. }
  70. function normalizeNestedObject(object, structure) {
  71. const nestedObjectArray: any[] = [];
  72. for (const index in object) {
  73. const nestedObject = cloneDeep(structure);
  74. for (const property in structure) {
  75. if (property in object[index]) {
  76. let data: any[] = [];
  77. if (['queries'].includes(property)) {
  78. // get the object properties so that we can loop through them
  79. const type = getPropertyStructure(property);
  80. data = normalizeNestedObject(object[index][property], type);
  81. } else {
  82. data = object[index][property];
  83. }
  84. nestedObject[property] = data;
  85. }
  86. }
  87. nestedObjectArray.push(nestedObject);
  88. }
  89. return nestedObjectArray;
  90. }
  91. function getPropertyStructure(property) {
  92. let structure = {};
  93. switch (property) {
  94. case 'widgets':
  95. structure = {
  96. title: '',
  97. description: '',
  98. interval: '',
  99. queries: [],
  100. displayType: '',
  101. widgetType: '',
  102. };
  103. break;
  104. case 'queries':
  105. structure = {
  106. aggregates: [],
  107. columns: [],
  108. conditions: [],
  109. name: '',
  110. orderby: '',
  111. fieldAliases: [],
  112. fields: [],
  113. };
  114. break;
  115. default:
  116. structure = {};
  117. }
  118. return structure;
  119. }
  120. function downloadObjectAsJson(exportObj, exportName) {
  121. const dataStr = `data:text/json;charset=utf-8,${encodeURIComponent(
  122. JSON.stringify(exportObj)
  123. )}`;
  124. const downloadAnchorNode = document.createElement('a');
  125. downloadAnchorNode.setAttribute('href', dataStr);
  126. downloadAnchorNode.setAttribute('download', `${exportName}.json`);
  127. document.body.appendChild(downloadAnchorNode); // required for firefox
  128. downloadAnchorNode.click();
  129. downloadAnchorNode.remove();
  130. }
  131. function cleanTitle(title) {
  132. const regex = /[^a-z0-9]/gi;
  133. const formattedTitle = title.replace(regex, '-');
  134. const date = new Date();
  135. return `${formattedTitle}-${date.toISOString()}`;
  136. }
  137. export default exportDashboard;