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.
180 lines
7.0 KiB
180 lines
7.0 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, { PluginConnectedStatusResponse } 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 PLUGIN_CONFIGURED_VERSION_QUERY_PARAM = 'pluginConfiguredVersion'; |
|
|
|
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 = ( |
|
{ version }: PluginConnectedStatusResponse, |
|
pluginEnabled: boolean |
|
): void => { |
|
if (!pluginEnabled) { |
|
window.location.href = `${window.location.href}?${PLUGIN_CONFIGURED_QUERY_PARAM}=${PLUGIN_CONFIGURED_QUERY_PARAM_TRUTHY_VALUE}&${PLUGIN_CONFIGURED_VERSION_QUERY_PARAM}=${version}`; |
|
} |
|
}; |
|
|
|
/** |
|
* 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 pluginConfiguredVersionQueryParam = queryParams.get(PLUGIN_CONFIGURED_VERSION_QUERY_PARAM); |
|
|
|
const pluginConfiguredRedirect = pluginConfiguredQueryParam === PLUGIN_CONFIGURED_QUERY_PARAM_TRUTHY_VALUE; |
|
|
|
const [checkingIfPluginIsConnected, setCheckingIfPluginIsConnected] = useState<boolean>(!pluginConfiguredRedirect); |
|
const [pluginConnectionCheckError, setPluginConnectionCheckError] = useState<string | null>(null); |
|
|
|
const [pluginIsConnected, setPluginIsConnected] = useState<PluginConnectedStatusResponse | null>( |
|
pluginConfiguredRedirect ? { version: pluginConfiguredVersionQueryParam as string } : null |
|
); |
|
|
|
const [resettingPlugin, setResettingPlugin] = useState<boolean>(false); |
|
const [pluginResetError, setPluginResetError] = useState<string | null>(null); |
|
|
|
const pluginMetaDataExporterApiUrl = jsonData?.dataExporterApiUrl; |
|
|
|
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(() => { |
|
/** |
|
* don't check the plugin status if the user was just redirected after a successful |
|
* plugin setup |
|
*/ |
|
if (!pluginConfiguredRedirect) { |
|
checkConnection(); |
|
} |
|
}, [pluginMetaDataExporterApiUrl, pluginConfiguredRedirect, checkConnection]); |
|
|
|
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 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={checkConnection} 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} |
|
</> |
|
); |
|
};
|
|
|