import React, { PureComponent } from 'react'; import DataSourceOption from './DataSourceOption'; import { getAngularLoader, AngularComponent } from 'app/core/services/AngularLoader'; import { EditorTabBody } from './EditorTabBody'; import { DataSourcePicker } from './DataSourcePicker'; import { JSONFormatter } from 'app/core/components/JSONFormatter/JSONFormatter'; import { PanelModel } from '../panel_model'; import { DashboardModel } from '../dashboard_model'; import './../../panel/metrics_tab'; import config from 'app/core/config'; // Services import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { getBackendSrv, BackendSrv } from 'app/core/services/backend_srv'; import { DataSourceSelectItem } from 'app/types'; import appEvents from 'app/core/app_events'; import Remarkable from 'remarkable'; interface Props { panel: PanelModel; dashboard: DashboardModel; } interface Help { isLoading: boolean; helpHtml: any; } interface State { currentDatasource: DataSourceSelectItem; help: Help; dsQuery: {}; } export class QueriesTab extends PureComponent { element: any; component: AngularComponent; datasources: DataSourceSelectItem[] = getDatasourceSrv().getMetricSources(); backendSrv: BackendSrv = getBackendSrv(); constructor(props) { super(props); const { panel } = props; this.state = { dsQuery: {}, currentDatasource: this.datasources.find(datasource => datasource.value === panel.datasource), help: { isLoading: false, helpHtml: null, }, }; appEvents.on('ds-request-response', this.onDataSourceResponse); } componentDidMount() { if (!this.element) { return; } const { panel, dashboard } = this.props; const loader = getAngularLoader(); const template = ''; const scopeProps = { ctrl: { panel: panel, dashboard: dashboard, refresh: () => panel.refresh(), }, }; this.component = loader.load(this.element, scopeProps, template); } componentWillUnmount() { appEvents.off('ds-request-response', this.onDataSourceResponse); if (this.component) { this.component.destroy(); } } onDataSourceResponse = (response: any = {}) => { // ignore if closed // if (!this.isOpen) { // return; // } // if (this.isMocking) { // this.handleMocking(data); // return; // } // this.isLoading = false; // data = _.cloneDeep(data); if (response.headers) { delete response.headers; } if (response.config) { response.request = response.config; delete response.config; delete response.request.transformRequest; delete response.request.transformResponse; delete response.request.paramSerializer; delete response.request.jsonpCallbackParam; delete response.request.headers; delete response.request.requestId; delete response.request.inspect; delete response.request.retry; delete response.request.timeout; } if (response.data) { response.response = response.data; // if (response.status === 200) { // // if we are in error state, assume we automatically opened // // and auto close it again // if (this.hasError) { // this.hasError = false; // this.isOpen = false; // } // } delete response.data; delete response.status; delete response.statusText; delete response.$$config; } this.setState(prevState => ({ ...prevState, dsQuery: response, })); }; onChangeDataSource = datasource => { const { panel } = this.props; const { currentDatasource } = this.state; // switching to mixed if (datasource.meta.mixed) { panel.targets.forEach(target => { target.datasource = panel.datasource; if (!target.datasource) { target.datasource = config.defaultDatasource; } }); } else if (currentDatasource && currentDatasource.meta.mixed) { panel.targets.forEach(target => { delete target.datasource; }); } panel.datasource = datasource.value; panel.refresh(); this.setState(prevState => ({ ...prevState, currentDatasource: datasource, })); }; loadHelp = () => { const { currentDatasource } = this.state; const hasHelp = currentDatasource.meta.hasQueryHelp; if (hasHelp) { this.setState(prevState => ({ ...prevState, help: { helpHtml:

Loading help...

, isLoading: true, }, })); this.backendSrv .get(`/api/plugins/${currentDatasource.meta.id}/markdown/query_help`) .then(res => { const md = new Remarkable(); const helpHtml = md.render(res); // TODO: Clean out dangerous code? Previous: this.helpHtml = this.$sce.trustAsHtml(md.render(res)); this.setState(prevState => ({ ...prevState, help: { helpHtml:
, isLoading: false, }, })); }) .catch(() => { this.setState(prevState => ({ ...prevState, help: { helpHtml: 'Error occured when loading help', isLoading: false, }, })); }); } }; renderOptions = close => { const { currentDatasource } = this.state; const { queryOptions } = currentDatasource.meta; const { panel } = this.props; const onChangeFn = (panelKey: string) => { return (value: string | number) => { panel[panelKey] = value; panel.refresh(); }; }; const allOptions = { cacheTimeout: { label: 'Cache timeout', placeholder: '60', name: 'cacheTimeout', value: panel.cacheTimeout, tooltipInfo: ( <> If your time series store has a query cache this option can override the default cache timeout. Specify a numeric value in seconds. ), }, maxDataPoints: { label: 'Max data points', placeholder: 'auto', name: 'maxDataPoints', value: panel.maxDataPoints, tooltipInfo: ( <> The maximum data points the query should return. For graphs this is automatically set to one data point per pixel. ), }, minInterval: { label: 'Min time interval', placeholder: '0', name: 'minInterval', value: panel.interval, panelKey: 'interval', tooltipInfo: ( <> A lower limit for the auto group by time interval. Recommended to be set to write frequency, for example{' '} 1m if your data is written every minute. Access auto interval via variable{' '} $__interval for time range string and $__interval_ms for numeric variable that can be used in math expressions. ), }, }; return Object.keys(queryOptions).map(key => { const options = allOptions[key]; return ; }); }; renderQueryInspector = () => { const { dsQuery } = this.state; return ; }; render() { const { currentDatasource } = this.state; const { helpHtml } = this.state.help; const { hasQueryHelp, queryOptions } = currentDatasource.meta; const hasQueryOptions = !!queryOptions; const dsInformation = { title: currentDatasource.name, imgSrc: currentDatasource.meta.info.logos.small, render: closeOpenView => ( { closeOpenView(); this.onChangeDataSource(ds); }} /> ), }; const queryInspector = { title: 'Query Inspector', render: this.renderQueryInspector, }; const dsHelp = { title: '', icon: 'fa fa-question', disabled: !hasQueryHelp, onClick: this.loadHelp, render: () => helpHtml, }; const options = { title: '', icon: 'fa fa-cog', disabled: !hasQueryOptions, render: this.renderOptions, }; return (
(this.element = element)} style={{ width: '100%' }} /> ); } }