Browse Source

it works

pull/1/head
rozetko 1 year ago
parent
commit
2978d78b2d
  1. 83
      src/components/PluginConfigPage/PluginConfigPage.tsx
  2. 7
      src/plugin.json
  3. 13
      src/plugin_state.ts
  4. 13
      src/services/network_service.ts

83
src/components/PluginConfigPage/PluginConfigPage.tsx

@ -4,7 +4,7 @@ import { Legend, LoadingPlaceholder } from '@grafana/ui';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
// import logo from '../../img/logo.svg'; // import logo from '../../img/logo.svg';
import PluginState from '../../plugin_state'; import PluginState, { PluginConnectedStatusResponse } from '../../plugin_state';
import ConfigurationForm from './parts/ConfigurationForm'; import ConfigurationForm from './parts/ConfigurationForm';
import RemoveCurrentConfigurationButton from './parts/RemoveCurrentConfigurationButton'; import RemoveCurrentConfigurationButton from './parts/RemoveCurrentConfigurationButton';
@ -14,6 +14,8 @@ import { DataExporterPluginConfigPageProps } from 'types';
const PLUGIN_CONFIGURED_QUERY_PARAM = 'pluginConfigured'; const PLUGIN_CONFIGURED_QUERY_PARAM = 'pluginConfigured';
const PLUGIN_CONFIGURED_QUERY_PARAM_TRUTHY_VALUE = 'true'; const PLUGIN_CONFIGURED_QUERY_PARAM_TRUTHY_VALUE = 'true';
const PLUGIN_CONFIGURED_VERSION_QUERY_PARAM = 'pluginConfiguredVersion';
const DEFAULT_API_URL = 'http://localhost:8080'; const DEFAULT_API_URL = 'http://localhost:8080';
/** /**
@ -22,9 +24,12 @@ const DEFAULT_API_URL = 'http://localhost:8080';
* *
* Don't refresh the page if the plugin is already enabled.. * Don't refresh the page if the plugin is already enabled..
*/ */
export const reloadPageWithPluginConfiguredQueryParams = (pluginEnabled: boolean): void => { export const reloadPageWithPluginConfiguredQueryParams = (
{ version }: PluginConnectedStatusResponse,
pluginEnabled: boolean
): void => {
if (!pluginEnabled) { if (!pluginEnabled) {
window.location.href = `${window.location.href}?${PLUGIN_CONFIGURED_QUERY_PARAM}=${PLUGIN_CONFIGURED_QUERY_PARAM_TRUTHY_VALUE}`; window.location.href = `${window.location.href}?${PLUGIN_CONFIGURED_QUERY_PARAM}=${PLUGIN_CONFIGURED_QUERY_PARAM_TRUTHY_VALUE}&${PLUGIN_CONFIGURED_VERSION_QUERY_PARAM}=${version}`;
} }
}; };
@ -48,71 +53,53 @@ export const PluginConfigPage: FC<DataExporterPluginConfigPageProps> = ({
const { search } = useLocation(); const { search } = useLocation();
const queryParams = new URLSearchParams(search); const queryParams = new URLSearchParams(search);
const pluginConfiguredQueryParam = queryParams.get(PLUGIN_CONFIGURED_QUERY_PARAM); const pluginConfiguredQueryParam = queryParams.get(PLUGIN_CONFIGURED_QUERY_PARAM);
const pluginConfiguredVersionQueryParam = queryParams.get(PLUGIN_CONFIGURED_VERSION_QUERY_PARAM);
const pluginConfiguredRedirect = pluginConfiguredQueryParam === PLUGIN_CONFIGURED_QUERY_PARAM_TRUTHY_VALUE; const pluginConfiguredRedirect = pluginConfiguredQueryParam === PLUGIN_CONFIGURED_QUERY_PARAM_TRUTHY_VALUE;
console.log(pluginConfiguredRedirect);
const [checkingIfPluginIsConnected, setCheckingIfPluginIsConnected] = useState<boolean>(!pluginConfiguredRedirect); const [checkingIfPluginIsConnected, setCheckingIfPluginIsConnected] = useState<boolean>(!pluginConfiguredRedirect);
const [pluginConnectionCheckError, setPluginConnectionCheckError] = useState<string | null>(null); const [pluginConnectionCheckError, setPluginConnectionCheckError] = useState<string | null>(null);
const [pluginIsConnected, setPluginIsConnected] = useState<Boolean | null>(pluginConfiguredRedirect); const [pluginIsConnected, setPluginIsConnected] = useState<PluginConnectedStatusResponse | null>(
pluginConfiguredRedirect ? { version: pluginConfiguredVersionQueryParam as string } : null
);
const [resettingPlugin, setResettingPlugin] = useState<boolean>(false); const [resettingPlugin, setResettingPlugin] = useState<boolean>(false);
const [pluginResetError, setPluginResetError] = useState<string | null>(null); const [pluginResetError, setPluginResetError] = useState<string | null>(null);
const pluginMetaDataExporterApiUrl = jsonData?.dataExporterApiUrl; const pluginMetaDataExporterApiUrl = jsonData?.dataExporterApiUrl;
const dataExporterApiUrl = pluginMetaDataExporterApiUrl || DEFAULT_API_URL;
const resetQueryParams = useCallback(() => removePluginConfiguredQueryParams(pluginIsEnabled), [pluginIsEnabled]); const resetQueryParams = useCallback(() => removePluginConfiguredQueryParams(pluginIsEnabled), [pluginIsEnabled]);
const checkConnection = useCallback(async () => {
setCheckingIfPluginIsConnected(true);
setPluginConnectionCheckError(null);
if (!pluginMetaDataExporterApiUrl) {
setCheckingIfPluginIsConnected(false);
return;
}
const pluginConnectionResponse = await PluginState.checkIfPluginIsConnected(pluginMetaDataExporterApiUrl);
if (typeof pluginConnectionResponse === 'string') {
setPluginConnectionCheckError(pluginConnectionResponse);
} else {
setPluginIsConnected(pluginConnectionResponse);
}
setCheckingIfPluginIsConnected(false);
}, [pluginMetaDataExporterApiUrl]);
useEffect(resetQueryParams, [resetQueryParams]); useEffect(resetQueryParams, [resetQueryParams]);
useEffect(() => { useEffect(() => {
const configurePluginAndSyncData = async () => {
/**
* If the plugin has never been configured, DataExporterApiUrl will be undefined in the plugin's jsonData
* In that case, check to see if DataExporter_API_URL has been supplied as an env var.
* Supplying the env var basically allows to skip the configuration form
* (check webpack.config.js to see how this is set)
*/
console.log(pluginMetaDataExporterApiUrl);
if (!pluginMetaDataExporterApiUrl) {
/**
* DataExporterApiUrl is not yet saved in the grafana plugin settings, but has been supplied as an env var
* lets auto-trigger a self-hosted plugin install w/ the DataExporterApiUrl passed in as an env var
*/
const errorMsg = await PluginState.installPlugin(dataExporterApiUrl);
if (errorMsg) {
setPluginConnectionCheckError(errorMsg);
setCheckingIfPluginIsConnected(false);
return;
}
}
/**
* If the DataExporterApiUrl is not set in the plugin settings, and not supplied via an env var
* there's no reason to check if the plugin is connected, we know it can't be
*/
if (dataExporterApiUrl) {
const pluginConnectionResponse = await PluginState.checkIfPluginIsConnected(dataExporterApiUrl);
console.log(pluginConnectionResponse);
if (typeof pluginConnectionResponse === 'string') {
setPluginConnectionCheckError(pluginConnectionResponse);
}
}
setCheckingIfPluginIsConnected(false);
};
/** /**
* don't check the plugin status if the user was just redirected after a successful * don't check the plugin status if the user was just redirected after a successful
* plugin setup * plugin setup
*/ */
if (!pluginConfiguredRedirect) { if (!pluginConfiguredRedirect) {
configurePluginAndSyncData(); checkConnection();
} }
}, [pluginMetaDataExporterApiUrl, dataExporterApiUrl, pluginConfiguredRedirect]); }, [pluginMetaDataExporterApiUrl, pluginConfiguredRedirect, checkConnection]);
const resetState = useCallback(() => { const resetState = useCallback(() => {
setPluginResetError(null); setPluginResetError(null);
@ -150,10 +137,6 @@ export const PluginConfigPage: FC<DataExporterPluginConfigPageProps> = ({
setResettingPlugin(false); setResettingPlugin(false);
}, [resetState]); }, [resetState]);
const onSuccessfulSetup = useCallback(async () => {
reloadPageWithPluginConfiguredQueryParams(false);
}, []);
const RemoveConfigButton = useCallback( const RemoveConfigButton = useCallback(
() => <RemoveCurrentConfigurationButton disabled={resettingPlugin} onClick={triggerPluginReset} />, () => <RemoveCurrentConfigurationButton disabled={resettingPlugin} onClick={triggerPluginReset} />,
[resettingPlugin, triggerPluginReset] [resettingPlugin, triggerPluginReset]
@ -171,7 +154,7 @@ export const PluginConfigPage: FC<DataExporterPluginConfigPageProps> = ({
</> </>
); );
} else if (!pluginIsConnected) { } else if (!pluginIsConnected) {
content = <ConfigurationForm onSuccessfulSetup={onSuccessfulSetup} defaultDataExporterApiUrl={DEFAULT_API_URL} />; content = <ConfigurationForm onSuccessfulSetup={checkConnection} defaultDataExporterApiUrl={DEFAULT_API_URL} />;
} else { } else {
// plugin is fully connected and synced // plugin is fully connected and synced
content = <RemoveConfigButton />; content = <RemoveConfigButton />;

7
src/plugin.json

@ -27,6 +27,13 @@
"defaultNav": true "defaultNav": true
} }
], ],
"routes": [
{
"path": "api/*",
"method": "*",
"url": "{{ .JsonData.dataExporterApiUrl }}/api/"
}
],
"dependencies": { "dependencies": {
"grafanaDependency": ">=7.0.0", "grafanaDependency": ">=7.0.0",
"plugins": [] "plugins": []

13
src/plugin_state.ts

@ -21,12 +21,11 @@ type InstallPluginResponse<DataExporterAPIResponse = any> = Pick<
dataExporterAPIResponse: DataExporterAPIResponse; dataExporterAPIResponse: DataExporterAPIResponse;
}; };
type PluginConnectedStatusResponse = { export type PluginConnectedStatusResponse = {
version: string; version: string;
}; };
class PluginState { class PluginState {
static DATA_EXPORTER_BASE_URL = '/plugin';
static GRAFANA_PLUGIN_SETTINGS_URL = '/api/plugins/corpglory-dataexporter-app/settings'; static GRAFANA_PLUGIN_SETTINGS_URL = '/api/plugins/corpglory-dataexporter-app/settings';
static grafanaBackend = getBackendSrv(); static grafanaBackend = getBackendSrv();
@ -111,17 +110,17 @@ class PluginState {
static timeout = (pollCount: number) => new Promise((resolve) => setTimeout(resolve, 10 * 2 ** pollCount)); static timeout = (pollCount: number) => new Promise((resolve) => setTimeout(resolve, 10 * 2 ** pollCount));
static connectBackend = async (): Promise<InstallPluginResponse<string>> => { static connectBackend = async <RT>(): Promise<InstallPluginResponse<RT>> => {
const { key: grafanaToken } = await this.createGrafanaToken(); const { key: grafanaToken } = await this.createGrafanaToken();
await this.updateGrafanaPluginSettings({ secureJsonData: { grafanaToken } }); await this.updateGrafanaPluginSettings({ secureJsonData: { grafanaToken } });
const dataExporterAPIResponse = await makeRequest<string>(`${this.DATA_EXPORTER_BASE_URL}/connect`, { const dataExporterAPIResponse = await makeRequest<RT>(`/connect`, {
method: 'POST', method: 'POST',
}); });
return { grafanaToken, dataExporterAPIResponse }; return { grafanaToken, dataExporterAPIResponse };
}; };
static installPlugin = async (dataExporterApiUrl: string): Promise<string | null> => { static installPlugin = async (dataExporterApiUrl: string): Promise<string | null> => {
let pluginInstallationDataExporterResponse: InstallPluginResponse<string>; let pluginInstallationDataExporterResponse: InstallPluginResponse<{ version: string }>;
// Step 1. Try provisioning the plugin w/ the Grafana API // Step 1. Try provisioning the plugin w/ the Grafana API
try { try {
@ -137,7 +136,7 @@ class PluginState {
* - configure the plugin in DataExporter's backend * - configure the plugin in DataExporter's backend
*/ */
try { try {
pluginInstallationDataExporterResponse = await this.connectBackend(); pluginInstallationDataExporterResponse = await this.connectBackend<{ version: string }>();
} catch (e) { } catch (e) {
return this.getHumanReadableErrorFromDataExporterError(e, dataExporterApiUrl); return this.getHumanReadableErrorFromDataExporterError(e, dataExporterApiUrl);
} }
@ -165,7 +164,7 @@ class PluginState {
dataExporterApiUrl: string dataExporterApiUrl: string
): Promise<PluginConnectedStatusResponse | string> => { ): Promise<PluginConnectedStatusResponse | string> => {
try { try {
const resp = await makeRequest<PluginConnectedStatusResponse>(`${this.DATA_EXPORTER_BASE_URL}/status`, { const resp = await makeRequest<PluginConnectedStatusResponse>(`/status`, {
method: 'GET', method: 'GET',
}); });

13
src/services/network_service.ts

@ -2,9 +2,20 @@ import axios from 'axios';
export const API_HOST = `${window.location.protocol}//${window.location.host}/`; export const API_HOST = `${window.location.protocol}//${window.location.host}/`;
export const API_PROXY_PREFIX = 'api/plugin-proxy/corpglory-dataexporter-app'; export const API_PROXY_PREFIX = 'api/plugin-proxy/corpglory-dataexporter-app';
export const API_PATH_PREFIX = '/api';
const instance = axios.create(); const instance = axios.create();
instance.interceptors.request.use(function (config) {
config.validateStatus = (status) => {
return status >= 200 && status < 300; // default
};
return {
...config,
};
});
interface RequestConfig { interface RequestConfig {
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'OPTIONS'; method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'OPTIONS';
params?: any; params?: any;
@ -16,7 +27,7 @@ interface RequestConfig {
export const makeRequest = async <RT = any>(path: string, config: RequestConfig) => { export const makeRequest = async <RT = any>(path: string, config: RequestConfig) => {
const { method = 'GET', params, data, validateStatus } = config; const { method = 'GET', params, data, validateStatus } = config;
const url = `${API_PROXY_PREFIX}${path}`; const url = `${API_PROXY_PREFIX}${API_PATH_PREFIX}${path}`;
const response = await instance({ const response = await instance({
method, method,

Loading…
Cancel
Save