ConfigEditor.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. import { uniqueId } from 'lodash';
  2. import React, { PureComponent } from 'react';
  3. import {
  4. DataSourcePluginOptionsEditorProps,
  5. SelectableValue,
  6. onUpdateDatasourceOption,
  7. updateDatasourcePluginResetOption,
  8. onUpdateDatasourceJsonDataOption,
  9. onUpdateDatasourceJsonDataOptionSelect,
  10. onUpdateDatasourceSecureJsonDataOption,
  11. updateDatasourcePluginJsonDataOption,
  12. } from '@grafana/data';
  13. import { Alert, DataSourceHttpSettings, InfoBox, InlineField, InlineFormLabel, LegacyForms, Select } from '@grafana/ui';
  14. const { Input, SecretFormField } = LegacyForms;
  15. import { InfluxOptions, InfluxSecureJsonData, InfluxVersion } from '../types';
  16. const httpModes = [
  17. { label: 'GET', value: 'GET' },
  18. { label: 'POST', value: 'POST' },
  19. ] as SelectableValue[];
  20. const versions = [
  21. {
  22. label: 'InfluxQL',
  23. value: InfluxVersion.InfluxQL,
  24. description: 'The InfluxDB SQL-like query language.',
  25. },
  26. {
  27. label: 'Flux',
  28. value: InfluxVersion.Flux,
  29. description: 'Advanced data scripting and query language. Supported in InfluxDB 2.x and 1.8+',
  30. },
  31. ] as Array<SelectableValue<InfluxVersion>>;
  32. export type Props = DataSourcePluginOptionsEditorProps<InfluxOptions>;
  33. type State = {
  34. maxSeries: string | undefined;
  35. };
  36. export class ConfigEditor extends PureComponent<Props, State> {
  37. state = {
  38. maxSeries: '',
  39. };
  40. htmlPrefix: string;
  41. constructor(props: Props) {
  42. super(props);
  43. this.state.maxSeries = props.options.jsonData.maxSeries?.toString() || '';
  44. this.htmlPrefix = uniqueId('influxdb-config');
  45. }
  46. // 1x
  47. onResetPassword = () => {
  48. updateDatasourcePluginResetOption(this.props, 'password');
  49. };
  50. // 2x
  51. onResetToken = () => {
  52. updateDatasourcePluginResetOption(this.props, 'token');
  53. };
  54. onVersionChanged = (selected: SelectableValue<InfluxVersion>) => {
  55. const { options, onOptionsChange } = this.props;
  56. const copy: any = {
  57. ...options,
  58. jsonData: {
  59. ...options.jsonData,
  60. version: selected.value,
  61. },
  62. };
  63. if (selected.value === InfluxVersion.Flux) {
  64. copy.access = 'proxy';
  65. copy.basicAuth = true;
  66. copy.jsonData.httpMode = 'POST';
  67. // Remove old 1x configs
  68. delete copy.user;
  69. delete copy.database;
  70. }
  71. onOptionsChange(copy);
  72. };
  73. renderInflux2x() {
  74. const { options } = this.props;
  75. const { secureJsonFields } = options;
  76. const secureJsonData = (options.secureJsonData || {}) as InfluxSecureJsonData;
  77. const { htmlPrefix } = this;
  78. return (
  79. <>
  80. <div className="gf-form-inline">
  81. <div className="gf-form">
  82. <InlineFormLabel htmlFor={`${htmlPrefix}-org`} className="width-10">
  83. Organization
  84. </InlineFormLabel>
  85. <div className="width-10">
  86. <Input
  87. id={`${htmlPrefix}-org`}
  88. className="width-20"
  89. value={options.jsonData.organization || ''}
  90. onChange={onUpdateDatasourceJsonDataOption(this.props, 'organization')}
  91. />
  92. </div>
  93. </div>
  94. </div>
  95. <div className="gf-form-inline">
  96. <div className="gf-form">
  97. <SecretFormField
  98. isConfigured={(secureJsonFields && secureJsonFields.token) as boolean}
  99. value={secureJsonData.token || ''}
  100. label="Token"
  101. aria-label="Token"
  102. labelWidth={10}
  103. inputWidth={20}
  104. onReset={this.onResetToken}
  105. onChange={onUpdateDatasourceSecureJsonDataOption(this.props, 'token')}
  106. />
  107. </div>
  108. </div>
  109. <div className="gf-form-inline">
  110. <div className="gf-form">
  111. <InlineFormLabel className="width-10">Default Bucket</InlineFormLabel>
  112. <div className="width-10">
  113. <Input
  114. className="width-20"
  115. placeholder="default bucket"
  116. value={options.jsonData.defaultBucket || ''}
  117. onChange={onUpdateDatasourceJsonDataOption(this.props, 'defaultBucket')}
  118. />
  119. </div>
  120. </div>
  121. </div>
  122. <div className="gf-form-inline">
  123. <div className="gf-form">
  124. <InlineFormLabel
  125. className="width-10"
  126. tooltip="A lower limit for the auto group by time interval. Recommended to be set to write frequency,
  127. for example 1m if your data is written every minute."
  128. >
  129. Min time interval
  130. </InlineFormLabel>
  131. <div className="width-10">
  132. <Input
  133. className="width-10"
  134. placeholder="10s"
  135. value={options.jsonData.timeInterval || ''}
  136. onChange={onUpdateDatasourceJsonDataOption(this.props, 'timeInterval')}
  137. />
  138. </div>
  139. </div>
  140. </div>
  141. </>
  142. );
  143. }
  144. renderInflux1x() {
  145. const { options } = this.props;
  146. const { secureJsonFields } = options;
  147. const secureJsonData = (options.secureJsonData || {}) as InfluxSecureJsonData;
  148. const { htmlPrefix } = this;
  149. return (
  150. <>
  151. <InfoBox>
  152. <h5>Database Access</h5>
  153. <p>
  154. Setting the database for this datasource does not deny access to other databases. The InfluxDB query syntax
  155. allows switching the database in the query. For example:
  156. <code>SHOW MEASUREMENTS ON _internal</code> or
  157. <code>SELECT * FROM &quot;_internal&quot;..&quot;database&quot; LIMIT 10</code>
  158. <br />
  159. <br />
  160. To support data isolation and security, make sure appropriate permissions are configured in InfluxDB.
  161. </p>
  162. </InfoBox>
  163. <div className="gf-form-inline">
  164. <div className="gf-form">
  165. <InlineFormLabel htmlFor={`${htmlPrefix}-db`} className="width-10">
  166. Database
  167. </InlineFormLabel>
  168. <div className="width-20">
  169. <Input
  170. id={`${htmlPrefix}-db`}
  171. className="width-20"
  172. value={options.database || ''}
  173. onChange={onUpdateDatasourceOption(this.props, 'database')}
  174. />
  175. </div>
  176. </div>
  177. </div>
  178. <div className="gf-form-inline">
  179. <div className="gf-form">
  180. <InlineFormLabel htmlFor={`${htmlPrefix}-user`} className="width-10">
  181. User
  182. </InlineFormLabel>
  183. <div className="width-10">
  184. <Input
  185. id={`${htmlPrefix}-user`}
  186. className="width-20"
  187. value={options.user || ''}
  188. onChange={onUpdateDatasourceOption(this.props, 'user')}
  189. />
  190. </div>
  191. </div>
  192. </div>
  193. <div className="gf-form-inline">
  194. <div className="gf-form">
  195. <SecretFormField
  196. isConfigured={(secureJsonFields && secureJsonFields.password) as boolean}
  197. value={secureJsonData.password || ''}
  198. label="Password"
  199. aria-label="Password"
  200. labelWidth={10}
  201. inputWidth={20}
  202. onReset={this.onResetPassword}
  203. onChange={onUpdateDatasourceSecureJsonDataOption(this.props, 'password')}
  204. />
  205. </div>
  206. </div>
  207. <div className="gf-form-inline">
  208. <div className="gf-form">
  209. <InlineFormLabel
  210. htmlFor={`${htmlPrefix}-http-method`}
  211. className="width-10"
  212. tooltip="You can use either GET or POST HTTP method to query your InfluxDB database. The POST
  213. method allows you to perform heavy requests (with a lots of WHERE clause) while the GET method
  214. will restrict you and return an error if the query is too large."
  215. >
  216. HTTP Method
  217. </InlineFormLabel>
  218. <Select
  219. inputId={`${htmlPrefix}-http-method`}
  220. className="width-10"
  221. value={httpModes.find((httpMode) => httpMode.value === options.jsonData.httpMode)}
  222. options={httpModes}
  223. defaultValue={options.jsonData.httpMode}
  224. onChange={onUpdateDatasourceJsonDataOptionSelect(this.props, 'httpMode')}
  225. />
  226. </div>
  227. </div>
  228. <div className="gf-form-inline">
  229. <div className="gf-form">
  230. <InlineFormLabel
  231. className="width-10"
  232. tooltip="A lower limit for the auto group by time interval. Recommended to be set to write frequency,
  233. for example 1m if your data is written every minute."
  234. >
  235. Min time interval
  236. </InlineFormLabel>
  237. <div className="width-10">
  238. <Input
  239. className="width-10"
  240. placeholder="10s"
  241. value={options.jsonData.timeInterval || ''}
  242. onChange={onUpdateDatasourceJsonDataOption(this.props, 'timeInterval')}
  243. />
  244. </div>
  245. </div>
  246. </div>
  247. </>
  248. );
  249. }
  250. render() {
  251. const { options, onOptionsChange } = this.props;
  252. return (
  253. <>
  254. <h3 className="page-heading">Query Language</h3>
  255. <div className="gf-form-group">
  256. <div className="gf-form-inline">
  257. <div className="gf-form">
  258. <Select
  259. aria-label="Query language"
  260. className="width-30"
  261. value={options.jsonData.version === InfluxVersion.Flux ? versions[1] : versions[0]}
  262. options={versions}
  263. defaultValue={versions[0]}
  264. onChange={this.onVersionChanged}
  265. />
  266. </div>
  267. </div>
  268. </div>
  269. {options.jsonData.version === InfluxVersion.Flux && (
  270. <InfoBox>
  271. <h5>Support for Flux in Grafana is currently in beta</h5>
  272. <p>
  273. Please report any issues to: <br />
  274. <a href="https://github.com/grafana/grafana/issues/new/choose">
  275. https://github.com/grafana/grafana/issues
  276. </a>
  277. </p>
  278. </InfoBox>
  279. )}
  280. {options.access === 'direct' && (
  281. <Alert title="Deprecation Notice" severity="warning">
  282. Browser access mode in the InfluxDB datasource is deprecated and will be removed in a future release.
  283. </Alert>
  284. )}
  285. <DataSourceHttpSettings
  286. showAccessOptions={true}
  287. dataSourceConfig={options}
  288. defaultUrl="http://localhost:8086"
  289. onChange={onOptionsChange}
  290. />
  291. <div className="gf-form-group">
  292. <div>
  293. <h3 className="page-heading">InfluxDB Details</h3>
  294. </div>
  295. {options.jsonData.version === InfluxVersion.Flux ? this.renderInflux2x() : this.renderInflux1x()}
  296. <div className="gf-form-inline">
  297. <InlineField
  298. labelWidth={20}
  299. label="Max series"
  300. tooltip="Limit the number of series/tables that Grafana will process. Lower this number to prevent abuse, and increase it if you have lots of small time series and not all are shown. Defaults to 1000."
  301. >
  302. <Input
  303. placeholder="1000"
  304. type="number"
  305. className="width-10"
  306. value={this.state.maxSeries}
  307. onChange={(event) => {
  308. // We duplicate this state so that we allow to write freely inside the input. We don't have
  309. // any influence over saving so this seems to be only way to do this.
  310. this.setState({ maxSeries: event.currentTarget.value });
  311. const val = parseInt(event.currentTarget.value, 10);
  312. updateDatasourcePluginJsonDataOption(this.props, 'maxSeries', Number.isFinite(val) ? val : undefined);
  313. }}
  314. />
  315. </InlineField>
  316. </div>
  317. </div>
  318. </>
  319. );
  320. }
  321. }
  322. export default ConfigEditor;