QueriesTab.tsx 7.3 KB

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