mirror of
https://github.com/grafana/grafana.git
synced 2025-02-13 00:55:47 -06:00
Merge branch 'panel-edit-in-react' of github.com:grafana/grafana into panel-edit-in-react
This commit is contained in:
commit
46ec9bd6ad
@ -24,6 +24,7 @@ type DataSourcePlugin struct {
|
||||
Metrics bool `json:"metrics"`
|
||||
Alerting bool `json:"alerting"`
|
||||
Explore bool `json:"explore"`
|
||||
Table bool `json:"tables"`
|
||||
Logs bool `json:"logs"`
|
||||
QueryOptions map[string]bool `json:"queryOptions,omitempty"`
|
||||
BuiltIn bool `json:"builtIn,omitempty"`
|
||||
|
@ -86,11 +86,10 @@ export function mergeTablesIntoModel(dst?: TableModel, ...tables: TableModel[]):
|
||||
if (arguments.length === 1) {
|
||||
return model;
|
||||
}
|
||||
|
||||
// Single query returns data columns and rows as is
|
||||
if (arguments.length === 2) {
|
||||
model.columns = [...tables[0].columns];
|
||||
model.rows = [...tables[0].rows];
|
||||
model.columns = tables[0].hasOwnProperty('columns') ? [...tables[0].columns] : [];
|
||||
model.rows = tables[0].hasOwnProperty('rows') ? [...tables[0].rows] : [];
|
||||
return model;
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ import {
|
||||
QueryHintGetter,
|
||||
QueryHint,
|
||||
} 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 {
|
||||
DEFAULT_RANGE,
|
||||
@ -30,6 +30,8 @@ import IndicatorsContainer from 'app/core/components/Picker/IndicatorsContainer'
|
||||
import NoOptionsMessage from 'app/core/components/Picker/NoOptionsMessage';
|
||||
import TableModel, { mergeTablesIntoModel } from 'app/core/table_model';
|
||||
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 QueryRows from './QueryRows';
|
||||
@ -88,6 +90,7 @@ interface ExploreProps {
|
||||
*/
|
||||
export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
||||
el: any;
|
||||
exploreEvents: Emitter;
|
||||
/**
|
||||
* Current query expressions of the rows including their modifications, used for running queries.
|
||||
* 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.exploreEvents = new Emitter();
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
@ -155,19 +159,20 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
||||
} else {
|
||||
datasource = await datasourceSrv.get();
|
||||
}
|
||||
if (!datasource.meta.explore) {
|
||||
datasource = await datasourceSrv.get(datasources[0].name);
|
||||
}
|
||||
await this.setDatasource(datasource);
|
||||
} else {
|
||||
this.setState({ datasourceMissing: true });
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.exploreEvents.removeAllListeners();
|
||||
}
|
||||
|
||||
async setDatasource(datasource: any, origin?: DataSource) {
|
||||
const supportsGraph = datasource.meta.metrics;
|
||||
const supportsLogs = datasource.meta.logs;
|
||||
const supportsTable = datasource.meta.metrics;
|
||||
const supportsTable = datasource.meta.tables;
|
||||
const datasourceId = datasource.meta.id;
|
||||
let datasourceError = null;
|
||||
|
||||
@ -317,8 +322,14 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
||||
}
|
||||
};
|
||||
|
||||
onChangeTime = (nextRange: RawTimeRange) => {
|
||||
const range: RawTimeRange = {
|
||||
// onChangeTime = (nextRange: RawTimeRange) => {
|
||||
// const range: RawTimeRange = {
|
||||
// ...nextRange,
|
||||
// };
|
||||
// this.setState({ range }, () => this.onSubmit());
|
||||
// };
|
||||
onChangeTime = (nextRange: TimeRange) => {
|
||||
const range: TimeRange = {
|
||||
...nextRange,
|
||||
};
|
||||
this.setState({ range }, () => this.onSubmit());
|
||||
@ -538,8 +549,8 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
||||
];
|
||||
|
||||
// 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.
|
||||
// Using `format` here because it relates to the view panel that the request is for.
|
||||
const panelId = queryOptions.format;
|
||||
@ -549,7 +560,12 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
||||
intervalMs,
|
||||
panelId,
|
||||
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 datasourceId = datasource.meta.id;
|
||||
// Run all queries concurrently
|
||||
// Run all queries concurrentlyso
|
||||
queries.forEach(async (query, rowIndex) => {
|
||||
const transaction = this.startQueryTransaction(query, rowIndex, resultType, queryOptions);
|
||||
try {
|
||||
const now = Date.now();
|
||||
const res = await datasource.query(transaction.options);
|
||||
this.exploreEvents.emit('data-received', res);
|
||||
const latency = Date.now() - now;
|
||||
const results = resultGetter ? resultGetter(res.data) : res.data;
|
||||
this.completeQueryTransaction(transaction.id, results, latency, queries, datasourceId);
|
||||
this.setState({ graphRange: transaction.options.range });
|
||||
} catch (response) {
|
||||
this.exploreEvents.emit('data-error', response);
|
||||
this.failQueryTransaction(transaction.id, response, datasourceId);
|
||||
}
|
||||
});
|
||||
@ -759,7 +777,10 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
|
||||
const graphResult = _.flatten(
|
||||
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(),
|
||||
...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}
|
||||
onRemoveQueryRow={this.onRemoveQueryRow}
|
||||
transactions={queryTransactions}
|
||||
exploreEvents={this.exploreEvents}
|
||||
range={range}
|
||||
/>
|
||||
<main className="m-t-2">
|
||||
<ErrorBoundary>
|
||||
|
77
public/app/features/explore/QueryEditor.tsx
Normal file
77
public/app/features/explore/QueryEditor.tsx
Normal 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%' }} />;
|
||||
}
|
||||
}
|
@ -1,10 +1,13 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
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 { DataSource, DataQuery } from 'app/types';
|
||||
import { RawTimeRange } from 'app/types/series';
|
||||
|
||||
function getFirstHintFromTransactions(transactions: QueryTransaction[]): QueryHint {
|
||||
const transaction = transactions.find(qt => qt.hints && qt.hints.length > 0);
|
||||
@ -27,6 +30,8 @@ interface QueryRowCommonProps {
|
||||
datasource: DataSource;
|
||||
history: HistoryItem[];
|
||||
transactions: QueryTransaction[];
|
||||
exploreEvents: Emitter;
|
||||
range: RawTimeRange;
|
||||
}
|
||||
|
||||
type QueryRowProps = QueryRowCommonProps &
|
||||
@ -36,6 +41,11 @@ type QueryRowProps = QueryRowCommonProps &
|
||||
};
|
||||
|
||||
class QueryRow extends PureComponent<QueryRowProps> {
|
||||
onExecuteQuery = () => {
|
||||
const { onExecuteQuery } = this.props;
|
||||
onExecuteQuery();
|
||||
};
|
||||
|
||||
onChangeQuery = (value: DataQuery, override?: boolean) => {
|
||||
const { index, onChangeQuery } = this.props;
|
||||
if (onChangeQuery) {
|
||||
@ -76,27 +86,41 @@ class QueryRow extends PureComponent<QueryRowProps> {
|
||||
};
|
||||
|
||||
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 hint = getFirstHintFromTransactions(transactions);
|
||||
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 (
|
||||
<div className="query-row">
|
||||
<div className="query-row-status">
|
||||
<QueryTransactionStatus transactions={transactions} />
|
||||
</div>
|
||||
<div className="query-row-field">
|
||||
<QueryField
|
||||
datasource={datasource}
|
||||
error={queryError}
|
||||
hint={hint}
|
||||
initialQuery={initialQuery}
|
||||
history={history}
|
||||
onClickHintFix={this.onClickHintFix}
|
||||
onPressEnter={this.onPressEnter}
|
||||
onQueryChange={this.onChangeQuery}
|
||||
/>
|
||||
{QueryField ? (
|
||||
<QueryField
|
||||
datasource={datasource}
|
||||
error={queryError}
|
||||
hint={hint}
|
||||
initialQuery={initialQuery}
|
||||
history={history}
|
||||
onClickHintFix={this.onClickHintFix}
|
||||
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 className="query-row-tools">
|
||||
<button className="btn navbar-button navbar-button--tight" onClick={this.onClickClearButton}>
|
||||
|
@ -3,7 +3,7 @@ import moment from 'moment';
|
||||
|
||||
import * as dateMath from 'app/core/utils/datemath';
|
||||
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';
|
||||
export const DEFAULT_RANGE = {
|
||||
@ -120,6 +120,12 @@ export default class TimePicker extends PureComponent<TimePickerProps, TimePicke
|
||||
to: moment(nextTo),
|
||||
};
|
||||
|
||||
const nextTimeRange: TimeRange = {
|
||||
raw: nextRange,
|
||||
from,
|
||||
to,
|
||||
};
|
||||
|
||||
this.setState(
|
||||
{
|
||||
rangeString: rangeUtil.describeTimeRange(nextRange),
|
||||
@ -127,7 +133,7 @@ export default class TimePicker extends PureComponent<TimePickerProps, TimePicke
|
||||
toRaw: nextRange.to.format(DATE_FORMAT),
|
||||
},
|
||||
() => {
|
||||
onChangeTime(nextRange);
|
||||
onChangeTime(nextTimeRange);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -1,59 +1,44 @@
|
||||
|
||||
<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">
|
||||
<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-right" ng-show="ctrl.collapsed"></i>
|
||||
</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>
|
||||
<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>
|
||||
</a>
|
||||
</label>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-query-content gf-form-query-content--collapsed" ng-if="ctrl.collapsed">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label pointer gf-form-label--grow" ng-click="ctrl.toggleCollapse()">
|
||||
{{ctrl.collapsedText}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gf-form-query-content gf-form-query-content--collapsed" ng-if="ctrl.collapsed">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label pointer gf-form-label--grow" ng-click="ctrl.toggleCollapse()">
|
||||
{{ ctrl.collapsedText }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-transclude class="gf-form-query-content" ng-if="!ctrl.collapsed">
|
||||
</div>
|
||||
<div ng-transclude class="gf-form-query-content" ng-if="!ctrl.collapsed"></div>
|
||||
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label dropdown">
|
||||
<a class="pointer dropdown-toggle" data-toggle="dropdown" tabindex="1">
|
||||
<i class="fa fa-bars"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu pull-right" role="menu">
|
||||
<li role="menuitem" ng-if="ctrl.hasTextEditMode">
|
||||
<a tabindex="1" ng-click="ctrl.toggleEditorMode()">Toggle Edit Mode</a>
|
||||
</li>
|
||||
<li role="menuitem">
|
||||
<a tabindex="1" ng-click="ctrl.duplicateQuery()">Duplicate</a>
|
||||
</li>
|
||||
<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>
|
||||
<div ng-if="!ctrl.hideEditorRowActions" class="gf-form">
|
||||
<label class="gf-form-label dropdown">
|
||||
<a class="pointer dropdown-toggle" data-toggle="dropdown" tabindex="1"> <i class="fa fa-bars"></i> </a>
|
||||
<ul class="dropdown-menu pull-right" role="menu">
|
||||
<li role="menuitem" ng-if="ctrl.hasTextEditMode">
|
||||
<a tabindex="1" ng-click="ctrl.toggleEditorMode()">Toggle Edit Mode</a>
|
||||
</li>
|
||||
<li role="menuitem"><a tabindex="1" ng-click="ctrl.duplicateQuery()">Duplicate</a></li>
|
||||
<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">
|
||||
<a ng-click="ctrl.toggleHideQuery()" role="menuitem">
|
||||
<i class="fa fa-eye"></i>
|
||||
</a>
|
||||
</label>
|
||||
<label class="gf-form-label">
|
||||
<a class="pointer" tabindex="1" ng-click="ctrl.removeQuery(ctrl.target)">
|
||||
<i class="fa fa-trash"></i>
|
||||
</a>
|
||||
</label>
|
||||
</div>
|
||||
<a ng-click="ctrl.toggleHideQuery()" role="menuitem"> <i class="fa fa-eye"></i> </a>
|
||||
</label>
|
||||
<label class="gf-form-label">
|
||||
<a class="pointer" tabindex="1" ng-click="ctrl.removeQuery(ctrl.target)"> <i class="fa fa-trash"></i> </a>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -11,11 +11,13 @@ export class QueryRowCtrl {
|
||||
panelCtrl: any;
|
||||
panel: any;
|
||||
collapsed: any;
|
||||
hideEditorRowActions: boolean;
|
||||
|
||||
constructor() {
|
||||
this.panelCtrl = this.queryCtrl.panelCtrl;
|
||||
this.target = this.queryCtrl.target;
|
||||
this.panel = this.panelCtrl.panel;
|
||||
this.hideEditorRowActions = this.panelCtrl.hideEditorRowActions;
|
||||
|
||||
if (!this.target.refId) {
|
||||
this.target.refId = this.panelCtrl.dashboard.getNextQueryLetter(this.panel);
|
||||
|
@ -93,9 +93,7 @@ export class DatasourceSrv {
|
||||
|
||||
getExploreSources() {
|
||||
const { datasources } = config;
|
||||
const es = Object.keys(datasources)
|
||||
.map(name => datasources[name])
|
||||
.filter(ds => ds.meta && ds.meta.explore);
|
||||
const es = Object.keys(datasources).map(name => datasources[name]);
|
||||
return _.sortBy(es, ['name']);
|
||||
}
|
||||
|
||||
|
@ -3,13 +3,12 @@
|
||||
"type": "datasource",
|
||||
"id": "graphite",
|
||||
|
||||
"includes": [
|
||||
{"type": "dashboard", "name": "Graphite Carbon Metrics", "path": "dashboards/carbon_metrics.json"}
|
||||
],
|
||||
"includes": [{ "type": "dashboard", "name": "Graphite Carbon Metrics", "path": "dashboards/carbon_metrics.json" }],
|
||||
|
||||
"metrics": true,
|
||||
"alerting": true,
|
||||
"annotations": true,
|
||||
"tables": false,
|
||||
|
||||
"queryOptions": {
|
||||
"maxDataPoints": true,
|
||||
@ -27,8 +26,11 @@
|
||||
"large": "img/graphite_logo.png"
|
||||
},
|
||||
"links": [
|
||||
{"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", "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/"
|
||||
}
|
||||
],
|
||||
"version": "5.0.0"
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
"annotations": false,
|
||||
"logs": true,
|
||||
"explore": true,
|
||||
"tables": true,
|
||||
"info": {
|
||||
"description": "Grafana Logging Data Source for Grafana",
|
||||
"author": {
|
||||
@ -25,4 +26,4 @@
|
||||
],
|
||||
"version": "5.3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,9 +18,9 @@
|
||||
"alerting": true,
|
||||
"annotations": true,
|
||||
"metrics": true,
|
||||
"tables": true,
|
||||
|
||||
"queryOptions": {
|
||||
"minInterval": true
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,9 +19,9 @@
|
||||
"alerting": true,
|
||||
"annotations": true,
|
||||
"metrics": true,
|
||||
"tables": true,
|
||||
|
||||
"queryOptions": {
|
||||
"minInterval": true
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
"defaultMatchFormat": "pipe",
|
||||
"annotations": true,
|
||||
"alerting": true,
|
||||
"tables": false,
|
||||
|
||||
"info": {
|
||||
"description": "OpenTSDB Data Source for Grafana",
|
||||
|
@ -19,9 +19,9 @@
|
||||
"alerting": true,
|
||||
"annotations": true,
|
||||
"metrics": true,
|
||||
"tables": true,
|
||||
|
||||
"queryOptions": {
|
||||
"minInterval": true
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
"alerting": true,
|
||||
"annotations": true,
|
||||
"explore": true,
|
||||
"tables": true,
|
||||
"queryOptions": {
|
||||
"minInterval": true
|
||||
},
|
||||
@ -44,4 +45,4 @@
|
||||
],
|
||||
"version": "5.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
"metrics": true,
|
||||
"alerting": true,
|
||||
"annotations": true,
|
||||
"tables": false,
|
||||
"state": "beta",
|
||||
"queryOptions": {
|
||||
"maxDataPoints": true,
|
||||
|
@ -41,6 +41,7 @@ export interface PluginMeta {
|
||||
|
||||
// Datasource-specific
|
||||
metrics?: boolean;
|
||||
tables?: boolean;
|
||||
logs?: boolean;
|
||||
explore?: boolean;
|
||||
annotations?: boolean;
|
||||
|
Loading…
Reference in New Issue
Block a user