Merge branch 'panel-edit-in-react' of github.com:grafana/grafana into panel-edit-in-react

This commit is contained in:
ijin08 2018-11-29 10:51:56 +01:00
commit 46ec9bd6ad
19 changed files with 212 additions and 2052 deletions

View File

@ -24,6 +24,7 @@ type DataSourcePlugin struct {
Metrics bool `json:"metrics"` Metrics bool `json:"metrics"`
Alerting bool `json:"alerting"` Alerting bool `json:"alerting"`
Explore bool `json:"explore"` Explore bool `json:"explore"`
Table bool `json:"tables"`
Logs bool `json:"logs"` Logs bool `json:"logs"`
QueryOptions map[string]bool `json:"queryOptions,omitempty"` QueryOptions map[string]bool `json:"queryOptions,omitempty"`
BuiltIn bool `json:"builtIn,omitempty"` BuiltIn bool `json:"builtIn,omitempty"`

View File

@ -86,11 +86,10 @@ export function mergeTablesIntoModel(dst?: TableModel, ...tables: TableModel[]):
if (arguments.length === 1) { if (arguments.length === 1) {
return model; return model;
} }
// Single query returns data columns and rows as is // Single query returns data columns and rows as is
if (arguments.length === 2) { if (arguments.length === 2) {
model.columns = [...tables[0].columns]; model.columns = tables[0].hasOwnProperty('columns') ? [...tables[0].columns] : [];
model.rows = [...tables[0].rows]; model.rows = tables[0].hasOwnProperty('rows') ? [...tables[0].rows] : [];
return model; return model;
} }

View File

@ -12,7 +12,7 @@ import {
QueryHintGetter, QueryHintGetter,
QueryHint, QueryHint,
} from 'app/types/explore'; } from 'app/types/explore';
import { RawTimeRange, DataQuery } from 'app/types/series'; import { TimeRange, DataQuery } from 'app/types/series';
import store from 'app/core/store'; import store from 'app/core/store';
import { import {
DEFAULT_RANGE, DEFAULT_RANGE,
@ -30,6 +30,8 @@ import IndicatorsContainer from 'app/core/components/Picker/IndicatorsContainer'
import NoOptionsMessage from 'app/core/components/Picker/NoOptionsMessage'; import NoOptionsMessage from 'app/core/components/Picker/NoOptionsMessage';
import TableModel, { mergeTablesIntoModel } from 'app/core/table_model'; import TableModel, { mergeTablesIntoModel } from 'app/core/table_model';
import { DatasourceSrv } from 'app/features/plugins/datasource_srv'; import { DatasourceSrv } from 'app/features/plugins/datasource_srv';
import { Emitter } from 'app/core/utils/emitter';
import * as dateMath from 'app/core/utils/datemath';
import Panel from './Panel'; import Panel from './Panel';
import QueryRows from './QueryRows'; import QueryRows from './QueryRows';
@ -88,6 +90,7 @@ interface ExploreProps {
*/ */
export class Explore extends React.PureComponent<ExploreProps, ExploreState> { export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
el: any; el: any;
exploreEvents: Emitter;
/** /**
* Current query expressions of the rows including their modifications, used for running queries. * Current query expressions of the rows including their modifications, used for running queries.
* Not kept in component state to prevent edit-render roundtrips. * Not kept in component state to prevent edit-render roundtrips.
@ -132,6 +135,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
}; };
} }
this.modifiedQueries = initialQueries.slice(); this.modifiedQueries = initialQueries.slice();
this.exploreEvents = new Emitter();
} }
async componentDidMount() { async componentDidMount() {
@ -155,19 +159,20 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
} else { } else {
datasource = await datasourceSrv.get(); datasource = await datasourceSrv.get();
} }
if (!datasource.meta.explore) {
datasource = await datasourceSrv.get(datasources[0].name);
}
await this.setDatasource(datasource); await this.setDatasource(datasource);
} else { } else {
this.setState({ datasourceMissing: true }); this.setState({ datasourceMissing: true });
} }
} }
componentWillUnmount() {
this.exploreEvents.removeAllListeners();
}
async setDatasource(datasource: any, origin?: DataSource) { async setDatasource(datasource: any, origin?: DataSource) {
const supportsGraph = datasource.meta.metrics; const supportsGraph = datasource.meta.metrics;
const supportsLogs = datasource.meta.logs; const supportsLogs = datasource.meta.logs;
const supportsTable = datasource.meta.metrics; const supportsTable = datasource.meta.tables;
const datasourceId = datasource.meta.id; const datasourceId = datasource.meta.id;
let datasourceError = null; let datasourceError = null;
@ -317,8 +322,14 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
} }
}; };
onChangeTime = (nextRange: RawTimeRange) => { // onChangeTime = (nextRange: RawTimeRange) => {
const range: RawTimeRange = { // const range: RawTimeRange = {
// ...nextRange,
// };
// this.setState({ range }, () => this.onSubmit());
// };
onChangeTime = (nextRange: TimeRange) => {
const range: TimeRange = {
...nextRange, ...nextRange,
}; };
this.setState({ range }, () => this.onSubmit()); this.setState({ range }, () => this.onSubmit());
@ -538,8 +549,8 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
]; ];
// Clone range for query request // Clone range for query request
const queryRange: RawTimeRange = { ...range }; // const queryRange: RawTimeRange = { ...range };
// const { from, to, raw } = this.timeSrv.timeRange();
// Datasource is using `panelId + query.refId` for cancellation logic. // Datasource is using `panelId + query.refId` for cancellation logic.
// Using `format` here because it relates to the view panel that the request is for. // Using `format` here because it relates to the view panel that the request is for.
const panelId = queryOptions.format; const panelId = queryOptions.format;
@ -549,7 +560,12 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
intervalMs, intervalMs,
panelId, panelId,
targets: configuredQueries, // Datasources rely on DataQueries being passed under the targets key. targets: configuredQueries, // Datasources rely on DataQueries being passed under the targets key.
range: queryRange, range: {
from: dateMath.parse(range.from, false),
to: dateMath.parse(range.to, true),
raw: range,
},
rangeRaw: range,
}; };
} }
@ -696,17 +712,19 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
} }
const { datasource } = this.state; const { datasource } = this.state;
const datasourceId = datasource.meta.id; const datasourceId = datasource.meta.id;
// Run all queries concurrently // Run all queries concurrentlyso
queries.forEach(async (query, rowIndex) => { queries.forEach(async (query, rowIndex) => {
const transaction = this.startQueryTransaction(query, rowIndex, resultType, queryOptions); const transaction = this.startQueryTransaction(query, rowIndex, resultType, queryOptions);
try { try {
const now = Date.now(); const now = Date.now();
const res = await datasource.query(transaction.options); const res = await datasource.query(transaction.options);
this.exploreEvents.emit('data-received', res);
const latency = Date.now() - now; const latency = Date.now() - now;
const results = resultGetter ? resultGetter(res.data) : res.data; const results = resultGetter ? resultGetter(res.data) : res.data;
this.completeQueryTransaction(transaction.id, results, latency, queries, datasourceId); this.completeQueryTransaction(transaction.id, results, latency, queries, datasourceId);
this.setState({ graphRange: transaction.options.range }); this.setState({ graphRange: transaction.options.range });
} catch (response) { } catch (response) {
this.exploreEvents.emit('data-error', response);
this.failQueryTransaction(transaction.id, response, datasourceId); this.failQueryTransaction(transaction.id, response, datasourceId);
} }
}); });
@ -759,7 +777,10 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
const graphResult = _.flatten( const graphResult = _.flatten(
queryTransactions.filter(qt => qt.resultType === 'Graph' && qt.done && qt.result).map(qt => qt.result) queryTransactions.filter(qt => qt.resultType === 'Graph' && qt.done && qt.result).map(qt => qt.result)
); );
const tableResult = mergeTablesIntoModel(
//Temp solution... How do detect if ds supports table format?
let tableResult;
tableResult = mergeTablesIntoModel(
new TableModel(), new TableModel(),
...queryTransactions.filter(qt => qt.resultType === 'Table' && qt.done && qt.result).map(qt => qt.result) ...queryTransactions.filter(qt => qt.resultType === 'Table' && qt.done && qt.result).map(qt => qt.result)
); );
@ -858,6 +879,8 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
onExecuteQuery={this.onSubmit} onExecuteQuery={this.onSubmit}
onRemoveQueryRow={this.onRemoveQueryRow} onRemoveQueryRow={this.onRemoveQueryRow}
transactions={queryTransactions} transactions={queryTransactions}
exploreEvents={this.exploreEvents}
range={range}
/> />
<main className="m-t-2"> <main className="m-t-2">
<ErrorBoundary> <ErrorBoundary>

View File

@ -0,0 +1,77 @@
import React, { PureComponent } from 'react';
import { getAngularLoader, AngularComponent } from 'app/core/services/AngularLoader';
import { Emitter } from 'app/core/utils/emitter';
import { getIntervals } from 'app/core/utils/explore';
import { DataQuery } from 'app/types';
import { RawTimeRange } from 'app/types/series';
import { getTimeSrv } from 'app/features/dashboard/time_srv';
import 'app/features/plugins/plugin_loader';
interface QueryEditorProps {
datasource: any;
error?: string | JSX.Element;
onExecuteQuery?: () => void;
onQueryChange?: (value: DataQuery, override?: boolean) => void;
initialQuery: DataQuery;
exploreEvents: Emitter;
range: RawTimeRange;
}
export default class QueryEditor extends PureComponent<QueryEditorProps, any> {
element: any;
component: AngularComponent;
async componentDidMount() {
if (!this.element) {
return;
}
const { datasource, initialQuery, exploreEvents, range } = this.props;
this.initTimeSrv(range);
const loader = getAngularLoader();
const template = '<plugin-component type="query-ctrl"> </plugin-component>';
const target = { datasource: datasource.name, ...initialQuery };
const scopeProps = {
target,
ctrl: {
refresh: () => {
this.props.onQueryChange({ refId: initialQuery.refId, ...target }, false);
this.props.onExecuteQuery();
},
events: exploreEvents,
panel: {
datasource,
targets: [target],
},
dashboard: {
getNextQueryLetter: x => '',
},
hideEditorRowActions: true,
...getIntervals(range, datasource, null), // Possible to get resolution?
},
};
this.component = loader.load(this.element, scopeProps, template);
}
componentWillUnmount() {
if (this.component) {
this.component.destroy();
}
}
initTimeSrv(range) {
const timeSrv = getTimeSrv();
timeSrv.init({
time: range,
refresh: false,
getTimezone: () => 'utc',
timeRangeUpdated: () => console.log('refreshDashboard!'),
});
}
render() {
return <div ref={element => (this.element = element)} style={{ width: '100%' }} />;
}
}

View File

@ -1,10 +1,13 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { QueryTransaction, HistoryItem, QueryHint } from 'app/types/explore'; import { QueryTransaction, HistoryItem, QueryHint } from 'app/types/explore';
import { Emitter } from 'app/core/utils/emitter';
import DefaultQueryField from './QueryField'; // import DefaultQueryField from './QueryField';
import QueryEditor from './QueryEditor';
import QueryTransactionStatus from './QueryTransactionStatus'; import QueryTransactionStatus from './QueryTransactionStatus';
import { DataSource, DataQuery } from 'app/types'; import { DataSource, DataQuery } from 'app/types';
import { RawTimeRange } from 'app/types/series';
function getFirstHintFromTransactions(transactions: QueryTransaction[]): QueryHint { function getFirstHintFromTransactions(transactions: QueryTransaction[]): QueryHint {
const transaction = transactions.find(qt => qt.hints && qt.hints.length > 0); const transaction = transactions.find(qt => qt.hints && qt.hints.length > 0);
@ -27,6 +30,8 @@ interface QueryRowCommonProps {
datasource: DataSource; datasource: DataSource;
history: HistoryItem[]; history: HistoryItem[];
transactions: QueryTransaction[]; transactions: QueryTransaction[];
exploreEvents: Emitter;
range: RawTimeRange;
} }
type QueryRowProps = QueryRowCommonProps & type QueryRowProps = QueryRowCommonProps &
@ -36,6 +41,11 @@ type QueryRowProps = QueryRowCommonProps &
}; };
class QueryRow extends PureComponent<QueryRowProps> { class QueryRow extends PureComponent<QueryRowProps> {
onExecuteQuery = () => {
const { onExecuteQuery } = this.props;
onExecuteQuery();
};
onChangeQuery = (value: DataQuery, override?: boolean) => { onChangeQuery = (value: DataQuery, override?: boolean) => {
const { index, onChangeQuery } = this.props; const { index, onChangeQuery } = this.props;
if (onChangeQuery) { if (onChangeQuery) {
@ -76,27 +86,41 @@ class QueryRow extends PureComponent<QueryRowProps> {
}; };
render() { render() {
const { datasource, history, initialQuery, transactions } = this.props; const { datasource, history, initialQuery, transactions, exploreEvents, range } = this.props;
const transactionWithError = transactions.find(t => t.error !== undefined); const transactionWithError = transactions.find(t => t.error !== undefined);
const hint = getFirstHintFromTransactions(transactions); const hint = getFirstHintFromTransactions(transactions);
const queryError = transactionWithError ? transactionWithError.error : null; const queryError = transactionWithError ? transactionWithError.error : null;
const QueryField = datasource.pluginExports.ExploreQueryField || DefaultQueryField; // const QueryField = datasource.pluginExports.ExploreQueryField || DefaultQueryField;
const QueryField = datasource.pluginExports.ExploreQueryField;
// const QueryEditor = datasource.pluginExports.QueryCtrl;
return ( return (
<div className="query-row"> <div className="query-row">
<div className="query-row-status"> <div className="query-row-status">
<QueryTransactionStatus transactions={transactions} /> <QueryTransactionStatus transactions={transactions} />
</div> </div>
<div className="query-row-field"> <div className="query-row-field">
<QueryField {QueryField ? (
datasource={datasource} <QueryField
error={queryError} datasource={datasource}
hint={hint} error={queryError}
initialQuery={initialQuery} hint={hint}
history={history} initialQuery={initialQuery}
onClickHintFix={this.onClickHintFix} history={history}
onPressEnter={this.onPressEnter} onClickHintFix={this.onClickHintFix}
onQueryChange={this.onChangeQuery} onPressEnter={this.onPressEnter}
/> onQueryChange={this.onChangeQuery}
/>
) : (
<QueryEditor
datasource={datasource}
error={queryError}
onQueryChange={this.onChangeQuery}
onExecuteQuery={this.onExecuteQuery}
initialQuery={initialQuery}
exploreEvents={exploreEvents}
range={range}
/>
)}
</div> </div>
<div className="query-row-tools"> <div className="query-row-tools">
<button className="btn navbar-button navbar-button--tight" onClick={this.onClickClearButton}> <button className="btn navbar-button navbar-button--tight" onClick={this.onClickClearButton}>

View File

@ -3,7 +3,7 @@ import moment from 'moment';
import * as dateMath from 'app/core/utils/datemath'; import * as dateMath from 'app/core/utils/datemath';
import * as rangeUtil from 'app/core/utils/rangeutil'; import * as rangeUtil from 'app/core/utils/rangeutil';
import { RawTimeRange } from 'app/types/series'; import { RawTimeRange, TimeRange } from 'app/types/series';
const DATE_FORMAT = 'YYYY-MM-DD HH:mm:ss'; const DATE_FORMAT = 'YYYY-MM-DD HH:mm:ss';
export const DEFAULT_RANGE = { export const DEFAULT_RANGE = {
@ -120,6 +120,12 @@ export default class TimePicker extends PureComponent<TimePickerProps, TimePicke
to: moment(nextTo), to: moment(nextTo),
}; };
const nextTimeRange: TimeRange = {
raw: nextRange,
from,
to,
};
this.setState( this.setState(
{ {
rangeString: rangeUtil.describeTimeRange(nextRange), rangeString: rangeUtil.describeTimeRange(nextRange),
@ -127,7 +133,7 @@ export default class TimePicker extends PureComponent<TimePickerProps, TimePicke
toRaw: nextRange.to.format(DATE_FORMAT), toRaw: nextRange.to.format(DATE_FORMAT),
}, },
() => { () => {
onChangeTime(nextRange); onChangeTime(nextTimeRange);
} }
); );
} }

View File

@ -1,59 +1,44 @@
<div class="gf-form-query"> <div class="gf-form-query">
<div class="gf-form gf-form-query-letter-cell"> <div ng-if="!ctrl.hideEditorRowActions" class="gf-form gf-form-query-letter-cell">
<label class="gf-form-label"> <label class="gf-form-label">
<a class="pointer" tabindex="1" ng-click="ctrl.toggleCollapse()"> <a class="pointer" tabindex="1" ng-click="ctrl.toggleCollapse()">
<span ng-class="{muted: !ctrl.canCollapse}" class="gf-form-query-letter-cell-carret"> <span ng-class="{muted: !ctrl.canCollapse}" class="gf-form-query-letter-cell-carret">
<i class="fa fa-caret-down" ng-hide="ctrl.collapsed"></i> <i class="fa fa-caret-down" ng-hide="ctrl.collapsed"></i>
<i class="fa fa-caret-right" ng-show="ctrl.collapsed"></i> <i class="fa fa-caret-right" ng-show="ctrl.collapsed"></i>
</span> </span>
<span class="gf-form-query-letter-cell-letter">{{ctrl.target.refId}}</span> <span class="gf-form-query-letter-cell-letter">{{ ctrl.target.refId }}</span>
<em class="gf-form-query-letter-cell-ds" ng-show="ctrl.target.datasource">({{ctrl.target.datasource}})</em> <em class="gf-form-query-letter-cell-ds" ng-show="ctrl.target.datasource">({{ ctrl.target.datasource }})</em>
</a> </a>
</label> </label>
</div> </div>
<div class="gf-form-query-content gf-form-query-content--collapsed" ng-if="ctrl.collapsed"> <div class="gf-form-query-content gf-form-query-content--collapsed" ng-if="ctrl.collapsed">
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label pointer gf-form-label--grow" ng-click="ctrl.toggleCollapse()"> <label class="gf-form-label pointer gf-form-label--grow" ng-click="ctrl.toggleCollapse()">
{{ctrl.collapsedText}} {{ ctrl.collapsedText }}
</label> </label>
</div> </div>
</div> </div>
<div ng-transclude class="gf-form-query-content" ng-if="!ctrl.collapsed"> <div ng-transclude class="gf-form-query-content" ng-if="!ctrl.collapsed"></div>
</div>
<div class="gf-form"> <div ng-if="!ctrl.hideEditorRowActions" class="gf-form">
<label class="gf-form-label dropdown"> <label class="gf-form-label dropdown">
<a class="pointer dropdown-toggle" data-toggle="dropdown" tabindex="1"> <a class="pointer dropdown-toggle" data-toggle="dropdown" tabindex="1"> <i class="fa fa-bars"></i> </a>
<i class="fa fa-bars"></i> <ul class="dropdown-menu pull-right" role="menu">
</a> <li role="menuitem" ng-if="ctrl.hasTextEditMode">
<ul class="dropdown-menu pull-right" role="menu"> <a tabindex="1" ng-click="ctrl.toggleEditorMode()">Toggle Edit Mode</a>
<li role="menuitem" ng-if="ctrl.hasTextEditMode"> </li>
<a tabindex="1" ng-click="ctrl.toggleEditorMode()">Toggle Edit Mode</a> <li role="menuitem"><a tabindex="1" ng-click="ctrl.duplicateQuery()">Duplicate</a></li>
</li> <li role="menuitem"><a tabindex="1" ng-click="ctrl.moveQuery(-1)">Move up</a></li>
<li role="menuitem"> <li role="menuitem"><a tabindex="1" ng-click="ctrl.moveQuery(1)">Move down</a></li>
<a tabindex="1" ng-click="ctrl.duplicateQuery()">Duplicate</a> </ul>
</li> </label>
<li role="menuitem">
<a tabindex="1" ng-click="ctrl.moveQuery(-1)">Move up</a>
</li>
<li role="menuitem">
<a tabindex="1" ng-click="ctrl.moveQuery(1)">Move down</a>
</li>
</ul>
</label>
<label class="gf-form-label"> <label class="gf-form-label">
<a ng-click="ctrl.toggleHideQuery()" role="menuitem"> <a ng-click="ctrl.toggleHideQuery()" role="menuitem"> <i class="fa fa-eye"></i> </a>
<i class="fa fa-eye"></i> </label>
</a> <label class="gf-form-label">
</label> <a class="pointer" tabindex="1" ng-click="ctrl.removeQuery(ctrl.target)"> <i class="fa fa-trash"></i> </a>
<label class="gf-form-label"> </label>
<a class="pointer" tabindex="1" ng-click="ctrl.removeQuery(ctrl.target)"> </div>
<i class="fa fa-trash"></i>
</a>
</label>
</div>
</div> </div>

View File

@ -11,11 +11,13 @@ export class QueryRowCtrl {
panelCtrl: any; panelCtrl: any;
panel: any; panel: any;
collapsed: any; collapsed: any;
hideEditorRowActions: boolean;
constructor() { constructor() {
this.panelCtrl = this.queryCtrl.panelCtrl; this.panelCtrl = this.queryCtrl.panelCtrl;
this.target = this.queryCtrl.target; this.target = this.queryCtrl.target;
this.panel = this.panelCtrl.panel; this.panel = this.panelCtrl.panel;
this.hideEditorRowActions = this.panelCtrl.hideEditorRowActions;
if (!this.target.refId) { if (!this.target.refId) {
this.target.refId = this.panelCtrl.dashboard.getNextQueryLetter(this.panel); this.target.refId = this.panelCtrl.dashboard.getNextQueryLetter(this.panel);

View File

@ -93,9 +93,7 @@ export class DatasourceSrv {
getExploreSources() { getExploreSources() {
const { datasources } = config; const { datasources } = config;
const es = Object.keys(datasources) const es = Object.keys(datasources).map(name => datasources[name]);
.map(name => datasources[name])
.filter(ds => ds.meta && ds.meta.explore);
return _.sortBy(es, ['name']); return _.sortBy(es, ['name']);
} }

View File

@ -3,13 +3,12 @@
"type": "datasource", "type": "datasource",
"id": "graphite", "id": "graphite",
"includes": [ "includes": [{ "type": "dashboard", "name": "Graphite Carbon Metrics", "path": "dashboards/carbon_metrics.json" }],
{"type": "dashboard", "name": "Graphite Carbon Metrics", "path": "dashboards/carbon_metrics.json"}
],
"metrics": true, "metrics": true,
"alerting": true, "alerting": true,
"annotations": true, "annotations": true,
"tables": false,
"queryOptions": { "queryOptions": {
"maxDataPoints": true, "maxDataPoints": true,
@ -27,8 +26,11 @@
"large": "img/graphite_logo.png" "large": "img/graphite_logo.png"
}, },
"links": [ "links": [
{"name": "Graphite", "url": "https://graphiteapp.org/"}, { "name": "Graphite", "url": "https://graphiteapp.org/" },
{"name": "Graphite 1.1 Release", "url": "https://grafana.com/blog/2018/01/11/graphite-1.1-teaching-an-old-dog-new-tricks/"} {
"name": "Graphite 1.1 Release",
"url": "https://grafana.com/blog/2018/01/11/graphite-1.1-teaching-an-old-dog-new-tricks/"
}
], ],
"version": "5.0.0" "version": "5.0.0"
} }

View File

@ -7,6 +7,7 @@
"annotations": false, "annotations": false,
"logs": true, "logs": true,
"explore": true, "explore": true,
"tables": true,
"info": { "info": {
"description": "Grafana Logging Data Source for Grafana", "description": "Grafana Logging Data Source for Grafana",
"author": { "author": {
@ -25,4 +26,4 @@
], ],
"version": "5.3.0" "version": "5.3.0"
} }
} }

View File

@ -18,9 +18,9 @@
"alerting": true, "alerting": true,
"annotations": true, "annotations": true,
"metrics": true, "metrics": true,
"tables": true,
"queryOptions": { "queryOptions": {
"minInterval": true "minInterval": true
} }
} }

View File

@ -19,9 +19,9 @@
"alerting": true, "alerting": true,
"annotations": true, "annotations": true,
"metrics": true, "metrics": true,
"tables": true,
"queryOptions": { "queryOptions": {
"minInterval": true "minInterval": true
} }
} }

View File

@ -7,6 +7,7 @@
"defaultMatchFormat": "pipe", "defaultMatchFormat": "pipe",
"annotations": true, "annotations": true,
"alerting": true, "alerting": true,
"tables": false,
"info": { "info": {
"description": "OpenTSDB Data Source for Grafana", "description": "OpenTSDB Data Source for Grafana",

View File

@ -19,9 +19,9 @@
"alerting": true, "alerting": true,
"annotations": true, "annotations": true,
"metrics": true, "metrics": true,
"tables": true,
"queryOptions": { "queryOptions": {
"minInterval": true "minInterval": true
} }
} }

View File

@ -23,6 +23,7 @@
"alerting": true, "alerting": true,
"annotations": true, "annotations": true,
"explore": true, "explore": true,
"tables": true,
"queryOptions": { "queryOptions": {
"minInterval": true "minInterval": true
}, },
@ -44,4 +45,4 @@
], ],
"version": "5.0.0" "version": "5.0.0"
} }
} }

View File

@ -5,6 +5,7 @@
"metrics": true, "metrics": true,
"alerting": true, "alerting": true,
"annotations": true, "annotations": true,
"tables": false,
"state": "beta", "state": "beta",
"queryOptions": { "queryOptions": {
"maxDataPoints": true, "maxDataPoints": true,

View File

@ -41,6 +41,7 @@ export interface PluginMeta {
// Datasource-specific // Datasource-specific
metrics?: boolean; metrics?: boolean;
tables?: boolean;
logs?: boolean; logs?: boolean;
explore?: boolean; explore?: boolean;
annotations?: boolean; annotations?: boolean;

1964
yarn.lock

File diff suppressed because it is too large Load Diff