// Libraries import React, { PureComponent } from 'react'; import { hot } from 'react-hot-loader'; import { connect } from 'react-redux'; import isString from 'lodash/isString'; // Components import Page from 'app/core/components/Page/Page'; import { PluginSettings, GenericDataSourcePlugin } from './PluginSettings'; import BasicSettings from './BasicSettings'; import ButtonRow from './ButtonRow'; // Services & Utils import appEvents from 'app/core/app_events'; import { getBackendSrv } from 'app/core/services/backend_srv'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; // Actions & selectors import { getDataSource, getDataSourceMeta } from '../state/selectors'; import { deleteDataSource, loadDataSource, setDataSourceName, setIsDefault, updateDataSource } from '../state/actions'; import { getNavModel } from 'app/core/selectors/navModel'; import { getRouteParamsId } from 'app/core/selectors/location'; // Types import { StoreState, UrlQueryMap } from 'app/types/'; import { NavModel, DataSourceSettings, DataSourcePluginMeta } from '@grafana/ui'; import { getDataSourceLoadingNav } from '../state/navModel'; import PluginStateinfo from 'app/features/plugins/PluginStateInfo'; import { importDataSourcePlugin } from 'app/features/plugins/plugin_loader'; export interface Props { navModel: NavModel; dataSource: DataSourceSettings; dataSourceMeta: DataSourcePluginMeta; pageId: number; deleteDataSource: typeof deleteDataSource; loadDataSource: typeof loadDataSource; setDataSourceName: typeof setDataSourceName; updateDataSource: typeof updateDataSource; setIsDefault: typeof setIsDefault; plugin?: GenericDataSourcePlugin; query: UrlQueryMap; page?: string; } interface State { dataSource: DataSourceSettings; plugin?: GenericDataSourcePlugin; isTesting?: boolean; testingMessage?: string; testingStatus?: string; loadError?: any; } export class DataSourceSettingsPage extends PureComponent { constructor(props: Props) { super(props); this.state = { dataSource: props.dataSource, plugin: props.plugin, }; } async loadPlugin(pluginId?: string) { const { dataSourceMeta } = this.props; let importedPlugin: GenericDataSourcePlugin; try { importedPlugin = await importDataSourcePlugin(dataSourceMeta); } catch (e) { console.log('Failed to import plugin module', e); } this.setState({ plugin: importedPlugin }); } async componentDidMount() { const { loadDataSource, pageId } = this.props; if (isNaN(pageId)) { this.setState({ loadError: 'Invalid ID' }); return; } try { await loadDataSource(pageId); if (!this.state.plugin) { await this.loadPlugin(); } } catch (err) { this.setState({ loadError: err }); } } componentDidUpdate(prevProps: Props) { const { dataSource } = this.props; if (prevProps.dataSource !== dataSource) { this.setState({ dataSource }); } } onSubmit = async (evt: React.FormEvent) => { evt.preventDefault(); await this.props.updateDataSource({ ...this.state.dataSource }); this.testDataSource(); }; onTest = async (evt: React.FormEvent) => { evt.preventDefault(); this.testDataSource(); }; onDelete = () => { appEvents.emit('confirm-modal', { title: 'Delete', text: 'Are you sure you want to delete this data source?', yesText: 'Delete', icon: 'fa-trash', onConfirm: () => { this.confirmDelete(); }, }); }; confirmDelete = () => { this.props.deleteDataSource(); }; onModelChange = (dataSource: DataSourceSettings) => { this.setState({ dataSource }); }; isReadOnly() { return this.props.dataSource.readOnly === true; } renderIsReadOnlyMessage() { return (
This datasource was added by config and cannot be modified using the UI. Please contact your server admin to update this datasource.
); } async testDataSource() { const dsApi = await getDatasourceSrv().get(this.state.dataSource.name); if (!dsApi.testDatasource) { return; } this.setState({ isTesting: true, testingMessage: 'Testing...', testingStatus: 'info' }); getBackendSrv().withNoBackendCache(async () => { try { const result = await dsApi.testDatasource(); this.setState({ isTesting: false, testingStatus: result.status, testingMessage: result.message, }); } catch (err) { let message = ''; if (err.statusText) { message = 'HTTP Error ' + err.statusText; } else { message = err.message; } this.setState({ isTesting: false, testingStatus: 'error', testingMessage: message, }); } }); } get hasDataSource() { return this.state.dataSource.id > 0; } renderLoadError(loadError: any) { let showDelete = false; let msg = loadError.toString(); if (loadError.data) { if (loadError.data.message) { msg = loadError.data.message; } } else if (isString(loadError)) { showDelete = true; } const node = { text: msg, subTitle: 'Data Source Error', icon: 'fa fa-fw fa-warning', }; const nav = { node: node, main: node, }; return (
{showDelete && ( )} Back
); } renderConfigPageBody(page: string) { const { plugin } = this.state; if (!plugin || !plugin.configPages) { return null; // still loading } for (const p of plugin.configPages) { if (p.id === page) { return ; } } return
Page Not Found: {page}
; } renderSettings() { const { dataSourceMeta, setDataSourceName, setIsDefault } = this.props; const { testingMessage, testingStatus, dataSource, plugin } = this.state; return (
{this.isReadOnly() && this.renderIsReadOnlyMessage()} {dataSourceMeta.state && (
)} setIsDefault(state)} onNameChange={name => setDataSourceName(name)} /> {plugin && ( )}
{testingMessage && (
{testingStatus === 'error' ? ( ) : ( )}
{testingMessage}
)}
this.onSubmit(event)} isReadOnly={this.isReadOnly()} onDelete={this.onDelete} onTest={event => this.onTest(event)} /> ); } render() { const { navModel, page } = this.props; const { loadError } = this.state; if (loadError) { return this.renderLoadError(loadError); } return ( {this.hasDataSource &&
{page ? this.renderConfigPageBody(page) : this.renderSettings()}
}
); } } function mapStateToProps(state: StoreState) { const pageId = getRouteParamsId(state.location); const dataSource = getDataSource(state.dataSources, pageId); const page = state.location.query.page as string; return { navModel: getNavModel( state.navIndex, page ? `datasource-page-${page}` : `datasource-settings-${pageId}`, getDataSourceLoadingNav('settings') ), dataSource: getDataSource(state.dataSources, pageId), dataSourceMeta: getDataSourceMeta(state.dataSources, dataSource.type), pageId: pageId, query: state.location.query, page, }; } const mapDispatchToProps = { deleteDataSource, loadDataSource, setDataSourceName, updateDataSource, setIsDefault, }; export default hot(module)( connect( mapStateToProps, mapDispatchToProps )(DataSourceSettingsPage) );