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; }; };
Edit
Rename
Chmod
Delete
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