You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

197 lines
7.7 KiB

import React, { FC, useCallback, useEffect, useState } from 'react';
import { Legend, LoadingPlaceholder } from '@grafana/ui';
import { useLocation } from 'react-router-dom';
// import logo from '../../img/logo.svg';
import PluginState from '../../plugin_state';
import ConfigurationForm from './parts/ConfigurationForm';
import RemoveCurrentConfigurationButton from './parts/RemoveCurrentConfigurationButton';
import StatusMessageBlock from './parts/StatusMessageBlock';
import { DataExporterPluginConfigPageProps } from 'types';
const PLUGIN_CONFIGURED_QUERY_PARAM = 'pluginConfigured';
const PLUGIN_CONFIGURED_QUERY_PARAM_TRUTHY_VALUE = 'true';
const DEFAULT_API_URL = 'http://localhost:8080';
/**
* When everything is successfully configured, reload the page, and pass along a few query parameters
* so that we avoid an infinite configuration-check/data-sync loop
*
* Don't refresh the page if the plugin is already enabled..
*/
export const reloadPageWithPluginConfiguredQueryParams = (pluginEnabled: boolean): void => {
if (!pluginEnabled) {
window.location.href = `${window.location.href}?${PLUGIN_CONFIGURED_QUERY_PARAM}=${PLUGIN_CONFIGURED_QUERY_PARAM_TRUTHY_VALUE}`;
}
};
/**
* remove the query params used to track state for a page reload after successful configuration, without triggering
* a page reload
* https://stackoverflow.com/a/19279428
*/
export const removePluginConfiguredQueryParams = (pluginIsEnabled?: boolean): void => {
if (window.history.pushState && pluginIsEnabled) {
const newurl = `${window.location.protocol}//${window.location.host}${window.location.pathname}`;
window.history.pushState({ path: newurl }, '', newurl);
}
};
export const PluginConfigPage: FC<DataExporterPluginConfigPageProps> = ({
plugin: {
meta: { jsonData, enabled: pluginIsEnabled },
},
}) => {
const { search } = useLocation();
const queryParams = new URLSearchParams(search);
const pluginConfiguredQueryParam = queryParams.get(PLUGIN_CONFIGURED_QUERY_PARAM);
const pluginConfiguredRedirect = pluginConfiguredQueryParam === PLUGIN_CONFIGURED_QUERY_PARAM_TRUTHY_VALUE;
console.log(pluginConfiguredRedirect);
const [checkingIfPluginIsConnected, setCheckingIfPluginIsConnected] = useState<boolean>(!pluginConfiguredRedirect);
const [pluginConnectionCheckError, setPluginConnectionCheckError] = useState<string | null>(null);
const [pluginIsConnected, setPluginIsConnected] = useState<Boolean | null>(pluginConfiguredRedirect);
const [resettingPlugin, setResettingPlugin] = useState<boolean>(false);
const [pluginResetError, setPluginResetError] = useState<string | null>(null);
const pluginMetaDataExporterApiUrl = jsonData?.dataExporterApiUrl;
const dataExporterApiUrl = pluginMetaDataExporterApiUrl || DEFAULT_API_URL;
const resetQueryParams = useCallback(() => removePluginConfiguredQueryParams(pluginIsEnabled), [pluginIsEnabled]);
useEffect(resetQueryParams, [resetQueryParams]);
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
* plugin setup
*/
if (!pluginConfiguredRedirect) {
configurePluginAndSyncData();
}
}, [pluginMetaDataExporterApiUrl, dataExporterApiUrl, pluginConfiguredRedirect]);
const resetState = useCallback(() => {
setPluginResetError(null);
setPluginConnectionCheckError(null);
setPluginIsConnected(null);
resetQueryParams();
}, [resetQueryParams]);
/**
* NOTE: there is a possible edge case when resetting the plugin, that would lead to an error message being shown
* (which could be fixed by just reloading the page)
* This would happen if the user removes the plugin configuration, leaves the page, then comes back to the plugin
* configuration.
*
* This is because the props being passed into this component wouldn't reflect the actual plugin
* provisioning state. The props would still have DataExporterApiUrl set in the plugin jsonData, so when we make the API
* call to check the plugin state w/ DataExporter API the plugin-proxy would return a 502 Bad Gateway because the actual
* provisioned plugin doesn't know about the DataExporterApiUrl.
*
* This could be fixed by instead of passing in the plugin provisioning information as props always fetching it
* when this component renders (via a useEffect). We probably don't need to worry about this because it should happen
* very rarely, if ever
*/
const triggerPluginReset = useCallback(async () => {
setResettingPlugin(true);
resetState();
try {
await PluginState.resetPlugin();
} catch (e) {
// this should rarely, if ever happen, but we should handle the case nevertheless
setPluginResetError('There was an error resetting your plugin, try again.');
}
setResettingPlugin(false);
}, [resetState]);
const onSuccessfulSetup = useCallback(async () => {
reloadPageWithPluginConfiguredQueryParams(false);
}, []);
const RemoveConfigButton = useCallback(
() => <RemoveCurrentConfigurationButton disabled={resettingPlugin} onClick={triggerPluginReset} />,
[resettingPlugin, triggerPluginReset]
);
let content: React.ReactNode;
if (checkingIfPluginIsConnected) {
content = <LoadingPlaceholder text="Validating your plugin connection..." />;
} else if (pluginConnectionCheckError || pluginResetError) {
content = (
<>
<StatusMessageBlock text={(pluginConnectionCheckError || pluginResetError) as string} />
<RemoveConfigButton />
</>
);
} else if (!pluginIsConnected) {
content = <ConfigurationForm onSuccessfulSetup={onSuccessfulSetup} defaultDataExporterApiUrl={DEFAULT_API_URL} />;
} else {
// plugin is fully connected and synced
content = <RemoveConfigButton />;
}
return (
<>
<Legend>Configure DataExporter</Legend>
{pluginIsConnected ? (
<>
<p>
Plugin is connected! Continue to DataExporter by clicking the{' '}
{/* <img alt="DataExporter Logo" src={logo} width={18} /> icon over there 👈 */}
</p>
<StatusMessageBlock text={`Connected to DataExporter`} />
</>
) : (
<p>This page will help you configure the DataExporter plugin 👋</p>
)}
{content}
</>
);
};