Submit
Path:
~
/
/
usr
/
share
/
grafana
/
public
/
app
/
features
/
plugins
/
admin
/
state
/
File Content:
actions.ts
import { createAction, createAsyncThunk, Update } from '@reduxjs/toolkit'; import { from, forkJoin, timeout, lastValueFrom, catchError, of } from 'rxjs'; import { PanelPlugin, PluginError } from '@grafana/data'; import { config, getBackendSrv, isFetchError } from '@grafana/runtime'; import configCore from 'app/core/config'; import { importPanelPlugin } from 'app/features/plugins/importPanelPlugin'; import { StoreState, ThunkResult } from 'app/types'; import { invalidatePluginInCache } from '../../loader/cache'; import { getRemotePlugins, getPluginErrors, getLocalPlugins, getPluginDetails, installPlugin, uninstallPlugin, getInstancePlugins, } from '../api'; import { STATE_PREFIX } from '../constants'; import { mapLocalToCatalog, mergeLocalsAndRemotes, updatePanels } from '../helpers'; import { CatalogPlugin, RemotePlugin, LocalPlugin, InstancePlugin } from '../types'; // Fetches export const fetchAll = createAsyncThunk(`${STATE_PREFIX}/fetchAll`, async (_, thunkApi) => { try { thunkApi.dispatch({ type: `${STATE_PREFIX}/fetchLocal/pending` }); thunkApi.dispatch({ type: `${STATE_PREFIX}/fetchRemote/pending` }); const instance$ = config.pluginAdminExternalManageEnabled && configCore.featureToggles.managedPluginsInstall ? from(getInstancePlugins()) : of(undefined); const TIMEOUT = 500; const pluginErrors$ = from(getPluginErrors()); const local$ = from(getLocalPlugins()); // Unknown error while fetching remote plugins from GCOM. // (In this case we still operate, but only with locally available plugins.) const remote$ = from(getRemotePlugins()).pipe( catchError((err) => { thunkApi.dispatch({ type: `${STATE_PREFIX}/fetchRemote/rejected` }); console.error(err); return of([]); }) ); forkJoin({ local: local$, remote: remote$, instance: instance$, pluginErrors: pluginErrors$, }) .pipe( // Fetching the list of plugins from GCOM is slow / times out // (We are waiting for TIMEOUT, and if there is still no response from GCOM we continue with locally // installed plugins only by returning a new observable. We also still wait for the remote request to finish or // time out, but we don't block the main execution flow.) timeout({ each: TIMEOUT, with: () => { remote$ // Remote plugins loaded after a timeout, updating the store .subscribe(async (remote: RemotePlugin[]) => { thunkApi.dispatch({ type: `${STATE_PREFIX}/fetchRemote/fulfilled` }); if (remote.length > 0) { const local = await lastValueFrom(local$); const instance = await lastValueFrom(instance$); const pluginErrors = await lastValueFrom(pluginErrors$); thunkApi.dispatch(addPlugins(mergeLocalsAndRemotes({ local, remote, instance, pluginErrors }))); } }); return forkJoin({ local: local$, instance: instance$, pluginErrors: pluginErrors$ }); }, }) ) .subscribe( ({ local, remote, instance, pluginErrors, }: { local: LocalPlugin[]; remote?: RemotePlugin[]; instance?: InstancePlugin[]; pluginErrors: PluginError[]; }) => { // Both local and remote plugins are loaded if (local && remote) { thunkApi.dispatch({ type: `${STATE_PREFIX}/fetchLocal/fulfilled` }); thunkApi.dispatch({ type: `${STATE_PREFIX}/fetchRemote/fulfilled` }); thunkApi.dispatch(addPlugins(mergeLocalsAndRemotes({ local, remote, instance, pluginErrors }))); // Only remote plugins are loaded (remote timed out) } else if (local) { thunkApi.dispatch({ type: `${STATE_PREFIX}/fetchLocal/fulfilled` }); thunkApi.dispatch(addPlugins(mergeLocalsAndRemotes({ local, pluginErrors }))); } }, (error) => { console.log(error); thunkApi.dispatch({ type: `${STATE_PREFIX}/fetchLocal/rejected` }); thunkApi.dispatch({ type: `${STATE_PREFIX}/fetchRemote/rejected` }); return thunkApi.rejectWithValue('Unknown error.'); } ); return null; } catch (e) { return thunkApi.rejectWithValue('Unknown error.'); } }); export const fetchAllLocal = createAsyncThunk(`${STATE_PREFIX}/fetchAllLocal`, async (_, thunkApi) => { try { const localPlugins = await getLocalPlugins(); return localPlugins.map((plugin: LocalPlugin) => mapLocalToCatalog(plugin)); } catch (e) { return thunkApi.rejectWithValue('Unknown error.'); } }); export const fetchRemotePlugins = createAsyncThunk<RemotePlugin[], void, { rejectValue: RemotePlugin[] }>( `${STATE_PREFIX}/fetchRemotePlugins`, async (_, thunkApi) => { try { return await getRemotePlugins(); } catch (error) { if (isFetchError(error)) { error.isHandled = true; } return thunkApi.rejectWithValue([]); } } ); export const fetchDetails = createAsyncThunk<Update<CatalogPlugin>, string>( `${STATE_PREFIX}/fetchDetails`, async (id: string, thunkApi) => { try { const details = await getPluginDetails(id); return { id, changes: { details }, }; } catch (e) { return thunkApi.rejectWithValue('Unknown error.'); } } ); export const addPlugins = createAction<CatalogPlugin[]>(`${STATE_PREFIX}/addPlugins`); // 1. gets remote equivalents from the store (if there are any) // 2. merges the remote equivalents with the local plugins // 3. updates the the store with the updated CatalogPlugin objects export const addLocalPlugins = createAction<LocalPlugin[]>(`${STATE_PREFIX}/addLocalPlugins`); // 1. gets local equivalents from the store (if there are any) // 2. merges the local equivalents with the remote plugins // 3. updates the the store with the updated CatalogPlugin objects export const addRemotePlugins = createAction<RemotePlugin[]>(`${STATE_PREFIX}/addLocalPlugins`); // 1. merges the local and remote plugins // 2. updates the store with the CatalogPlugin objects export const addLocalAndRemotePlugins = createAction<{ local: LocalPlugin[]; remote: RemotePlugin[] }>( `${STATE_PREFIX}/addLocalPlugins` ); // We are also using the install API endpoint to update the plugin export const install = createAsyncThunk< Update<CatalogPlugin>, { id: string; version?: string; isUpdating?: boolean; } >(`${STATE_PREFIX}/install`, async ({ id, version, isUpdating = false }, thunkApi) => { const changes = isUpdating ? { isInstalled: true, installedVersion: version, hasUpdate: false } : { isInstalled: true, installedVersion: version }; try { await installPlugin(id); await updatePanels(); if (isUpdating) { invalidatePluginInCache(id); } return { id, changes }; } catch (e) { console.error(e); if (isFetchError(e)) { return thunkApi.rejectWithValue(e.data); } return thunkApi.rejectWithValue('Unknown error.'); } }); export const unsetInstall = createAsyncThunk(`${STATE_PREFIX}/install`, async () => ({})); export const uninstall = createAsyncThunk<Update<CatalogPlugin>, string>( `${STATE_PREFIX}/uninstall`, async (id, thunkApi) => { try { await uninstallPlugin(id); await updatePanels(); invalidatePluginInCache(id); return { id, changes: { isInstalled: false, installedVersion: undefined }, }; } catch (e) { console.error(e); return thunkApi.rejectWithValue('Unknown error.'); } } ); // We need this to be backwards-compatible with other parts of Grafana. // (Originally in "public/app/features/plugins/state/actions.ts") // TODO<remove once the "plugin_admin_enabled" feature flag is removed> export const loadPluginDashboards = createAsyncThunk(`${STATE_PREFIX}/loadPluginDashboards`, async (_, thunkApi) => { const state = thunkApi.getState() as StoreState; const dataSourceType = state.dataSources.dataSource.type; const url = `api/plugins/${dataSourceType}/dashboards`; return getBackendSrv().get(url); }); export const panelPluginLoaded = createAction<PanelPlugin>(`${STATE_PREFIX}/panelPluginLoaded`); // We need this to be backwards-compatible with other parts of Grafana. // (Originally in "public/app/features/plugins/state/actions.ts") // It cannot be constructed with `createAsyncThunk()` as we need the return value on the call-site, // and we cannot easily change the call-site to unwrap the result. // TODO<remove once the "plugin_admin_enabled" feature flag is removed> export const loadPanelPlugin = (id: string): ThunkResult<Promise<PanelPlugin>> => { return async (dispatch, getStore) => { let plugin = getStore().plugins.panels[id]; if (!plugin) { plugin = await importPanelPlugin(id); // second check to protect against raise condition if (!getStore().plugins.panels[id]) { dispatch(panelPluginLoaded(plugin)); } } return plugin; }; };
Submit
FILE
FOLDER
INFO
Name
Size
Permission
Action
actions.ts
9228 bytes
0644
hooks.ts
4993 bytes
0644
reducer.ts
4279 bytes
0644
selectors.test.ts
2881 bytes
0644
selectors.ts
3133 bytes
0644
N4ST4R_ID | Naxtarrr