DashboardLoaderSrv.ts 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. import $ from 'jquery';
  2. import _, { isFunction } from 'lodash'; // eslint-disable-line lodash/import-scope
  3. import moment from 'moment'; // eslint-disable-line no-restricted-imports
  4. import { AppEvents, dateMath, UrlQueryValue } from '@grafana/data';
  5. import { getBackendSrv, locationService } from '@grafana/runtime';
  6. import { backendSrv } from 'app/core/services/backend_srv';
  7. import impressionSrv from 'app/core/services/impression_srv';
  8. import kbn from 'app/core/utils/kbn';
  9. import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
  10. import { appEvents } from '../../../core/core';
  11. import { getDashboardSrv } from './DashboardSrv';
  12. export class DashboardLoaderSrv {
  13. constructor() {}
  14. _dashboardLoadFailed(title: string, snapshot?: boolean) {
  15. snapshot = snapshot || false;
  16. return {
  17. meta: {
  18. canStar: false,
  19. isSnapshot: snapshot,
  20. canDelete: false,
  21. canSave: false,
  22. canEdit: false,
  23. dashboardNotFound: true,
  24. },
  25. dashboard: { title },
  26. };
  27. }
  28. loadDashboard(type: UrlQueryValue, slug: any, uid: any) {
  29. let promise;
  30. if (type === 'script') {
  31. promise = this._loadScriptedDashboard(slug);
  32. } else if (type === 'snapshot') {
  33. promise = backendSrv.get('/api/snapshots/' + slug).catch(() => {
  34. return this._dashboardLoadFailed('Snapshot not found', true);
  35. });
  36. } else if (type === 'ds') {
  37. promise = this._loadFromDatasource(slug); // explore dashboards as code
  38. } else {
  39. promise = backendSrv
  40. .getDashboardByUid(uid)
  41. .then((result: any) => {
  42. if (result.meta.isFolder) {
  43. appEvents.emit(AppEvents.alertError, ['Dashboard not found']);
  44. throw new Error('Dashboard not found');
  45. }
  46. return result;
  47. })
  48. .catch(() => {
  49. return this._dashboardLoadFailed('Not found', true);
  50. });
  51. }
  52. promise.then((result: any) => {
  53. if (result.meta.dashboardNotFound !== true) {
  54. impressionSrv.addDashboardImpression(result.dashboard.id);
  55. }
  56. return result;
  57. });
  58. return promise;
  59. }
  60. _loadScriptedDashboard(file: string) {
  61. const url = 'public/dashboards/' + file.replace(/\.(?!js)/, '/') + '?' + new Date().getTime();
  62. return getBackendSrv()
  63. .get(url)
  64. .then(this._executeScript.bind(this))
  65. .then(
  66. (result: any) => {
  67. return {
  68. meta: {
  69. fromScript: true,
  70. canDelete: false,
  71. canSave: false,
  72. canStar: false,
  73. },
  74. dashboard: result.data,
  75. };
  76. },
  77. (err: any) => {
  78. console.error('Script dashboard error ' + err);
  79. appEvents.emit(AppEvents.alertError, [
  80. 'Script Error',
  81. 'Please make sure it exists and returns a valid dashboard',
  82. ]);
  83. return this._dashboardLoadFailed('Scripted dashboard');
  84. }
  85. );
  86. }
  87. /**
  88. * This is a temporary solution to load dashboards dynamically from a datasource
  89. * Eventually this should become a plugin type or a special handler in the dashboard
  90. * loading code
  91. */
  92. async _loadFromDatasource(dsid: string) {
  93. const ds = await getDatasourceSrv().get(dsid);
  94. if (!ds) {
  95. return Promise.reject('can not find datasource: ' + dsid);
  96. }
  97. const params = new URLSearchParams(window.location.search);
  98. const path = params.get('path');
  99. if (!path) {
  100. return Promise.reject('expecting path parameter');
  101. }
  102. const queryParams: { [key: string]: any } = {};
  103. params.forEach((value, key) => {
  104. queryParams[key] = value;
  105. });
  106. return getBackendSrv()
  107. .get(`/api/datasources/${ds.id}/resources/${path}`, queryParams)
  108. .then((data) => {
  109. return {
  110. meta: {
  111. fromScript: true,
  112. canDelete: false,
  113. canSave: false,
  114. canStar: false,
  115. },
  116. dashboard: data,
  117. };
  118. });
  119. }
  120. _executeScript(result: any) {
  121. const services = {
  122. dashboardSrv: getDashboardSrv(),
  123. datasourceSrv: getDatasourceSrv(),
  124. };
  125. const scriptFunc = new Function(
  126. 'ARGS',
  127. 'kbn',
  128. 'dateMath',
  129. '_',
  130. 'moment',
  131. 'window',
  132. 'document',
  133. '$',
  134. 'jQuery',
  135. 'services',
  136. result
  137. );
  138. const scriptResult = scriptFunc(
  139. locationService.getSearchObject(),
  140. kbn,
  141. dateMath,
  142. _,
  143. moment,
  144. window,
  145. document,
  146. $,
  147. $,
  148. services
  149. );
  150. // Handle async dashboard scripts
  151. if (isFunction(scriptResult)) {
  152. return new Promise((resolve) => {
  153. scriptResult((dashboard: any) => {
  154. resolve({ data: dashboard });
  155. });
  156. });
  157. }
  158. return { data: scriptResult };
  159. }
  160. }
  161. let dashboardLoaderSrv = new DashboardLoaderSrv();
  162. export { dashboardLoaderSrv };
  163. /** @internal
  164. * Used for tests only
  165. */
  166. export const setDashboardLoaderSrv = (srv: DashboardLoaderSrv) => {
  167. if (process.env.NODE_ENV !== 'test') {
  168. throw new Error('dashboardLoaderSrv can be only overriden in test environment');
  169. }
  170. dashboardLoaderSrv = srv;
  171. };