ElasticDetails.tsx 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. import React from 'react';
  2. import { gte, lt, valid } from 'semver';
  3. import { DataSourceSettings, SelectableValue } from '@grafana/data';
  4. import { FieldSet, InlineField, Input, Select, InlineSwitch } from '@grafana/ui';
  5. import { ElasticsearchOptions, Interval } from '../types';
  6. import { isTruthy } from './utils';
  7. const indexPatternTypes: Array<SelectableValue<'none' | Interval>> = [
  8. { label: 'No pattern', value: 'none' },
  9. { label: 'Hourly', value: 'Hourly', example: '[logstash-]YYYY.MM.DD.HH' },
  10. { label: 'Daily', value: 'Daily', example: '[logstash-]YYYY.MM.DD' },
  11. { label: 'Weekly', value: 'Weekly', example: '[logstash-]GGGG.WW' },
  12. { label: 'Monthly', value: 'Monthly', example: '[logstash-]YYYY.MM' },
  13. { label: 'Yearly', value: 'Yearly', example: '[logstash-]YYYY' },
  14. ];
  15. const esVersions: SelectableValue[] = [
  16. { label: '7.10+', value: '7.10.0' },
  17. {
  18. label: '8.0+',
  19. value: '8.0.0',
  20. description: 'support for Elasticsearch 8 is currently experimental',
  21. },
  22. ];
  23. type Props = {
  24. value: DataSourceSettings<ElasticsearchOptions>;
  25. onChange: (value: DataSourceSettings<ElasticsearchOptions>) => void;
  26. };
  27. export const ElasticDetails = ({ value, onChange }: Props) => {
  28. const currentVersion = esVersions.find((version) => version.value === value.jsonData.esVersion);
  29. const customOption =
  30. !currentVersion && valid(value.jsonData.esVersion)
  31. ? {
  32. label: value.jsonData.esVersion,
  33. value: value.jsonData.esVersion,
  34. }
  35. : undefined;
  36. return (
  37. <>
  38. <FieldSet label="Elasticsearch details">
  39. <InlineField label="Index name" labelWidth={26}>
  40. <Input
  41. id="es_config_indexName"
  42. value={value.database || ''}
  43. onChange={changeHandler('database', value, onChange)}
  44. width={24}
  45. placeholder="es-index-name"
  46. required
  47. />
  48. </InlineField>
  49. <InlineField label="Pattern" labelWidth={26}>
  50. <Select
  51. inputId="es_config_indexPattern"
  52. value={indexPatternTypes.find(
  53. (pattern) => pattern.value === (value.jsonData.interval === undefined ? 'none' : value.jsonData.interval)
  54. )}
  55. options={indexPatternTypes}
  56. onChange={intervalHandler(value, onChange)}
  57. width={24}
  58. />
  59. </InlineField>
  60. <InlineField label="Time field name" labelWidth={26}>
  61. <Input
  62. id="es_config_timeField"
  63. value={value.jsonData.timeField || ''}
  64. onChange={jsonDataChangeHandler('timeField', value, onChange)}
  65. width={24}
  66. placeholder="@timestamp"
  67. required
  68. />
  69. </InlineField>
  70. <InlineField label="ElasticSearch version" labelWidth={26}>
  71. <Select
  72. inputId="es_config_version"
  73. options={[customOption, ...esVersions].filter(isTruthy)}
  74. onChange={(option) => {
  75. const maxConcurrentShardRequests = getMaxConcurrenShardRequestOrDefault(
  76. value.jsonData.maxConcurrentShardRequests,
  77. option.value!
  78. );
  79. onChange({
  80. ...value,
  81. jsonData: {
  82. ...value.jsonData,
  83. esVersion: option.value!,
  84. maxConcurrentShardRequests,
  85. },
  86. });
  87. }}
  88. value={currentVersion || customOption}
  89. width={24}
  90. />
  91. </InlineField>
  92. {gte(value.jsonData.esVersion, '5.6.0') && (
  93. <InlineField label="Max concurrent Shard Requests" labelWidth={26}>
  94. <Input
  95. id="es_config_shardRequests"
  96. value={value.jsonData.maxConcurrentShardRequests || ''}
  97. onChange={jsonDataChangeHandler('maxConcurrentShardRequests', value, onChange)}
  98. width={24}
  99. />
  100. </InlineField>
  101. )}
  102. <InlineField
  103. label="Min time interval"
  104. labelWidth={26}
  105. tooltip={
  106. <>
  107. A lower limit for the auto group by time interval. Recommended to be set to write frequency, for example{' '}
  108. <code>1m</code> if your data is written every minute.
  109. </>
  110. }
  111. error="Value is not valid, you can use number with time unit specifier: y, M, w, d, h, m, s"
  112. invalid={!!value.jsonData.timeInterval && !/^\d+(ms|[Mwdhmsy])$/.test(value.jsonData.timeInterval)}
  113. >
  114. <Input
  115. id="es_config_minTimeInterval"
  116. value={value.jsonData.timeInterval || ''}
  117. onChange={jsonDataChangeHandler('timeInterval', value, onChange)}
  118. width={24}
  119. placeholder="10s"
  120. />
  121. </InlineField>
  122. <InlineField label="X-Pack enabled" labelWidth={26}>
  123. <InlineSwitch
  124. id="es_config_xpackEnabled"
  125. checked={value.jsonData.xpack || false}
  126. onChange={jsonDataSwitchChangeHandler('xpack', value, onChange)}
  127. />
  128. </InlineField>
  129. {gte(value.jsonData.esVersion, '6.6.0') && value.jsonData.xpack && (
  130. <InlineField label="Include Frozen Indices" labelWidth={26}>
  131. <InlineSwitch
  132. id="es_config_frozenIndices"
  133. checked={value.jsonData.includeFrozen ?? false}
  134. onChange={jsonDataSwitchChangeHandler('includeFrozen', value, onChange)}
  135. />
  136. </InlineField>
  137. )}
  138. </FieldSet>
  139. </>
  140. );
  141. };
  142. // TODO: Use change handlers from @grafana/data
  143. const changeHandler =
  144. (key: keyof DataSourceSettings<ElasticsearchOptions>, value: Props['value'], onChange: Props['onChange']) =>
  145. (event: React.SyntheticEvent<HTMLInputElement | HTMLSelectElement>) => {
  146. onChange({
  147. ...value,
  148. [key]: event.currentTarget.value,
  149. });
  150. };
  151. // TODO: Use change handlers from @grafana/data
  152. const jsonDataChangeHandler =
  153. (key: keyof ElasticsearchOptions, value: Props['value'], onChange: Props['onChange']) =>
  154. (event: React.SyntheticEvent<HTMLInputElement | HTMLSelectElement>) => {
  155. onChange({
  156. ...value,
  157. jsonData: {
  158. ...value.jsonData,
  159. [key]: event.currentTarget.value,
  160. },
  161. });
  162. };
  163. const jsonDataSwitchChangeHandler =
  164. (key: keyof ElasticsearchOptions, value: Props['value'], onChange: Props['onChange']) =>
  165. (event: React.SyntheticEvent<HTMLInputElement>) => {
  166. onChange({
  167. ...value,
  168. jsonData: {
  169. ...value.jsonData,
  170. [key]: event.currentTarget.checked,
  171. },
  172. });
  173. };
  174. const intervalHandler =
  175. (value: Props['value'], onChange: Props['onChange']) => (option: SelectableValue<Interval | 'none'>) => {
  176. const { database } = value;
  177. // If option value is undefined it will send its label instead so we have to convert made up value to undefined here.
  178. const newInterval = option.value === 'none' ? undefined : option.value;
  179. if (!database || database.length === 0 || database.startsWith('[logstash-]')) {
  180. let newDatabase = '';
  181. if (newInterval !== undefined) {
  182. const pattern = indexPatternTypes.find((pattern) => pattern.value === newInterval);
  183. if (pattern) {
  184. newDatabase = pattern.example ?? '';
  185. }
  186. }
  187. onChange({
  188. ...value,
  189. database: newDatabase,
  190. jsonData: {
  191. ...value.jsonData,
  192. interval: newInterval,
  193. },
  194. });
  195. } else {
  196. onChange({
  197. ...value,
  198. jsonData: {
  199. ...value.jsonData,
  200. interval: newInterval,
  201. },
  202. });
  203. }
  204. };
  205. function getMaxConcurrenShardRequestOrDefault(maxConcurrentShardRequests: number | undefined, version: string): number {
  206. if (maxConcurrentShardRequests === 5 && lt(version, '7.0.0')) {
  207. return 256;
  208. }
  209. if (maxConcurrentShardRequests === 256 && gte(version, '7.0.0')) {
  210. return 5;
  211. }
  212. return maxConcurrentShardRequests || defaultMaxConcurrentShardRequests(version);
  213. }
  214. export function defaultMaxConcurrentShardRequests(version: string) {
  215. return gte(version, '7.0.0') ? 5 : 256;
  216. }