actions.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. import { createAction, createAsyncThunk, Update } from '@reduxjs/toolkit';
  2. import { PanelPlugin } from '@grafana/data';
  3. import { getBackendSrv } from '@grafana/runtime';
  4. import { importPanelPlugin } from 'app/features/plugins/importPanelPlugin';
  5. import { StoreState, ThunkResult } from 'app/types';
  6. import { invalidatePluginInCache } from '../../pluginCacheBuster';
  7. import {
  8. getRemotePlugins,
  9. getPluginErrors,
  10. getLocalPlugins,
  11. getPluginDetails,
  12. installPlugin,
  13. uninstallPlugin,
  14. } from '../api';
  15. import { STATE_PREFIX } from '../constants';
  16. import { mergeLocalsAndRemotes, updatePanels } from '../helpers';
  17. import { CatalogPlugin, RemotePlugin } from '../types';
  18. export const fetchAll = createAsyncThunk(`${STATE_PREFIX}/fetchAll`, async (_, thunkApi) => {
  19. try {
  20. const { dispatch } = thunkApi;
  21. const [localPlugins, pluginErrors, { payload: remotePlugins }] = await Promise.all([
  22. getLocalPlugins(),
  23. getPluginErrors(),
  24. dispatch(fetchRemotePlugins()),
  25. ]);
  26. return mergeLocalsAndRemotes(localPlugins, remotePlugins, pluginErrors);
  27. } catch (e) {
  28. return thunkApi.rejectWithValue('Unknown error.');
  29. }
  30. });
  31. export const fetchRemotePlugins = createAsyncThunk<RemotePlugin[], void, { rejectValue: RemotePlugin[] }>(
  32. `${STATE_PREFIX}/fetchRemotePlugins`,
  33. async (_, thunkApi) => {
  34. try {
  35. return await getRemotePlugins();
  36. } catch (error) {
  37. error.isHandled = true;
  38. return thunkApi.rejectWithValue([]);
  39. }
  40. }
  41. );
  42. export const fetchDetails = createAsyncThunk(`${STATE_PREFIX}/fetchDetails`, async (id: string, thunkApi) => {
  43. try {
  44. const details = await getPluginDetails(id);
  45. return {
  46. id,
  47. changes: { details },
  48. } as Update<CatalogPlugin>;
  49. } catch (e) {
  50. return thunkApi.rejectWithValue('Unknown error.');
  51. }
  52. });
  53. // We are also using the install API endpoint to update the plugin
  54. export const install = createAsyncThunk(
  55. `${STATE_PREFIX}/install`,
  56. async ({ id, version, isUpdating = false }: { id: string; version?: string; isUpdating?: boolean }, thunkApi) => {
  57. const changes = isUpdating
  58. ? { isInstalled: true, installedVersion: version, hasUpdate: false }
  59. : { isInstalled: true, installedVersion: version };
  60. try {
  61. await installPlugin(id);
  62. await updatePanels();
  63. if (isUpdating) {
  64. invalidatePluginInCache(id);
  65. }
  66. return { id, changes } as Update<CatalogPlugin>;
  67. } catch (e) {
  68. console.error(e);
  69. return thunkApi.rejectWithValue('Unknown error.');
  70. }
  71. }
  72. );
  73. export const uninstall = createAsyncThunk(`${STATE_PREFIX}/uninstall`, async (id: string, thunkApi) => {
  74. try {
  75. await uninstallPlugin(id);
  76. await updatePanels();
  77. invalidatePluginInCache(id);
  78. return {
  79. id,
  80. changes: { isInstalled: false, installedVersion: undefined },
  81. } as Update<CatalogPlugin>;
  82. } catch (e) {
  83. console.error(e);
  84. return thunkApi.rejectWithValue('Unknown error.');
  85. }
  86. });
  87. // We need this to be backwards-compatible with other parts of Grafana.
  88. // (Originally in "public/app/features/plugins/state/actions.ts")
  89. // TODO<remove once the "plugin_admin_enabled" feature flag is removed>
  90. export const loadPluginDashboards = createAsyncThunk(`${STATE_PREFIX}/loadPluginDashboards`, async (_, thunkApi) => {
  91. const state = thunkApi.getState() as StoreState;
  92. const dataSourceType = state.dataSources.dataSource.type;
  93. const url = `api/plugins/${dataSourceType}/dashboards`;
  94. return getBackendSrv().get(url);
  95. });
  96. export const panelPluginLoaded = createAction<PanelPlugin>(`${STATE_PREFIX}/panelPluginLoaded`);
  97. // We need this to be backwards-compatible with other parts of Grafana.
  98. // (Originally in "public/app/features/plugins/state/actions.ts")
  99. // It cannot be constructed with `createAsyncThunk()` as we need the return value on the call-site,
  100. // and we cannot easily change the call-site to unwrap the result.
  101. // TODO<remove once the "plugin_admin_enabled" feature flag is removed>
  102. export const loadPanelPlugin = (id: string): ThunkResult<Promise<PanelPlugin>> => {
  103. return async (dispatch, getStore) => {
  104. let plugin = getStore().plugins.panels[id];
  105. if (!plugin) {
  106. plugin = await importPanelPlugin(id);
  107. // second check to protect against raise condition
  108. if (!getStore().plugins.panels[id]) {
  109. dispatch(panelPluginLoaded(plugin));
  110. }
  111. }
  112. return plugin;
  113. };
  114. };