mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -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"`
|
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"`
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
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 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,17 +86,20 @@ 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
|
<QueryField
|
||||||
datasource={datasource}
|
datasource={datasource}
|
||||||
error={queryError}
|
error={queryError}
|
||||||
@ -97,6 +110,17 @@ class QueryRow extends PureComponent<QueryRowProps> {
|
|||||||
onPressEnter={this.onPressEnter}
|
onPressEnter={this.onPressEnter}
|
||||||
onQueryChange={this.onChangeQuery}
|
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}>
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
|
|
||||||
<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">
|
||||||
@ -21,39 +20,25 @@
|
|||||||
</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>
|
|
||||||
</a>
|
|
||||||
<ul class="dropdown-menu pull-right" role="menu">
|
<ul class="dropdown-menu pull-right" role="menu">
|
||||||
<li role="menuitem" ng-if="ctrl.hasTextEditMode">
|
<li role="menuitem" ng-if="ctrl.hasTextEditMode">
|
||||||
<a tabindex="1" ng-click="ctrl.toggleEditorMode()">Toggle Edit Mode</a>
|
<a tabindex="1" ng-click="ctrl.toggleEditorMode()">Toggle Edit Mode</a>
|
||||||
</li>
|
</li>
|
||||||
<li role="menuitem">
|
<li role="menuitem"><a tabindex="1" ng-click="ctrl.duplicateQuery()">Duplicate</a></li>
|
||||||
<a tabindex="1" ng-click="ctrl.duplicateQuery()">Duplicate</a>
|
<li role="menuitem"><a tabindex="1" ng-click="ctrl.moveQuery(-1)">Move up</a></li>
|
||||||
</li>
|
<li role="menuitem"><a tabindex="1" ng-click="ctrl.moveQuery(1)">Move down</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>
|
</ul>
|
||||||
</label>
|
</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>
|
|
||||||
</a>
|
|
||||||
</label>
|
</label>
|
||||||
<label class="gf-form-label">
|
<label class="gf-form-label">
|
||||||
<a class="pointer" tabindex="1" ng-click="ctrl.removeQuery(ctrl.target)">
|
<a class="pointer" tabindex="1" ng-click="ctrl.removeQuery(ctrl.target)"> <i class="fa fa-trash"></i> </a>
|
||||||
<i class="fa fa-trash"></i>
|
|
||||||
</a>
|
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
@ -28,7 +27,10 @@
|
|||||||
},
|
},
|
||||||
"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"
|
||||||
}
|
}
|
||||||
|
@ -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": {
|
||||||
|
@ -18,9 +18,9 @@
|
|||||||
"alerting": true,
|
"alerting": true,
|
||||||
"annotations": true,
|
"annotations": true,
|
||||||
"metrics": true,
|
"metrics": true,
|
||||||
|
"tables": true,
|
||||||
|
|
||||||
"queryOptions": {
|
"queryOptions": {
|
||||||
"minInterval": true
|
"minInterval": true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -19,9 +19,9 @@
|
|||||||
"alerting": true,
|
"alerting": true,
|
||||||
"annotations": true,
|
"annotations": true,
|
||||||
"metrics": true,
|
"metrics": true,
|
||||||
|
"tables": true,
|
||||||
|
|
||||||
"queryOptions": {
|
"queryOptions": {
|
||||||
"minInterval": true
|
"minInterval": true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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",
|
||||||
|
@ -19,9 +19,9 @@
|
|||||||
"alerting": true,
|
"alerting": true,
|
||||||
"annotations": true,
|
"annotations": true,
|
||||||
"metrics": true,
|
"metrics": true,
|
||||||
|
"tables": true,
|
||||||
|
|
||||||
"queryOptions": {
|
"queryOptions": {
|
||||||
"minInterval": true
|
"minInterval": true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
"alerting": true,
|
"alerting": true,
|
||||||
"annotations": true,
|
"annotations": true,
|
||||||
"explore": true,
|
"explore": true,
|
||||||
|
"tables": true,
|
||||||
"queryOptions": {
|
"queryOptions": {
|
||||||
"minInterval": true
|
"minInterval": true
|
||||||
},
|
},
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user