QueriesTab.tsx 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. import React, { SFC, PureComponent } from 'react';
  2. import DataSourceOption from './DataSourceOption';
  3. import { getAngularLoader, AngularComponent } from 'app/core/services/AngularLoader';
  4. import { EditorTabBody } from './EditorTabBody';
  5. import { DataSourcePicker } from './DataSourcePicker';
  6. import { PanelModel } from '../panel_model';
  7. import { DashboardModel } from '../dashboard_model';
  8. import './../../panel/metrics_tab';
  9. import config from 'app/core/config';
  10. import { QueryInspector } from './QueryInspector';
  11. import { TimeRangeOptions } from './TimeRangeOptions';
  12. // Services
  13. import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
  14. import { getBackendSrv, BackendSrv } from 'app/core/services/backend_srv';
  15. import { DataSourceSelectItem } from 'app/types';
  16. import Remarkable from 'remarkable';
  17. interface Props {
  18. panel: PanelModel;
  19. dashboard: DashboardModel;
  20. }
  21. interface Help {
  22. isLoading: boolean;
  23. helpHtml: any;
  24. }
  25. interface State {
  26. currentDatasource: DataSourceSelectItem;
  27. help: Help;
  28. hideTimeOverride: boolean;
  29. }
  30. interface LoadingPlaceholderProps {
  31. text: string;
  32. }
  33. const LoadingPlaceholder: SFC<LoadingPlaceholderProps> = ({ text }) => <h2>{text}</h2>;
  34. export class QueriesTab extends PureComponent<Props, State> {
  35. element: any;
  36. component: AngularComponent;
  37. datasources: DataSourceSelectItem[] = getDatasourceSrv().getMetricSources();
  38. backendSrv: BackendSrv = getBackendSrv();
  39. constructor(props) {
  40. super(props);
  41. const { panel } = props;
  42. this.state = {
  43. currentDatasource: this.datasources.find(datasource => datasource.value === panel.datasource),
  44. help: {
  45. isLoading: false,
  46. helpHtml: null,
  47. },
  48. hideTimeOverride: false,
  49. };
  50. }
  51. componentDidMount() {
  52. if (!this.element) {
  53. return;
  54. }
  55. const { panel, dashboard } = this.props;
  56. const loader = getAngularLoader();
  57. const template = '<metrics-tab />';
  58. const scopeProps = {
  59. ctrl: {
  60. panel: panel,
  61. dashboard: dashboard,
  62. refresh: () => panel.refresh(),
  63. },
  64. };
  65. this.component = loader.load(this.element, scopeProps, template);
  66. }
  67. componentWillUnmount() {
  68. if (this.component) {
  69. this.component.destroy();
  70. }
  71. }
  72. onChangeDataSource = datasource => {
  73. const { panel } = this.props;
  74. const { currentDatasource } = this.state;
  75. // switching to mixed
  76. if (datasource.meta.mixed) {
  77. panel.targets.forEach(target => {
  78. target.datasource = panel.datasource;
  79. if (!target.datasource) {
  80. target.datasource = config.defaultDatasource;
  81. }
  82. });
  83. } else if (currentDatasource && currentDatasource.meta.mixed) {
  84. panel.targets.forEach(target => {
  85. delete target.datasource;
  86. });
  87. }
  88. panel.datasource = datasource.value;
  89. panel.refresh();
  90. this.setState(prevState => ({
  91. ...prevState,
  92. currentDatasource: datasource,
  93. }));
  94. };
  95. loadHelp = () => {
  96. const { currentDatasource } = this.state;
  97. const hasHelp = currentDatasource.meta.hasQueryHelp;
  98. if (hasHelp) {
  99. this.setState(prevState => ({
  100. ...prevState,
  101. help: {
  102. helpHtml: <h2>Loading help...</h2>,
  103. isLoading: true,
  104. },
  105. }));
  106. this.backendSrv
  107. .get(`/api/plugins/${currentDatasource.meta.id}/markdown/query_help`)
  108. .then(res => {
  109. const md = new Remarkable();
  110. const helpHtml = md.render(res); // TODO: Clean out dangerous code? Previous: this.helpHtml = this.$sce.trustAsHtml(md.render(res));
  111. this.setState(prevState => ({
  112. ...prevState,
  113. help: {
  114. helpHtml: <div className="markdown-html" dangerouslySetInnerHTML={{ __html: helpHtml }} />,
  115. isLoading: false,
  116. },
  117. }));
  118. })
  119. .catch(() => {
  120. this.setState(prevState => ({
  121. ...prevState,
  122. help: {
  123. helpHtml: 'Error occured when loading help',
  124. isLoading: false,
  125. },
  126. }));
  127. });
  128. }
  129. };
  130. renderOptions = close => {
  131. const { currentDatasource } = this.state;
  132. const { queryOptions } = currentDatasource.meta;
  133. const { panel } = this.props;
  134. const onChangeFn = (panelKey: string) => {
  135. return (value: string | number) => {
  136. panel[panelKey] = value;
  137. panel.refresh();
  138. };
  139. };
  140. const allOptions = {
  141. cacheTimeout: {
  142. label: 'Cache timeout',
  143. placeholder: '60',
  144. name: 'cacheTimeout',
  145. value: panel.cacheTimeout,
  146. tooltipInfo: (
  147. <>
  148. If your time series store has a query cache this option can override the default cache timeout. Specify a
  149. numeric value in seconds.
  150. </>
  151. ),
  152. },
  153. maxDataPoints: {
  154. label: 'Max data points',
  155. placeholder: 'auto',
  156. name: 'maxDataPoints',
  157. value: panel.maxDataPoints,
  158. tooltipInfo: (
  159. <>
  160. The maximum data points the query should return. For graphs this is automatically set to one data point per
  161. pixel.
  162. </>
  163. ),
  164. },
  165. minInterval: {
  166. label: 'Min time interval',
  167. placeholder: '0',
  168. name: 'minInterval',
  169. value: panel.interval,
  170. panelKey: 'interval',
  171. tooltipInfo: (
  172. <>
  173. A lower limit for the auto group by time interval. Recommended to be set to write frequency, for example{' '}
  174. <code>1m</code> if your data is written every minute. Access auto interval via variable{' '}
  175. <code>$__interval</code> for time range string and <code>$__interval_ms</code> for numeric variable that can
  176. be used in math expressions.
  177. </>
  178. ),
  179. },
  180. };
  181. const dsOptions = queryOptions
  182. ? Object.keys(queryOptions).map(key => {
  183. const options = allOptions[key];
  184. return <DataSourceOption key={key} {...options} onChange={onChangeFn(allOptions[key].panelKey || key)} />;
  185. })
  186. : null;
  187. return (
  188. <>
  189. <TimeRangeOptions panel={this.props.panel} />
  190. {dsOptions}
  191. </>
  192. );
  193. };
  194. renderQueryInspector = () => {
  195. const { panel } = this.props;
  196. return <QueryInspector panel={panel} LoadingPlaceholder={LoadingPlaceholder} />;
  197. };
  198. renderHelp = () => {
  199. const { helpHtml, isLoading } = this.state.help;
  200. return isLoading ? <LoadingPlaceholder text="Loading help..." /> : helpHtml;
  201. };
  202. render() {
  203. const { currentDatasource } = this.state;
  204. const { hasQueryHelp } = currentDatasource.meta;
  205. const dsInformation = {
  206. title: currentDatasource.name,
  207. imgSrc: currentDatasource.meta.info.logos.small,
  208. render: closeOpenView => (
  209. <DataSourcePicker
  210. datasources={this.datasources}
  211. onChangeDataSource={ds => {
  212. closeOpenView();
  213. this.onChangeDataSource(ds);
  214. }}
  215. />
  216. ),
  217. };
  218. const queryInspector = {
  219. title: 'Query Inspector',
  220. render: this.renderQueryInspector,
  221. };
  222. const dsHelp = {
  223. title: '',
  224. icon: 'fa fa-question',
  225. disabled: !hasQueryHelp,
  226. onClick: this.loadHelp,
  227. render: this.renderHelp,
  228. };
  229. const options = {
  230. title: 'Time Range',
  231. icon: '',
  232. disabled: false,
  233. render: this.renderOptions,
  234. };
  235. return (
  236. <EditorTabBody heading="Queries" main={dsInformation} toolbarItems={[options, queryInspector, dsHelp]}>
  237. <>
  238. <div ref={element => (this.element = element)} style={{ width: '100%' }} />
  239. </>
  240. </EditorTabBody>
  241. );
  242. }
  243. }