actions.ts 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. import { DataSourceInstanceSettings, locationUtil } from '@grafana/data';
  2. import { getDataSourceSrv, locationService, getBackendSrv } from '@grafana/runtime';
  3. import { notifyApp } from 'app/core/actions';
  4. import { createErrorNotification } from 'app/core/copy/appNotification';
  5. import { dashboardWatcher } from 'app/features/live/dashboard/dashboardWatcher';
  6. import { DashboardDataDTO, DashboardDTO, FolderInfo, PermissionLevelString, ThunkResult } from 'app/types';
  7. import { LibraryElementExport } from '../../dashboard/components/DashExportModal/DashboardExporter';
  8. import { getLibraryPanel } from '../../library-panels/state/api';
  9. import { LibraryElementDTO, LibraryElementKind } from '../../library-panels/types';
  10. import { DashboardSearchHit } from '../../search/types';
  11. import {
  12. clearDashboard,
  13. fetchDashboard,
  14. fetchFailed,
  15. ImportDashboardDTO,
  16. InputType,
  17. LibraryPanelInput,
  18. LibraryPanelInputState,
  19. setGcomDashboard,
  20. setInputs,
  21. setJsonDashboard,
  22. setLibraryPanelInputs,
  23. } from './reducers';
  24. export function fetchGcomDashboard(id: string): ThunkResult<void> {
  25. return async (dispatch) => {
  26. try {
  27. dispatch(fetchDashboard());
  28. const dashboard = await getBackendSrv().get(`/api/gnet/dashboards/${id}`);
  29. dispatch(setGcomDashboard(dashboard));
  30. dispatch(processInputs(dashboard.json));
  31. dispatch(processElements(dashboard.json));
  32. } catch (error) {
  33. dispatch(fetchFailed());
  34. dispatch(notifyApp(createErrorNotification(error.data.message || error)));
  35. }
  36. };
  37. }
  38. export function importDashboardJson(dashboard: any): ThunkResult<void> {
  39. return async (dispatch) => {
  40. dispatch(setJsonDashboard(dashboard));
  41. dispatch(processInputs(dashboard));
  42. dispatch(processElements(dashboard));
  43. };
  44. }
  45. function processInputs(dashboardJson: any): ThunkResult<void> {
  46. return (dispatch) => {
  47. if (dashboardJson && dashboardJson.__inputs) {
  48. const inputs: any[] = [];
  49. dashboardJson.__inputs.forEach((input: any) => {
  50. const inputModel: any = {
  51. name: input.name,
  52. label: input.label,
  53. info: input.description,
  54. value: input.value,
  55. type: input.type,
  56. pluginId: input.pluginId,
  57. options: [],
  58. };
  59. if (input.type === InputType.DataSource) {
  60. getDataSourceOptions(input, inputModel);
  61. } else if (!inputModel.info) {
  62. inputModel.info = 'Specify a string constant';
  63. }
  64. inputs.push(inputModel);
  65. });
  66. dispatch(setInputs(inputs));
  67. }
  68. };
  69. }
  70. function processElements(dashboardJson?: { __elements?: Record<string, LibraryElementExport> }): ThunkResult<void> {
  71. return async function (dispatch) {
  72. if (!dashboardJson || !dashboardJson.__elements) {
  73. return;
  74. }
  75. const libraryPanelInputs: LibraryPanelInput[] = [];
  76. for (const element of Object.values(dashboardJson.__elements)) {
  77. if (element.kind !== LibraryElementKind.Panel) {
  78. continue;
  79. }
  80. const model = element.model;
  81. const { type, description } = model;
  82. const { uid, name } = element;
  83. const input: LibraryPanelInput = {
  84. model: {
  85. model,
  86. uid,
  87. name,
  88. version: 0,
  89. meta: {},
  90. id: 0,
  91. type,
  92. kind: LibraryElementKind.Panel,
  93. description,
  94. } as LibraryElementDTO,
  95. state: LibraryPanelInputState.New,
  96. };
  97. try {
  98. const panelInDb = await getLibraryPanel(uid, true);
  99. input.state = LibraryPanelInputState.Exists;
  100. input.model = panelInDb;
  101. } catch (e: any) {
  102. if (e.status !== 404) {
  103. throw e;
  104. }
  105. }
  106. libraryPanelInputs.push(input);
  107. }
  108. dispatch(setLibraryPanelInputs(libraryPanelInputs));
  109. };
  110. }
  111. export function clearLoadedDashboard(): ThunkResult<void> {
  112. return (dispatch) => {
  113. dispatch(clearDashboard());
  114. };
  115. }
  116. export function importDashboard(importDashboardForm: ImportDashboardDTO): ThunkResult<void> {
  117. return async (dispatch, getState) => {
  118. const dashboard = getState().importDashboard.dashboard;
  119. const inputs = getState().importDashboard.inputs;
  120. let inputsToPersist = [] as any[];
  121. importDashboardForm.dataSources?.forEach((dataSource: DataSourceInstanceSettings, index: number) => {
  122. const input = inputs.dataSources[index];
  123. inputsToPersist.push({
  124. name: input.name,
  125. type: input.type,
  126. pluginId: input.pluginId,
  127. value: dataSource.uid,
  128. });
  129. });
  130. importDashboardForm.constants?.forEach((constant: any, index: number) => {
  131. const input = inputs.constants[index];
  132. inputsToPersist.push({
  133. value: constant,
  134. name: input.name,
  135. type: input.type,
  136. });
  137. });
  138. const result = await getBackendSrv().post('api/dashboards/import', {
  139. // uid: if user changed it, take the new uid from importDashboardForm,
  140. // else read it from original dashboard
  141. // by default the uid input is disabled, onSubmit ignores values from disabled inputs
  142. dashboard: { ...dashboard, title: importDashboardForm.title, uid: importDashboardForm.uid || dashboard.uid },
  143. overwrite: true,
  144. inputs: inputsToPersist,
  145. folderId: importDashboardForm.folder.id,
  146. });
  147. const dashboardUrl = locationUtil.stripBaseFromUrl(result.importedUrl);
  148. locationService.push(dashboardUrl);
  149. };
  150. }
  151. const getDataSourceOptions = (input: { pluginId: string; pluginName: string }, inputModel: any) => {
  152. const sources = getDataSourceSrv().getList({ pluginId: input.pluginId });
  153. if (sources.length === 0) {
  154. inputModel.info = 'No data sources of type ' + input.pluginName + ' found';
  155. } else if (!inputModel.info) {
  156. inputModel.info = 'Select a ' + input.pluginName + ' data source';
  157. }
  158. };
  159. export function moveDashboards(dashboardUids: string[], toFolder: FolderInfo) {
  160. const tasks = [];
  161. for (const uid of dashboardUids) {
  162. tasks.push(createTask(moveDashboard, true, uid, toFolder));
  163. }
  164. return executeInOrder(tasks).then((result: any) => {
  165. return {
  166. totalCount: result.length,
  167. successCount: result.filter((res: any) => res.succeeded).length,
  168. alreadyInFolderCount: result.filter((res: any) => res.alreadyInFolder).length,
  169. };
  170. });
  171. }
  172. async function moveDashboard(uid: string, toFolder: FolderInfo) {
  173. const fullDash: DashboardDTO = await getBackendSrv().get(`/api/dashboards/uid/${uid}`);
  174. if ((!fullDash.meta.folderId && toFolder.id === 0) || fullDash.meta.folderId === toFolder.id) {
  175. return { alreadyInFolder: true };
  176. }
  177. const options = {
  178. dashboard: fullDash.dashboard,
  179. folderId: toFolder.id,
  180. overwrite: false,
  181. };
  182. try {
  183. await saveDashboard(options);
  184. return { succeeded: true };
  185. } catch (err) {
  186. if (err.data?.status !== 'plugin-dashboard') {
  187. return { succeeded: false };
  188. }
  189. err.isHandled = true;
  190. options.overwrite = true;
  191. try {
  192. await saveDashboard(options);
  193. return { succeeded: true };
  194. } catch (e) {
  195. return { succeeded: false };
  196. }
  197. }
  198. }
  199. function createTask(fn: (...args: any[]) => Promise<any>, ignoreRejections: boolean, ...args: any[]) {
  200. return async (result: any) => {
  201. try {
  202. const res = await fn(...args);
  203. return Array.prototype.concat(result, [res]);
  204. } catch (err) {
  205. if (ignoreRejections) {
  206. return result;
  207. }
  208. throw err;
  209. }
  210. };
  211. }
  212. export function deleteFoldersAndDashboards(folderUids: string[], dashboardUids: string[]) {
  213. const tasks = [];
  214. for (const folderUid of folderUids) {
  215. tasks.push(createTask(deleteFolder, true, folderUid, true));
  216. }
  217. for (const dashboardUid of dashboardUids) {
  218. tasks.push(createTask(deleteDashboard, true, dashboardUid, true));
  219. }
  220. return executeInOrder(tasks);
  221. }
  222. export interface SaveDashboardOptions {
  223. dashboard: DashboardDataDTO;
  224. message?: string;
  225. folderId?: number;
  226. overwrite?: boolean;
  227. }
  228. export function saveDashboard(options: SaveDashboardOptions) {
  229. dashboardWatcher.ignoreNextSave();
  230. return getBackendSrv().post('/api/dashboards/db/', {
  231. dashboard: options.dashboard,
  232. message: options.message ?? '',
  233. overwrite: options.overwrite ?? false,
  234. folderId: options.folderId,
  235. });
  236. }
  237. function deleteFolder(uid: string, showSuccessAlert: boolean) {
  238. return getBackendSrv().request({
  239. method: 'DELETE',
  240. url: `/api/folders/${uid}?forceDeleteRules=false`,
  241. showSuccessAlert: showSuccessAlert,
  242. });
  243. }
  244. export function createFolder(payload: any) {
  245. return getBackendSrv().post('/api/folders', payload);
  246. }
  247. export function searchFolders(
  248. query: any,
  249. permission?: PermissionLevelString,
  250. withAccessControl = false
  251. ): Promise<DashboardSearchHit[]> {
  252. return getBackendSrv().get('/api/search', {
  253. query,
  254. type: 'dash-folder',
  255. permission,
  256. accesscontrol: withAccessControl,
  257. });
  258. }
  259. export function getFolderById(id: number): Promise<{ id: number; title: string }> {
  260. return getBackendSrv().get(`/api/folders/id/${id}`);
  261. }
  262. export function deleteDashboard(uid: string, showSuccessAlert: boolean) {
  263. return getBackendSrv().request({
  264. method: 'DELETE',
  265. url: `/api/dashboards/uid/${uid}`,
  266. showSuccessAlert: showSuccessAlert,
  267. });
  268. }
  269. function executeInOrder(tasks: any[]) {
  270. return tasks.reduce((acc, task) => {
  271. return Promise.resolve(acc).then(task);
  272. }, []);
  273. }