DataSourceSettings.tsx 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. import React, { PureComponent } from 'react';
  2. import { hot } from 'react-hot-loader';
  3. import { connect } from 'react-redux';
  4. import { DataSource, DataSourceTest, NavModel, Plugin } from 'app/types/';
  5. import PageHeader from '../../../core/components/PageHeader/PageHeader';
  6. import PageLoader from '../../../core/components/PageLoader/PageLoader';
  7. import PluginSettings from './PluginSettings';
  8. import BasicSettings from './BasicSettings';
  9. import ButtonRow from './ButtonRow';
  10. import appEvents from '../../../core/app_events';
  11. import { clearTesting, deleteDataSource, loadDataSource, setDataSourceName, updateDataSource } from '../state/actions';
  12. import { getNavModel } from '../../../core/selectors/navModel';
  13. import { getRouteParamsId } from '../../../core/selectors/location';
  14. import { getDataSource, getDataSourceMeta } from '../state/selectors';
  15. export interface Props {
  16. navModel: NavModel;
  17. dataSource: DataSource;
  18. dataSourceMeta: Plugin;
  19. pageId: number;
  20. testing: DataSourceTest;
  21. deleteDataSource: typeof deleteDataSource;
  22. loadDataSource: typeof loadDataSource;
  23. setDataSourceName: typeof setDataSourceName;
  24. updateDataSource: typeof updateDataSource;
  25. clearTesting: typeof clearTesting;
  26. }
  27. interface State {
  28. dataSource: DataSource;
  29. hasClosedTest: boolean;
  30. }
  31. enum DataSourceStates {
  32. Alpha = 'alpha',
  33. Beta = 'beta',
  34. }
  35. export class DataSourceSettings extends PureComponent<Props, State> {
  36. state = {
  37. dataSource: {} as DataSource,
  38. hasClosedTest: false,
  39. };
  40. async componentDidMount() {
  41. const { loadDataSource, pageId } = this.props;
  42. await loadDataSource(pageId);
  43. }
  44. componentDidUpdate(prevProps) {
  45. const { clearTesting } = this.props;
  46. if (!this.state.hasClosedTest && prevProps.testing.status === 'success') {
  47. this.setState({ hasClosedTest: true });
  48. setTimeout(() => {
  49. clearTesting();
  50. this.setState({ hasClosedTest: false });
  51. }, 3000);
  52. }
  53. }
  54. componentWillUnmount() {
  55. this.props.clearTesting();
  56. }
  57. onSubmit = event => {
  58. event.preventDefault();
  59. this.props.updateDataSource({ ...this.state.dataSource, name: this.props.dataSource.name });
  60. };
  61. onDelete = () => {
  62. appEvents.emit('confirm-modal', {
  63. title: 'Delete',
  64. text: 'Are you sure you want to delete this data source?',
  65. yesText: 'Delete',
  66. icon: 'fa-trash',
  67. onConfirm: () => {
  68. this.confirmDelete();
  69. },
  70. });
  71. };
  72. confirmDelete = () => {
  73. this.props.deleteDataSource();
  74. };
  75. onModelChange = dataSource => {
  76. this.setState({
  77. dataSource: dataSource,
  78. });
  79. };
  80. isReadOnly() {
  81. return this.props.dataSource.readOnly === true;
  82. }
  83. shouldRenderInfoBox() {
  84. const { state } = this.props.dataSourceMeta;
  85. return state === DataSourceStates.Alpha || state === DataSourceStates.Beta;
  86. }
  87. getInfoText() {
  88. const { dataSourceMeta } = this.props;
  89. switch (dataSourceMeta.state) {
  90. case DataSourceStates.Alpha:
  91. return (
  92. 'This plugin is marked as being in alpha state, which means it is in early development phase and updates' +
  93. ' will include breaking changes.'
  94. );
  95. case DataSourceStates.Beta:
  96. return (
  97. 'This plugin is marked as being in a beta development state. This means it is in currently in active' +
  98. ' development and could be missing important features.'
  99. );
  100. }
  101. return null;
  102. }
  103. renderIsReadOnlyMessage() {
  104. return (
  105. <div className="grafana-info-box span8">
  106. This datasource was added by config and cannot be modified using the UI. Please contact your server admin to
  107. update this datasource.
  108. </div>
  109. );
  110. }
  111. render() {
  112. const { dataSource, dataSourceMeta, navModel, testing } = this.props;
  113. return (
  114. <div>
  115. <PageHeader model={navModel} />
  116. {Object.keys(dataSource).length === 0 ? (
  117. <PageLoader pageName="Data source settings" />
  118. ) : (
  119. <div className="page-container page-body">
  120. <div>
  121. <form onSubmit={this.onSubmit}>
  122. <BasicSettings
  123. dataSourceName={this.props.dataSource.name}
  124. onChange={name => this.props.setDataSourceName(name)}
  125. />
  126. {this.shouldRenderInfoBox() && <div className="grafana-info-box">{this.getInfoText()}</div>}
  127. {this.isReadOnly() && this.renderIsReadOnlyMessage()}
  128. {dataSourceMeta.module && (
  129. <PluginSettings
  130. dataSource={dataSource}
  131. dataSourceMeta={dataSourceMeta}
  132. onModelChange={this.onModelChange}
  133. />
  134. )}
  135. <div className="gf-form-group section">
  136. {testing.inProgress && (
  137. <h5>
  138. Testing.... <i className="fa fa-spiner fa-spin" />
  139. </h5>
  140. )}
  141. {!testing.inProgress &&
  142. testing.status && (
  143. <div className={`alert-${testing.status} alert`}>
  144. <div className="alert-icon">
  145. {testing.status === 'error' ? (
  146. <i className="fa fa-exclamation-triangle" />
  147. ) : (
  148. <i className="fa fa-check" />
  149. )}
  150. </div>
  151. <div className="alert-body">
  152. <div className="alert-title">{testing.message}</div>
  153. </div>
  154. </div>
  155. )}
  156. </div>
  157. <ButtonRow
  158. onSubmit={event => this.onSubmit(event)}
  159. isReadOnly={this.isReadOnly()}
  160. onDelete={this.onDelete}
  161. />
  162. </form>
  163. </div>
  164. </div>
  165. )}
  166. </div>
  167. );
  168. }
  169. }
  170. function mapStateToProps(state) {
  171. const pageId = getRouteParamsId(state.location);
  172. const dataSource = getDataSource(state.dataSources, pageId);
  173. return {
  174. navModel: getNavModel(state.navIndex, `datasource-settings-${pageId}`),
  175. dataSource: getDataSource(state.dataSources, pageId),
  176. dataSourceMeta: getDataSourceMeta(state.dataSources, dataSource.type),
  177. pageId: pageId,
  178. testing: state.dataSources.testing,
  179. };
  180. }
  181. const mapDispatchToProps = {
  182. deleteDataSource,
  183. loadDataSource,
  184. setDataSourceName,
  185. updateDataSource,
  186. clearTesting,
  187. };
  188. export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(DataSourceSettings));