diff --git a/pkg/plugins/models.go b/pkg/plugins/models.go
index e60ff558df2..4516bbd5491 100644
--- a/pkg/plugins/models.go
+++ b/pkg/plugins/models.go
@@ -38,6 +38,7 @@ type DataSourcePlugin struct {
Annotations bool `json:"annotations"`
Metrics bool `json:"metrics"`
BuiltIn bool `json:"builtIn"`
+ Mixed bool `json:"mixed"`
App string `json:"app"`
}
diff --git a/public/app/core/controllers/all.js b/public/app/core/controllers/all.js
index 0d39cf57d69..d22010cffdc 100644
--- a/public/app/core/controllers/all.js
+++ b/public/app/core/controllers/all.js
@@ -1,9 +1,3 @@
-// import grafanaCtrl from './grafana_ctrl';
-//
-// import * as asd from './sidemenu_ctrl';
-//
-// export {grafanaCtrl};
-
define([
'./grafana_ctrl',
'./search_ctrl',
diff --git a/public/app/core/services/datasource_srv.js b/public/app/core/services/datasource_srv.js
index 3811daad168..054504ec602 100644
--- a/public/app/core/services/datasource_srv.js
+++ b/public/app/core/services/datasource_srv.js
@@ -58,12 +58,21 @@ function (angular, _, coreModule, config) {
}
var deferred = $q.defer();
-
var pluginDef = dsConfig.meta;
- System.import(pluginDef.module).then(function() {
- var AngularService = $injector.get(pluginDef.serviceName);
- var instance = new AngularService(dsConfig, pluginDef);
+ System.import(pluginDef.module).then(function(plugin) {
+ // check if its in cache now
+ if (self.datasources[name]) {
+ deferred.resolve(self.datasources[name]);
+ return;
+ }
+
+ // plugin module needs to export a constructor function named Datasource
+ if (!plugin.Datasource) {
+ return;
+ }
+
+ var instance = $injector.instantiate(plugin.Datasource, {instanceSettings: dsConfig});
instance.meta = pluginDef;
instance.name = name;
self.datasources[name] = instance;
diff --git a/public/app/plugins/datasource/elasticsearch/datasource.d.ts b/public/app/plugins/datasource/elasticsearch/datasource.d.ts
new file mode 100644
index 00000000000..4de8bcda15d
--- /dev/null
+++ b/public/app/plugins/datasource/elasticsearch/datasource.d.ts
@@ -0,0 +1,3 @@
+declare var Datasource: any;
+export {Datasource};
+
diff --git a/public/app/plugins/datasource/elasticsearch/datasource.js b/public/app/plugins/datasource/elasticsearch/datasource.js
index 72eaa4fcfd6..2a43e45aba8 100644
--- a/public/app/plugins/datasource/elasticsearch/datasource.js
+++ b/public/app/plugins/datasource/elasticsearch/datasource.js
@@ -12,28 +12,22 @@ define([
function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticResponse) {
'use strict';
- var module = angular.module('grafana.services');
+ function ElasticDatasource(instanceSettings, $q, backendSrv, templateSrv, timeSrv) {
+ this.basicAuth = instanceSettings.basicAuth;
+ this.withCredentials = instanceSettings.withCredentials;
+ this.url = instanceSettings.url;
+ this.name = instanceSettings.name;
+ this.index = instanceSettings.index;
+ this.timeField = instanceSettings.jsonData.timeField;
+ this.esVersion = instanceSettings.jsonData.esVersion;
+ this.indexPattern = new IndexPattern(instanceSettings.index, instanceSettings.jsonData.interval);
+ this.interval = instanceSettings.jsonData.timeInterval;
+ this.queryBuilder = new ElasticQueryBuilder({
+ timeField: this.timeField,
+ esVersion: this.esVersion,
+ });
- module.factory('ElasticDatasource', function($q, backendSrv, templateSrv, timeSrv) {
-
- function ElasticDatasource(datasource) {
- this.type = 'elasticsearch';
- this.basicAuth = datasource.basicAuth;
- this.withCredentials = datasource.withCredentials;
- this.url = datasource.url;
- this.name = datasource.name;
- this.index = datasource.index;
- this.timeField = datasource.jsonData.timeField;
- this.esVersion = datasource.jsonData.esVersion;
- this.indexPattern = new IndexPattern(datasource.index, datasource.jsonData.interval);
- this.interval = datasource.jsonData.timeInterval;
- this.queryBuilder = new ElasticQueryBuilder({
- timeField: this.timeField,
- esVersion: this.esVersion,
- });
- }
-
- ElasticDatasource.prototype._request = function(method, url, data) {
+ this._request = function(method, url, data) {
var options = {
url: this.url + "/" + url,
method: method,
@@ -52,21 +46,21 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
return backendSrv.datasourceRequest(options);
};
- ElasticDatasource.prototype._get = function(url) {
+ this._get = function(url) {
return this._request('GET', this.indexPattern.getIndexForToday() + url)
- .then(function(results) {
- return results.data;
- });
+ .then(function(results) {
+ return results.data;
+ });
};
- ElasticDatasource.prototype._post = function(url, data) {
+ this._post = function(url, data) {
return this._request('POST', url, data)
- .then(function(results) {
- return results.data;
- });
+ .then(function(results) {
+ return results.data;
+ });
};
- ElasticDatasource.prototype.annotationQuery = function(options) {
+ this.annotationQuery = function(options) {
var annotation = options.annotation;
var timeField = annotation.timeField || '@timestamp';
var queryString = annotation.query || '*';
@@ -147,7 +141,7 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
});
};
- ElasticDatasource.prototype.testDatasource = function() {
+ this.testDatasource = function() {
return this._get('/_stats').then(function() {
return { status: "success", message: "Data source is working", title: "Success" };
}, function(err) {
@@ -159,13 +153,13 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
});
};
- ElasticDatasource.prototype.getQueryHeader = function(searchType, timeFrom, timeTo) {
+ this.getQueryHeader = function(searchType, timeFrom, timeTo) {
var header = {search_type: searchType, "ignore_unavailable": true};
header.index = this.indexPattern.getIndexList(timeFrom, timeTo);
return angular.toJson(header);
};
- ElasticDatasource.prototype.query = function(options) {
+ this.query = function(options) {
var payload = "";
var target;
var sentTargets = [];
@@ -203,7 +197,7 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
});
};
- ElasticDatasource.prototype.getFields = function(query) {
+ this.getFields = function(query) {
return this._get('/_mapping').then(function(res) {
var fields = {};
var typeMap = {
@@ -240,7 +234,7 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
});
};
- ElasticDatasource.prototype.getTerms = function(queryDef) {
+ this.getTerms = function(queryDef) {
var range = timeSrv.timeRange();
var header = this.getQueryHeader('count', range.from, range.to);
var esQuery = angular.toJson(this.queryBuilder.getTermsQuery(queryDef));
@@ -258,7 +252,7 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
});
};
- ElasticDatasource.prototype.metricFindQuery = function(query) {
+ this.metricFindQuery = function(query) {
query = templateSrv.replace(query);
query = angular.fromJson(query);
if (!query) {
@@ -273,14 +267,14 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
}
};
- ElasticDatasource.prototype.getDashboard = function(id) {
+ this.getDashboard = function(id) {
return this._get('/dashboard/' + id)
.then(function(result) {
return angular.fromJson(result._source.dashboard);
});
};
- ElasticDatasource.prototype.searchDashboards = function() {
+ this.searchDashboards = function() {
var query = {
query: { query_string: { query: '*' } },
size: 10000,
@@ -308,7 +302,9 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
return displayHits;
});
};
+ }
- return ElasticDatasource;
- });
+ return {
+ Datasource: ElasticDatasource,
+ };
});
diff --git a/public/app/plugins/datasource/elasticsearch/specs/datasource_specs.ts b/public/app/plugins/datasource/elasticsearch/specs/datasource_specs.ts
index f34d52b42df..88700838cc0 100644
--- a/public/app/plugins/datasource/elasticsearch/specs/datasource_specs.ts
+++ b/public/app/plugins/datasource/elasticsearch/specs/datasource_specs.ts
@@ -1,28 +1,32 @@
-import "../datasource";
import {describe, beforeEach, it, sinon, expect, angularMocks} from 'test/lib/common';
import moment from 'moment';
import angular from 'angular';
import helpers from 'test/specs/helpers';
+import {Datasource} from "../datasource";
describe('ElasticDatasource', function() {
var ctx = new helpers.ServiceTestContext();
+ var instanceSettings: any = {jsonData: {}};
beforeEach(angularMocks.module('grafana.core'));
beforeEach(angularMocks.module('grafana.services'));
beforeEach(ctx.providePhase(['templateSrv', 'backendSrv']));
- beforeEach(ctx.createService('ElasticDatasource'));
- beforeEach(function() {
- ctx.ds = new ctx.service({jsonData: {}});
- });
+ beforeEach(angularMocks.inject(function($q, $rootScope, $httpBackend, $injector) {
+ ctx.$q = $q;
+ ctx.$httpBackend = $httpBackend;
+ ctx.$rootScope = $rootScope;
+ ctx.$injector = $injector;
+ }));
+
+ function createDatasource(instanceSettings) {
+ instanceSettings.jsonData = instanceSettings.jsonData || {};
+ ctx.ds = ctx.$injector.instantiate(Datasource, {instanceSettings: instanceSettings});
+ }
describe('When testing datasource with index pattern', function() {
beforeEach(function() {
- ctx.ds = new ctx.service({
- url: 'http://es.com',
- index: '[asd-]YYYY.MM.DD',
- jsonData: { interval: 'Daily' }
- });
+ createDatasource({url: 'http://es.com', index: '[asd-]YYYY.MM.DD', jsonData: {interval: 'Daily'}});
});
it('should translate index pattern to current day', function() {
@@ -44,11 +48,7 @@ describe('ElasticDatasource', function() {
var requestOptions, parts, header;
beforeEach(function() {
- ctx.ds = new ctx.service({
- url: 'http://es.com',
- index: '[asd-]YYYY.MM.DD',
- jsonData: { interval: 'Daily' }
- });
+ createDatasource({url: 'http://es.com', index: '[asd-]YYYY.MM.DD', jsonData: {interval: 'Daily'}});
ctx.backendSrv.datasourceRequest = function(options) {
requestOptions = options;
@@ -83,7 +83,7 @@ describe('ElasticDatasource', function() {
var requestOptions, parts, header;
beforeEach(function() {
- ctx.ds = new ctx.service({url: 'http://es.com', index: 'test', jsonData: {}});
+ createDatasource({url: 'http://es.com', index: 'test'});
ctx.backendSrv.datasourceRequest = function(options) {
requestOptions = options;
diff --git a/public/app/plugins/datasource/graphite/datasource.js b/public/app/plugins/datasource/graphite/datasource.js
index 49aa589db45..3548fcb9571 100644
--- a/public/app/plugins/datasource/graphite/datasource.js
+++ b/public/app/plugins/datasource/graphite/datasource.js
@@ -301,4 +301,7 @@ function (angular, _, $, config, dateMath) {
});
+ return {
+ serviceName: "GraphiteDatasource"
+ };
});
diff --git a/public/app/plugins/datasource/mixed/datasource.js b/public/app/plugins/datasource/mixed/datasource.js
deleted file mode 100644
index a74e872276b..00000000000
--- a/public/app/plugins/datasource/mixed/datasource.js
+++ /dev/null
@@ -1,35 +0,0 @@
-define([
- 'angular',
- 'lodash',
-],
-function (angular, _) {
- 'use strict';
-
- var module = angular.module('grafana.services');
-
- module.factory('MixedDatasource', function($q, backendSrv, datasourceSrv) {
-
- function MixedDatasource() {
- }
-
- MixedDatasource.prototype.query = function(options) {
- var sets = _.groupBy(options.targets, 'datasource');
- var promises = _.map(sets, function(targets) {
- return datasourceSrv.get(targets[0].datasource).then(function(ds) {
- var opt = angular.copy(options);
- opt.targets = targets;
- return ds.query(opt);
- });
- });
-
- return $q.all(promises).then(function(results) {
- return { data: _.flatten(_.pluck(results, 'data')) };
- });
-
- };
-
- return MixedDatasource;
-
- });
-
-});
diff --git a/public/app/plugins/datasource/mixed/datasource.ts b/public/app/plugins/datasource/mixed/datasource.ts
new file mode 100644
index 00000000000..874773e7f45
--- /dev/null
+++ b/public/app/plugins/datasource/mixed/datasource.ts
@@ -0,0 +1,37 @@
+///
+
+import angular from 'angular';
+import _ from 'lodash';
+
+class MixedDatasource {
+
+ constructor(private $q, private datasourceSrv) {
+ }
+
+ query(options) {
+ var sets = _.groupBy(options.targets, 'datasource');
+ var promises = _.map(sets, targets => {
+ var dsName = targets[0].datasource;
+ if (dsName === '-- Mixed --') {
+ return this.$q([]);
+ }
+
+ return this.datasourceSrv.get(dsName).then(function(ds) {
+ var opt = angular.copy(options);
+ opt.targets = targets;
+ return ds.query(opt);
+ });
+ });
+
+ return this.$q.all(promises).then(function(results) {
+ return { data: _.flatten(_.pluck(results, 'data')) };
+ });
+ }
+}
+
+export {MixedDatasource, MixedDatasource as Datasource}
+
+// var module = angular.module('grafana.services');
+// module.factory('MixedDatasource', MixedDatasource);
+//
+
diff --git a/public/test/lib/common.ts b/public/test/lib/common.ts
index 523a97c2c03..c7e8147c9c9 100644
--- a/public/test/lib/common.ts
+++ b/public/test/lib/common.ts
@@ -2,6 +2,7 @@
var _global = (window);
var beforeEach = _global.beforeEach;
+var before = _global.before;
var describe = _global.describe;
var it = _global.it;
var sinon = _global.sinon;
@@ -9,10 +10,12 @@ var expect = _global.expect;
var angularMocks = {
module: _global.module,
+ inject: _global.inject,
};
export {
beforeEach,
+ before,
describe,
it,
sinon,