Skip to content

Commit

Permalink
Merge branch 'query-editor-breakout'
Browse files Browse the repository at this point in the history
Conflicts:
	CHANGELOG.md
  • Loading branch information
torkelo committed Aug 18, 2015
2 parents af39e4d + d4432dd commit 9a9c9b2
Show file tree
Hide file tree
Showing 53 changed files with 1,701 additions and 1,383 deletions.
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
# 2.2 (unreleased)

**New Features && Enhancements**
** New Feature: Mix data sources **
A built in data source is now available named `-- Mixed --`, When picked in the metrics tab,
it allows you to add queries of differnet data source types & instances to the same graph/panel!
[Issue #436](https://github.com/grafana/grafana/issues/436)

** Other new Features && Enhancements**
- [Issue #2457](https://github.com/grafana/grafana/issues/2457). Admin: admin page for all grafana organizations (list / edit view)
- [Issue #1186](https://github.com/grafana/grafana/issues/1186). Time Picker: New option `today`, will set time range from midnight to now

**Fixes**
- [Issue #2490](https://github.com/grafana/grafana/issues/2490). Graphite: Dashboard import was broken in 2.1 and 2.1.1, working now

**Breaking Changes**
- Notice to makers/users of custom data sources, there is a minor breaking change in 2.2 that
require and update to custom data sources for them to work in 2.2. [Read this doc](https://github.com/grafana/grafana/tree/master/docs/sources/datasources/plugin_api.md) for more on the
data source api change.

# 2.1.x (currently unreleased patch branch)

**Fixes**
Expand Down
40 changes: 40 additions & 0 deletions docs/sources/datasources/plugin_api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
----
page_title: Data source Plugin API
page_description: Data Source Plugin Description
page_keywords: grafana, data source, plugin, api, docs
---

# Data source plugin API

All data sources in Grafana are implemented as plugins.

## Breaking change in 2.2

In Grafana 2.2 a breaking change was introduced for how data source query editors
are structured, defined and loaded. This was in order to support mixing multiple data sources
in the same panel.

In Grafana 2.2, the query editor is no longer defined using the partials section in
`plugin.json`, but defined via an angular directive named using convention naming
scheme like `metricQueryEditor<data source type name>`. For example

Graphite defines a directive like this:

```javascript
module.directive('metricQueryEditorGraphite', function() {
return {controller: 'GraphiteQueryCtrl', templateUrl: 'app/plugins/datasource/graphite/partials/query.editor.html'};
});
```

Even though the data source type name is with lowercase `g`, the directive uses capital `G` in `Graphite` because
that is how angular directives needs to be named in order to match an element with name `<metric-query-editor-graphite />`.
You also specify the query controller here instead of in the query.editor.html partial like before.

### query.editor.html

This partial needs to be updated, remove the `np-repeat` this is done in the outer partial now,m the query.editor.html
should only render a single query. Take a look at the Graphite or InfluxDB partials for `query.editor.html` for reference.
You should also add a `tight-form-item` with `{{target.refId}}`, all queries needs to be assigned a letter (`refId`).
These query reference letters are going to be utilized in a later feature.


10 changes: 9 additions & 1 deletion pkg/api/datasources.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,5 +112,13 @@ func UpdateDataSource(c *middleware.Context, cmd m.UpdateDataSourceCommand) {
}

func GetDataSourcePlugins(c *middleware.Context) {
c.JSON(200, plugins.DataSources)
dsList := make(map[string]interface{})

for key, value := range plugins.DataSources {
if value.(map[string]interface{})["builtIn"] == nil {
dsList[key] = value
}
}

c.JSON(200, dsList)
}
8 changes: 7 additions & 1 deletion pkg/api/frontendsettings.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,17 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro

// add grafana backend data source
grafanaDatasourceMeta, _ := plugins.DataSources["grafana"]
datasources["grafana"] = map[string]interface{}{
datasources["-- Grafana --"] = map[string]interface{}{
"type": "grafana",
"meta": grafanaDatasourceMeta,
}

// add mixed backend data source
datasources["-- Mixed --"] = map[string]interface{}{
"type": "mixed",
"meta": plugins.DataSources["mixed"],
}

if defaultDatasource == "" {
defaultDatasource = "grafana"
}
Expand Down
7 changes: 5 additions & 2 deletions public/app/directives/giveFocus.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ function (angular) {
}
setTimeout(function() {
element.focus();
var pos = element.val().length * 2;
element[0].setSelectionRange(pos, pos);
var domEl = element[0];
if (domEl.setSelectionRange) {
var pos = element.val().length * 2;
domEl.setSelectionRange(pos, pos);
}
}, 200);
},true);
};
Expand Down
2 changes: 1 addition & 1 deletion public/app/directives/metric.segment.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function (angular, app, _, $) {
' spellcheck="false" style="display:none"></input>';

var buttonTemplate = '<a class="tight-form-item" ng-class="segment.cssClass" ' +
'tabindex="1" focus-me="segment.focus" ng-bind-html="segment.html"></a>';
'tabindex="1" give-focus="segment.focus" ng-bind-html="segment.html"></a>';

return {
scope: {
Expand Down
3 changes: 1 addition & 2 deletions public/app/features/annotations/partials/editor.html
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,7 @@
</div>
</div>

<div ng-include src="currentDatasource.meta.partials.annotations">
</div>
<datasource-editor-view datasource="currentAnnotation.datasource" name="annotations-query-editor"></datasource-editor-view>

<br>
<button ng-show="editor.index === 1" type="button" class="btn btn-success" ng-click="add()">Add</button>
Expand Down
131 changes: 102 additions & 29 deletions public/app/features/panel/panelDirective.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,108 @@ define([
function (angular, $, config) {
'use strict';

angular
.module('grafana.directives')
.directive('panelLoader', function($compile, $parse) {
return {
restrict: 'E',
link: function(scope, elem, attr) {
var getter = $parse(attr.type), panelType = getter(scope);
var panelPath = config.panels[panelType].path;

scope.require([panelPath + "/module"], function () {
var panelEl = angular.element(document.createElement('grafana-panel-' + panelType));
elem.append(panelEl);
$compile(panelEl)(scope);
});
var module = angular.module('grafana.directives');

module.directive('panelLoader', function($compile, $parse) {
return {
restrict: 'E',
link: function(scope, elem, attr) {
var getter = $parse(attr.type), panelType = getter(scope);
var panelPath = config.panels[panelType].path;

scope.require([panelPath + "/module"], function () {
var panelEl = angular.element(document.createElement('grafana-panel-' + panelType));
elem.append(panelEl);
$compile(panelEl)(scope);
});
}
};
});

module.directive('grafanaPanel', function() {
return {
restrict: 'E',
templateUrl: 'app/features/panel/partials/panel.html',
transclude: true,
link: function(scope, elem) {
var panelContainer = elem.find('.panel-container');

scope.$watchGroup(['fullscreen', 'height', 'panel.height', 'row.height'], function() {
panelContainer.css({ minHeight: scope.height || scope.panel.height || scope.row.height, display: 'block' });
elem.toggleClass('panel-fullscreen', scope.fullscreen ? true : false);
});
}
};
});

module.service('dynamicDirectiveSrv', function($compile, $parse, datasourceSrv) {
var self = this;

this.addDirective = function(options, type, editorScope) {
var panelEl = angular.element(document.createElement(options.name + '-' + type));
options.parentElem.append(panelEl);
$compile(panelEl)(editorScope);
};

this.define = function(options) {
var editorScope;
options.scope.$watch(options.datasourceProperty, function(newVal) {
if (editorScope) {
editorScope.$destroy();
options.parentElem.empty();
}
};
}).directive('grafanaPanel', function() {
return {
restrict: 'E',
templateUrl: 'app/features/panel/partials/panel.html',
transclude: true,
link: function(scope, elem) {
var panelContainer = elem.find('.panel-container');

scope.$watchGroup(['fullscreen', 'height', 'panel.height', 'row.height'], function() {
panelContainer.css({ minHeight: scope.height || scope.panel.height || scope.row.height, display: 'block' });
elem.toggleClass('panel-fullscreen', scope.fullscreen ? true : false);

editorScope = options.scope.$new();
datasourceSrv.get(newVal).then(function(ds) {
self.addDirective(options, ds.meta.type, editorScope);
});
});
};
});

module.directive('queryEditorLoader', function($compile, $parse, datasourceSrv) {
return {
restrict: 'E',
link: function(scope, elem) {
var editorScope;

scope.$watch("panel.datasource", function() {
var datasource = scope.target.datasource || scope.panel.datasource;

datasourceSrv.get(datasource).then(function(ds) {
if (editorScope) {
editorScope.$destroy();
elem.empty();
}

editorScope = scope.$new();
editorScope.datasource = ds;

if (!scope.target.refId) {
scope.target.refId = 'A';
}

var panelEl = angular.element(document.createElement('metric-query-editor-' + ds.meta.type));
elem.append(panelEl);
$compile(panelEl)(editorScope);
});
}
};
});
});
}
};
});

module.directive('datasourceEditorView', function(dynamicDirectiveSrv) {
return {
restrict: 'E',
link: function(scope, elem, attrs) {
dynamicDirectiveSrv.define({
datasourceProperty: attrs.datasource,
name: attrs.name,
scope: scope,
parentElem: elem,
});
}
};
});

});
35 changes: 32 additions & 3 deletions public/app/features/panel/panelSrv.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,21 @@ function (angular, _, config) {
});
};

$scope.addDataQuery = function() {
$scope.panel.targets.push({target: ''});
$scope.addDataQuery = function(datasource) {
var letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
var target = {};

if (datasource) {
target.datasource = datasource.name;
}

target.refId = _.find(letters, function(refId) {
return _.every($scope.panel.targets, function(other) {
return other.refId !== refId;
});
});

$scope.panel.targets.push(target);
};

$scope.removeDataQuery = function (query) {
Expand All @@ -53,7 +66,23 @@ function (angular, _, config) {
};

$scope.setDatasource = function(datasource) {
$scope.panel.datasource = datasource;
// switching to mixed
if (datasource.meta.mixed) {
_.each($scope.panel.targets, function(target) {
target.datasource = $scope.panel.datasource;
if (target.datasource === null) {
target.datasource = config.defaultDatasource;
}
});
}
// switching from mixed
else if ($scope.datasource && $scope.datasource.meta.mixed) {
_.each($scope.panel.targets, function(target) {
delete target.datasource;
});
}

$scope.panel.datasource = datasource.value;
$scope.datasource = null;
$scope.get_data();
};
Expand Down
5 changes: 4 additions & 1 deletion public/app/features/templating/editorCtrl.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ function (angular, _) {

$scope.init = function() {
$scope.editor = { index: 0 };
$scope.datasources = datasourceSrv.getMetricSources();
$scope.datasources = _.filter(datasourceSrv.getMetricSources(), function(ds) {
return !ds.meta.builtIn;
});

$scope.variables = templateSrv.variables;
$scope.reset();

Expand Down
50 changes: 37 additions & 13 deletions public/app/partials/metrics.html
Original file line number Diff line number Diff line change
@@ -1,24 +1,48 @@
<div ng-include src="datasource.meta.partials.query"></div>
<div class="editor-row">

<div class="tight-form-container">
<query-editor-loader ng-repeat="target in panel.targets" ng-class="{'tight-form-disabled': target.hide}" >
</query-editor-loader>
</div>

<div style="margin: 20px 0 0 0">
<button class="btn btn-inverse" ng-click="addDataQuery()" ng-hide="datasource.meta.mixed">
<i class="fa fa-plus"></i>&nbsp;
Query
</button>

<div class="dropdown" ng-if="datasource.meta.mixed">
<button class="btn btn-inverse dropdown-toggle" data-toggle="dropdown">
<i class="fa fa-plus"></i>&nbsp;
Query &nbsp; <span class="caret"></span>
</button>

<ul class="dropdown-menu" role="menu">
<li ng-repeat="datasource in datasources" role="menuitem" ng-hide="datasource.meta.builtIn">
<a ng-click="addDataQuery(datasource);">{{datasource.name}}</a>
</li>
</ul>
</div>

</div>

<datasource-editor-view datasource="panel.datasource" name="metric-query-options"></datasource-editor-view>
</div>

<div class="editor-row" style="margin-top: 30px">
<button class="btn btn-inverse pull-right" ng-click="addDataQuery(panel.target)">
<i class="fa fa-plus"></i>&nbsp;
Add query
</button>

<div class="pull-right dropdown" style="margin-right: 10px;">
<div class="pull-right dropdown" style="margin-right: 10px;">
<button class="btn btn-inverse dropdown-toggle" data-toggle="dropdown" bs-tooltip="'Datasource'">
<i class="fa fa-database"></i>&nbsp;
{{datasource.name}} <span class="caret"></span>
{{datasource.name}} &nbsp; <span class="caret"></span>
</button>

<ul class="dropdown-menu" role="menu">
<li ng-repeat="datasource in datasources" role="menuitem">
<a ng-click="setDatasource(datasource.value);">{{datasource.name}}</a>
</li>
</ul>
</div>
<ul class="dropdown-menu" role="menu">
<li ng-repeat="datasource in datasources" role="menuitem">
<a ng-click="setDatasource(datasource);">{{datasource.name}}</a>
</li>
</ul>
</div>

<div class="clearfix"></div>
</div>
Loading

0 comments on commit 9a9c9b2

Please sign in to comment.