Pārlūkot izejas kodu

feat(plugin-editors): more work on plugin editor loading

Torkel Ödegaard 10 gadi atpakaļ
vecāks
revīzija
12f487e223

+ 2 - 0
public/app/core/core.ts

@@ -16,6 +16,8 @@ import "./directives/password_strenght";
 import "./directives/spectrum_picker";
 import "./directives/tags";
 import "./directives/value_select_dropdown";
+import "./directives/plugin_directive_loader";
+import "./directives/rebuild_on_change";
 import "./directives/give_focus";
 import './jquery_extended';
 import './partials';

+ 92 - 0
public/app/core/directives/plugin_directive_loader.ts

@@ -0,0 +1,92 @@
+///<reference path="../../headers/common.d.ts" />
+
+import angular from 'angular';
+import _ from 'lodash';
+
+import coreModule from '../core_module';
+
+function pluginDirectiveLoader($compile, datasourceSrv) {
+
+  function getPluginComponentDirective(options) {
+    return function() {
+      return {
+        templateUrl: options.Component.templateUrl,
+        restrict: 'E',
+        controller: options.Component,
+        controllerAs: 'ctrl',
+        bindToController: true,
+        scope: options.bindings,
+        link: (scope, elem, attrs, ctrl) => {
+          if (ctrl.link) {
+            ctrl.link(scope, elem, attrs, ctrl);
+          }
+        }
+      };
+    };
+  }
+
+  function getModule(scope, attrs) {
+    switch (attrs.type) {
+      case "metrics-query-editor":
+        let datasource = scope.target.datasource || scope.ctrl.panel.datasource;
+        return datasourceSrv.get(datasource).then(ds => {
+          if (!scope.target.refId) {
+            scope.target.refId = 'A';
+          }
+
+          return System.import(ds.meta.module).then(dsModule => {
+            return {
+              name: 'metrics-query-editor-' + ds.meta.id,
+              bindings: {target: "=", panelCtrl: "="},
+              attrs: {"target": "target", "panel-ctrl": "ctrl"},
+              Component: dsModule.MetricsQueryEditor
+            };
+          });
+        });
+      case 'datasource-config-view':
+        return System.import(scope.datasourceMeta.module).then(function(dsModule) {
+          return {
+            name: 'ds-config-' + scope.datasourceMeta.id,
+            bindings: {meta: "=", current: "="},
+            attrs: {meta: "datasourceMeta", current: "current"},
+            Component: dsModule.ConfigView,
+          };
+        });
+    }
+  }
+
+  function appendAndCompile(scope, elem, componentInfo) {
+    console.log('compile', elem, componentInfo);
+    var child = angular.element(document.createElement(componentInfo.name));
+    _.each(componentInfo.attrs, (value, key) => {
+      child.attr(key, value);
+    });
+
+    $compile(child)(scope);
+
+    elem.empty();
+    elem.append(child);
+  }
+
+  function registerPluginComponent(scope, elem, attrs, componentInfo) {
+    if (!componentInfo.Component.registered) {
+      var directiveName = attrs.$normalize(componentInfo.name);
+      var directiveFn = getPluginComponentDirective(componentInfo);
+      coreModule.directive(directiveName, directiveFn);
+      componentInfo.Component.registered = true;
+    }
+
+    appendAndCompile(scope, elem, componentInfo);
+  }
+
+  return {
+    restrict: 'E',
+    link: function(scope, elem, attrs) {
+      getModule(scope, attrs).then(function (componentInfo) {
+        registerPluginComponent(scope, elem, attrs, componentInfo);
+      });
+    }
+  };
+}
+
+coreModule.directive('pluginDirectiveLoader', pluginDirectiveLoader);

+ 58 - 0
public/app/core/directives/rebuild_on_change.ts

@@ -0,0 +1,58 @@
+///<reference path="../../headers/common.d.ts" />
+
+import angular from 'angular';
+import _ from 'lodash';
+import $ from 'jquery';
+
+import coreModule from '../core_module';
+
+function getBlockNodes(nodes) {
+  var node = nodes[0];
+  var endNode = nodes[nodes.length - 1];
+  var blockNodes;
+
+  for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
+    if (blockNodes || nodes[i] !== node) {
+      if (!blockNodes) {
+        blockNodes = $([].slice.call(nodes, 0, i));
+      }
+      blockNodes.push(node);
+    }
+  }
+
+  return blockNodes || nodes;
+}
+
+function rebuildOnChange($compile) {
+
+  return {
+    transclude: true,
+    priority: 600,
+    restrict: 'A',
+    link: function(scope, elem, attrs, ctrl, transclude) {
+      var childScope, previousElements;
+      var uncompiledHtml;
+
+      scope.$watch(attrs.rebuildOnChange, function rebuildOnChangeAction(value) {
+
+        if (childScope) {
+          childScope.$destroy();
+          childScope = null;
+          elem.empty();
+        }
+
+        if (value) {
+          if (!childScope) {
+            transclude(function(clone, newScope) {
+              childScope = newScope;
+              elem.append($compile(clone)(childScope));
+            });
+          }
+        }
+
+      });
+    }
+  };
+}
+
+coreModule.directive('rebuildOnChange', rebuildOnChange);

+ 6 - 1
public/app/features/datasources/partials/edit.html

@@ -41,7 +41,12 @@
 				<div class="clearfix"></div>
 			</div>
 
-			<ds-config-view ng-if="datasourceMeta.id" ds-meta="datasourceMeta" current="current"></ds-config-view>
+			<div rebuild-on-change="datasourceMeta.id">
+				<plugin-directive-loader type="datasource-config-view">
+				</plugin-directive-loader>
+			</div>
+
+			<!-- <ds&#45;config&#45;view ds&#45;meta="datasourceMeta" current="current"></ds&#45;config&#45;view> -->
 
 			<div ng-if="testing" style="margin-top: 25px">
 				<h5 ng-show="!testing.done">Testing.... <i class="fa fa-spiner fa-spin"></i></h5>

+ 1 - 1
public/app/features/panel/panel_directive.ts

@@ -50,7 +50,7 @@ var module = angular.module('grafana.directives');
 module.directive('grafanaPanel', function() {
   return {
     restrict: 'E',
-    templateUrl: 'app/features/panel/partials/panel.html',
+    templateUrl: 'public/app/features/panel/partials/panel.html',
     transclude: true,
     scope: { ctrl: "=" },
     link: function(scope, elem) {

+ 0 - 101
public/app/features/panel/query_editor.ts

@@ -5,105 +5,6 @@ import _ from 'lodash';
 
 var directivesModule = angular.module('grafana.directives');
 
-function pluginDirectiveLoader($compile, datasourceSrv) {
-
-  function getPluginComponentDirective(options) {
-    return function() {
-      return {
-        templateUrl: options.Component.templateUrl,
-        restrict: 'E',
-        controller: options.Component,
-        controllerAs: 'ctrl',
-        bindToController: true,
-        scope: options.bindings,
-        link: (scope, elem, attrs, ctrl) => {
-          if (ctrl.link) {
-            ctrl.link(scope, elem, attrs, ctrl);
-          }
-        }
-      };
-    };
-  }
-
-  function getModule(scope, attrs) {
-    switch (attrs.type) {
-      case "metrics-query-editor": {
-        let datasource = scope.target.datasource || scope.ctrl.panel.datasource;
-        return datasourceSrv.get(datasource).then(ds => {
-          if (!scope.target.refId) {
-            scope.target.refId = 'A';
-          }
-
-          return System.import(ds.meta.module).then(dsModule => {
-            return {
-              name: 'metrics-query-editor-' + ds.meta.id,
-              bindings: {target: "=", panelCtrl: "="},
-              attrs: {"target": "target", "panel-ctrl": "ctrl"},
-              Component: dsModule.MetricsQueryEditor
-            };
-          });
-        });
-      }
-    }
-  }
-
-  function appendAndCompile(scope, elem, componentInfo) {
-    var child = angular.element(document.createElement(componentInfo.name));
-    _.each(componentInfo.attrs, (value, key) => {
-      child.attr(key, value);
-    });
-
-    $compile(child)(scope);
-
-    elem.empty();
-    elem.append(child);
-  }
-
-  function registerPluginComponent(scope, elem, attrs, componentInfo) {
-    if (!componentInfo.Component.registered) {
-      var directiveName = attrs.$normalize(componentInfo.name);
-      var directiveFn = getPluginComponentDirective(componentInfo);
-      directivesModule.directive(directiveName, directiveFn);
-      componentInfo.Component.registered = true;
-    }
-
-    appendAndCompile(scope, elem, componentInfo);
-  }
-
-  return {
-    restrict: 'E',
-    link: function(scope, elem, attrs) {
-      getModule(scope, attrs).then(function (componentInfo) {
-        registerPluginComponent(scope, elem, attrs, componentInfo);
-      });
-    }
-  };
-}
-
-/** @ngInject */
-function metricsQueryEditor(dynamicDirectiveSrv, datasourceSrv) {
-  return dynamicDirectiveSrv.create({
-    watchPath: "ctrl.panel.datasource",
-    directive: scope => {
-      let datasource = scope.target.datasource || scope.ctrl.panel.datasource;
-      return datasourceSrv.get(datasource).then(ds => {
-        scope.datasource = ds;
-
-        if (!scope.target.refId) {
-          scope.target.refId = 'A';
-        }
-
-        return System.import(ds.meta.module).then(dsModule => {
-          return {
-            name: 'metrics-query-editor-' + ds.meta.id,
-            fn: dsModule.metricsQueryEditor,
-          };
-        });
-      });
-    }
-  });
-}
-
 /** @ngInject */
 function metricsQueryOptions(dynamicDirectiveSrv, datasourceSrv) {
   return dynamicDirectiveSrv.create({
@@ -121,6 +22,4 @@ function metricsQueryOptions(dynamicDirectiveSrv, datasourceSrv) {
   });
 }
 
-directivesModule.directive('pluginDirectiveLoader', pluginDirectiveLoader);
-directivesModule.directive('metricsQueryEditor', metricsQueryEditor);
 directivesModule.directive('metricsQueryOptions', metricsQueryOptions);

+ 5 - 0
public/app/plugins/datasource/graphite/module.js

@@ -23,11 +23,16 @@ function (GraphiteDatasource) {
     return {templateUrl: 'public/app/plugins/datasource/graphite/partials/config.html'};
   }
 
+  function ConfigView() {
+  }
+  ConfigView.templateUrl = 'public/app/plugins/datasource/graphite/partials/config.html';
+
   return {
     Datasource: GraphiteDatasource,
     configView: configView,
     annotationsQueryEditor: annotationsQueryEditor,
     metricsQueryEditor: metricsQueryEditor,
     metricsQueryOptions: metricsQueryOptions,
+    ConfigView: ConfigView
   };
 });

+ 0 - 3
public/app/plugins/datasource/prometheus/datasource.d.ts

@@ -1,3 +0,0 @@
-declare var Datasource: any;
-export default Datasource;
-

+ 0 - 282
public/app/plugins/datasource/prometheus/datasource.js

@@ -1,282 +0,0 @@
-define([
-  'angular',
-  'lodash',
-  'moment',
-  'app/core/utils/datemath',
-  './query_ctrl',
-],
-function (angular, _, moment, dateMath) {
-  'use strict';
-
-  var durationSplitRegexp = /(\d+)(ms|s|m|h|d|w|M|y)/;
-
-  /** @ngInject */
-  function PrometheusDatasource(instanceSettings, $q, backendSrv, templateSrv) {
-    this.type = 'prometheus';
-    this.editorSrc = 'app/features/prometheus/partials/query.editor.html';
-    this.name = instanceSettings.name;
-    this.supportMetrics = true;
-    this.url = instanceSettings.url;
-    this.directUrl = instanceSettings.directUrl;
-    this.basicAuth = instanceSettings.basicAuth;
-    this.withCredentials = instanceSettings.withCredentials;
-    this.lastErrors = {};
-
-    this._request = function(method, url) {
-      var options = {
-        url: this.url + url,
-        method: method
-      };
-
-      if (this.basicAuth || this.withCredentials) {
-        options.withCredentials = true;
-      }
-      if (this.basicAuth) {
-        options.headers = {
-          "Authorization": this.basicAuth
-        };
-      }
-
-      return backendSrv.datasourceRequest(options);
-    };
-
-    // Called once per panel (graph)
-    this.query = function(options) {
-      var start = getPrometheusTime(options.range.from, false);
-      var end = getPrometheusTime(options.range.to, true);
-
-      var queries = [];
-      options = _.clone(options);
-      _.each(options.targets, _.bind(function(target) {
-        if (!target.expr || target.hide) {
-          return;
-        }
-
-        var query = {};
-        query.expr = templateSrv.replace(target.expr, options.scopedVars);
-
-        var interval = target.interval || options.interval;
-        var intervalFactor = target.intervalFactor || 1;
-        target.step = query.step = this.calculateInterval(interval, intervalFactor);
-        var range = Math.ceil(end - start);
-        // Prometheus drop query if range/step > 11000
-        // calibrate step if it is too big
-        if (query.step !== 0 && range / query.step > 11000) {
-          target.step = query.step = Math.ceil(range / 11000);
-        }
-
-        queries.push(query);
-      }, this));
-
-      // No valid targets, return the empty result to save a round trip.
-      if (_.isEmpty(queries)) {
-        var d = $q.defer();
-        d.resolve({ data: [] });
-        return d.promise;
-      }
-
-      var allQueryPromise = _.map(queries, _.bind(function(query) {
-        return this.performTimeSeriesQuery(query, start, end);
-      }, this));
-
-      var self = this;
-      return $q.all(allQueryPromise)
-      .then(function(allResponse) {
-        var result = [];
-
-        _.each(allResponse, function(response, index) {
-          if (response.status === 'error') {
-            self.lastErrors.query = response.error;
-            throw response.error;
-          }
-          delete self.lastErrors.query;
-
-          _.each(response.data.data.result, function(metricData) {
-            result.push(transformMetricData(metricData, options.targets[index], start, end));
-          });
-        });
-
-        return { data: result };
-      });
-    };
-
-    this.performTimeSeriesQuery = function(query, start, end) {
-      var url = '/api/v1/query_range?query=' + encodeURIComponent(query.expr) + '&start=' + start + '&end=' + end + '&step=' + query.step;
-      return this._request('GET', url);
-    };
-
-    this.performSuggestQuery = function(query) {
-      var url = '/api/v1/label/__name__/values';
-
-      return this._request('GET', url).then(function(result) {
-        return _.filter(result.data.data, function (metricName) {
-          return metricName.indexOf(query) !== 1;
-        });
-      });
-    };
-
-    this.metricFindQuery = function(query) {
-      if (!query) { return $q.when([]); }
-
-      var interpolated;
-      try {
-        interpolated = templateSrv.replace(query);
-      }
-      catch (err) {
-        return $q.reject(err);
-      }
-
-      var label_values_regex = /^label_values\(([^,]+)(?:,\s*(.+))?\)$/;
-      var metric_names_regex = /^metrics\((.+)\)$/;
-
-      var url;
-      var label_values_query = interpolated.match(label_values_regex);
-      if (label_values_query) {
-        if (!label_values_query[2]) {
-          // return label values globally
-          url = '/api/v1/label/' + label_values_query[1] + '/values';
-
-          return this._request('GET', url).then(function(result) {
-            return _.map(result.data.data, function(value) {
-              return {text: value};
-            });
-          });
-        } else {
-          url = '/api/v1/series?match[]=' + encodeURIComponent(label_values_query[1]);
-
-          return this._request('GET', url)
-            .then(function(result) {
-              return _.map(result.data.data, function(metric) {
-                return {
-                  text: metric[label_values_query[2]],
-                  expandable: true
-                };
-              });
-            });
-        }
-      }
-
-      var metric_names_query = interpolated.match(metric_names_regex);
-      if (metric_names_query) {
-        url = '/api/v1/label/__name__/values';
-
-        return this._request('GET', url)
-          .then(function(result) {
-            return _.chain(result.data.data)
-              .filter(function(metricName) {
-                var r = new RegExp(metric_names_query[1]);
-                return r.test(metricName);
-              })
-              .map(function(matchedMetricName) {
-                return {
-                  text: matchedMetricName,
-                  expandable: true
-                };
-              })
-              .value();
-          });
-      } else {
-        // if query contains full metric name, return metric name and label list
-        url = '/api/v1/series?match[]=' + encodeURIComponent(interpolated);
-
-        return this._request('GET', url)
-          .then(function(result) {
-            return _.map(result.data.data, function(metric) {
-              return {
-                text: getOriginalMetricName(metric),
-                expandable: true
-              };
-            });
-          });
-      }
-    };
-
-    this.testDatasource = function() {
-      return this.metricFindQuery('metrics(.*)').then(function() {
-        return { status: 'success', message: 'Data source is working', title: 'Success' };
-      });
-    };
-
-    PrometheusDatasource.prototype.calculateInterval = function(interval, intervalFactor) {
-      var m = interval.match(durationSplitRegexp);
-      var dur = moment.duration(parseInt(m[1]), m[2]);
-      var sec = dur.asSeconds();
-      if (sec < 1) {
-        sec = 1;
-      }
-
-      return Math.ceil(sec * intervalFactor);
-    };
-
-    function transformMetricData(md, options, start, end) {
-      var dps = [],
-          metricLabel = null;
-
-      metricLabel = createMetricLabel(md.metric, options);
-
-      var stepMs = parseInt(options.step) * 1000;
-      var baseTimestamp = start * 1000;
-      _.each(md.values, function(value) {
-        var dp_value = parseFloat(value[1]);
-        if (_.isNaN(dp_value)) {
-          dp_value = null;
-        }
-
-        var timestamp = value[0] * 1000;
-        for (var t = baseTimestamp; t < timestamp; t += stepMs) {
-          dps.push([null, t]);
-        }
-        baseTimestamp = timestamp + stepMs;
-        dps.push([dp_value, timestamp]);
-      });
-
-      var endTimestamp = end * 1000;
-      for (var t = baseTimestamp; t <= endTimestamp; t += stepMs) {
-        dps.push([null, t]);
-      }
-
-      return { target: metricLabel, datapoints: dps };
-    }
-
-    function createMetricLabel(labelData, options) {
-      if (_.isUndefined(options) || _.isEmpty(options.legendFormat)) {
-        return getOriginalMetricName(labelData);
-      }
-
-      var originalSettings = _.templateSettings;
-      _.templateSettings = {
-        interpolate: /\{\{(.+?)\}\}/g
-      };
-
-      var template = _.template(templateSrv.replace(options.legendFormat));
-      var metricName;
-      try {
-        metricName = template(labelData);
-      } catch (e) {
-        metricName = '{}';
-      }
-
-      _.templateSettings = originalSettings;
-
-      return metricName;
-    }
-
-    function getOriginalMetricName(labelData) {
-      var metricName = labelData.__name__ || '';
-      delete labelData.__name__;
-      var labelPart = _.map(_.pairs(labelData), function(label) {
-        return label[0] + '="' + label[1] + '"';
-      }).join(',');
-      return metricName + '{' + labelPart + '}';
-    }
-
-    function getPrometheusTime(date, roundUp) {
-      if (_.isString(date)) {
-        date = dateMath.parse(date, roundUp);
-      }
-      return (date.valueOf() / 1000).toFixed(0);
-    }
-  }
-
-  return PrometheusDatasource;
-});

+ 278 - 0
public/app/plugins/datasource/prometheus/datasource.ts

@@ -0,0 +1,278 @@
+///<reference path="../../../headers/common.d.ts" />
+
+import angular from 'angular';
+import _ from 'lodash';
+import moment from 'moment';
+
+import * as dateMath from 'app/core/utils/datemath';
+
+var durationSplitRegexp = /(\d+)(ms|s|m|h|d|w|M|y)/;
+
+/** @ngInject */
+function PrometheusDatasource(instanceSettings, $q, backendSrv, templateSrv) {
+  this.type = 'prometheus';
+  this.editorSrc = 'app/features/prometheus/partials/query.editor.html';
+  this.name = instanceSettings.name;
+  this.supportMetrics = true;
+  this.url = instanceSettings.url;
+  this.directUrl = instanceSettings.directUrl;
+  this.basicAuth = instanceSettings.basicAuth;
+  this.withCredentials = instanceSettings.withCredentials;
+  this.lastErrors = {};
+
+  this._request = function(method, url) {
+    var options: any = {
+      url: this.url + url,
+      method: method
+    };
+
+    if (this.basicAuth || this.withCredentials) {
+      options.withCredentials = true;
+    }
+    if (this.basicAuth) {
+      options.headers = {
+        "Authorization": this.basicAuth
+      };
+    }
+
+    return backendSrv.datasourceRequest(options);
+  };
+
+  // Called once per panel (graph)
+  this.query = function(options) {
+    var start = getPrometheusTime(options.range.from, false);
+    var end = getPrometheusTime(options.range.to, true);
+
+    var queries = [];
+    options = _.clone(options);
+    _.each(options.targets, _.bind(function(target) {
+      if (!target.expr || target.hide) {
+        return;
+      }
+
+      var query: any = {};
+      query.expr = templateSrv.replace(target.expr, options.scopedVars);
+
+      var interval = target.interval || options.interval;
+      var intervalFactor = target.intervalFactor || 1;
+      target.step = query.step = this.calculateInterval(interval, intervalFactor);
+      var range = Math.ceil(end - start);
+      // Prometheus drop query if range/step > 11000
+      // calibrate step if it is too big
+      if (query.step !== 0 && range / query.step > 11000) {
+        target.step = query.step = Math.ceil(range / 11000);
+      }
+
+      queries.push(query);
+    }, this));
+
+    // No valid targets, return the empty result to save a round trip.
+    if (_.isEmpty(queries)) {
+      var d = $q.defer();
+      d.resolve({ data: [] });
+      return d.promise;
+    }
+
+    var allQueryPromise = _.map(queries, _.bind(function(query) {
+      return this.performTimeSeriesQuery(query, start, end);
+    }, this));
+
+    var self = this;
+    return $q.all(allQueryPromise)
+    .then(function(allResponse) {
+      var result = [];
+
+      _.each(allResponse, function(response, index) {
+        if (response.status === 'error') {
+          self.lastErrors.query = response.error;
+          throw response.error;
+        }
+        delete self.lastErrors.query;
+
+        _.each(response.data.data.result, function(metricData) {
+          result.push(transformMetricData(metricData, options.targets[index], start, end));
+        });
+      });
+
+      return { data: result };
+    });
+  };
+
+  this.performTimeSeriesQuery = function(query, start, end) {
+    var url = '/api/v1/query_range?query=' + encodeURIComponent(query.expr) + '&start=' + start + '&end=' + end + '&step=' + query.step;
+    return this._request('GET', url);
+  };
+
+  this.performSuggestQuery = function(query) {
+    var url = '/api/v1/label/__name__/values';
+
+    return this._request('GET', url).then(function(result) {
+      return _.filter(result.data.data, function (metricName) {
+        return metricName.indexOf(query) !== 1;
+      });
+    });
+  };
+
+  this.metricFindQuery = function(query) {
+    if (!query) { return $q.when([]); }
+
+    var interpolated;
+    try {
+      interpolated = templateSrv.replace(query);
+    } catch (err) {
+      return $q.reject(err);
+    }
+
+    var label_values_regex = /^label_values\(([^,]+)(?:,\s*(.+))?\)$/;
+    var metric_names_regex = /^metrics\((.+)\)$/;
+
+    var url;
+    var label_values_query = interpolated.match(label_values_regex);
+    if (label_values_query) {
+      if (!label_values_query[2]) {
+        // return label values globally
+        url = '/api/v1/label/' + label_values_query[1] + '/values';
+
+        return this._request('GET', url).then(function(result) {
+          return _.map(result.data.data, function(value) {
+            return {text: value};
+          });
+        });
+      } else {
+        url = '/api/v1/series?match[]=' + encodeURIComponent(label_values_query[1]);
+
+        return this._request('GET', url)
+        .then(function(result) {
+          return _.map(result.data.data, function(metric) {
+            return {
+              text: metric[label_values_query[2]],
+              expandable: true
+            };
+          });
+        });
+      }
+    }
+
+    var metric_names_query = interpolated.match(metric_names_regex);
+    if (metric_names_query) {
+      url = '/api/v1/label/__name__/values';
+
+      return this._request('GET', url)
+      .then(function(result) {
+        return _.chain(result.data.data)
+        .filter(function(metricName) {
+          var r = new RegExp(metric_names_query[1]);
+          return r.test(metricName);
+        })
+        .map(function(matchedMetricName) {
+          return {
+            text: matchedMetricName,
+            expandable: true
+          };
+        })
+        .value();
+      });
+    } else {
+      // if query contains full metric name, return metric name and label list
+      url = '/api/v1/series?match[]=' + encodeURIComponent(interpolated);
+
+      return this._request('GET', url)
+      .then(function(result) {
+        return _.map(result.data.data, function(metric) {
+          return {
+            text: getOriginalMetricName(metric),
+            expandable: true
+          };
+        });
+      });
+    }
+  };
+
+  this.testDatasource = function() {
+    return this.metricFindQuery('metrics(.*)').then(function() {
+      return { status: 'success', message: 'Data source is working', title: 'Success' };
+    });
+  };
+
+  PrometheusDatasource.prototype.calculateInterval = function(interval, intervalFactor) {
+    var m = interval.match(durationSplitRegexp);
+    var dur = moment.duration(parseInt(m[1]), m[2]);
+    var sec = dur.asSeconds();
+    if (sec < 1) {
+      sec = 1;
+    }
+
+    return Math.ceil(sec * intervalFactor);
+  };
+
+  function transformMetricData(md, options, start, end) {
+    var dps = [],
+      metricLabel = null;
+
+    metricLabel = createMetricLabel(md.metric, options);
+
+    var stepMs = parseInt(options.step) * 1000;
+    var baseTimestamp = start * 1000;
+    _.each(md.values, function(value) {
+      var dp_value = parseFloat(value[1]);
+      if (_.isNaN(dp_value)) {
+        dp_value = null;
+      }
+
+      var timestamp = value[0] * 1000;
+      for (var t = baseTimestamp; t < timestamp; t += stepMs) {
+        dps.push([null, t]);
+      }
+      baseTimestamp = timestamp + stepMs;
+      dps.push([dp_value, timestamp]);
+    });
+
+    var endTimestamp = end * 1000;
+    for (var t = baseTimestamp; t <= endTimestamp; t += stepMs) {
+      dps.push([null, t]);
+    }
+
+    return { target: metricLabel, datapoints: dps };
+  }
+
+  function createMetricLabel(labelData, options) {
+    if (_.isUndefined(options) || _.isEmpty(options.legendFormat)) {
+      return getOriginalMetricName(labelData);
+    }
+
+    var originalSettings = _.templateSettings;
+    _.templateSettings = {
+      interpolate: /\{\{(.+?)\}\}/g
+    };
+
+    var template = _.template(templateSrv.replace(options.legendFormat));
+    var metricName;
+    try {
+      metricName = template(labelData);
+    } catch (e) {
+      metricName = '{}';
+    }
+
+    _.templateSettings = originalSettings;
+
+    return metricName;
+  }
+
+  function getOriginalMetricName(labelData) {
+    var metricName = labelData.__name__ || '';
+    delete labelData.__name__;
+    var labelPart = _.map(_.pairs(labelData), function(label) {
+      return label[0] + '="' + label[1] + '"';
+    }).join(',');
+    return metricName + '{' + labelPart + '}';
+  }
+
+  function getPrometheusTime(date, roundUp): number {
+    if (_.isString(date)) {
+      date = dateMath.parse(date, roundUp);
+    }
+    return Math.floor(date.valueOf() / 1000);
+  }
+}
+
+export {PrometheusDatasource};

+ 0 - 20
public/app/plugins/datasource/prometheus/module.js

@@ -1,20 +0,0 @@
-define([
-  './datasource',
-],
-function (PromDatasource) {
-  'use strict';
-
-  function metricsQueryEditor() {
-    return {controller: 'PrometheusQueryCtrl', templateUrl: 'public/app/plugins/datasource/prometheus/partials/query.editor.html'};
-  }
-
-  function configView() {
-    return {templateUrl: 'public/app/plugins/datasource/prometheus/partials/config.html'};
-  }
-
-  return {
-    Datasource: PromDatasource,
-    metricsQueryEditor: metricsQueryEditor,
-    configView: configView,
-  };
-});

+ 23 - 0
public/app/plugins/datasource/prometheus/module.ts

@@ -0,0 +1,23 @@
+import {PrometheusDatasource} from './datasource';
+import {PrometheusQueryCtrl} from './query_ctrl';
+
+
+
+
+  // function metricsQueryEditor() {
+  //   return {controller: 'PrometheusQueryCtrl', templateUrl: 'public/app/plugins/datasource/prometheus/partials/query.editor.html'};
+  // }
+  //
+  // function configView() {
+  //   return {templateUrl: ''};
+  // }
+
+class PrometheusConfigViewCtrl {
+  static templateUrl = 'public/app/plugins/datasource/prometheus/partials/config.html';
+}
+
+export {
+  PrometheusDatasource as Datasource,
+  PrometheusQueryCtrl as MetricsQueryEditor,
+  PrometheusConfigViewCtrl as ConfigView
+};

+ 0 - 67
public/app/plugins/datasource/prometheus/query_ctrl.js

@@ -1,67 +0,0 @@
-define([
-  'angular',
-  'lodash',
-],
-function (angular, _) {
-  'use strict';
-
-  var module = angular.module('grafana.controllers');
-
-  module.controller('PrometheusQueryCtrl', function($scope, templateSrv) {
-    $scope.panelCtrl = $scope.ctrl;
-    $scope.panel = $scope.panelCtrl.panel;
-
-    $scope.init = function() {
-      var target = $scope.target;
-
-      target.expr = target.expr || '';
-      target.intervalFactor = target.intervalFactor || 2;
-
-      $scope.metric = '';
-      $scope.resolutions = _.map([1,2,3,4,5,10], function(f) {
-        return {factor: f, label: '1/' + f};
-      });
-
-      $scope.$on('typeahead-updated', function() {
-        $scope.$apply($scope.inputMetric);
-        $scope.refreshMetricData();
-      });
-    };
-
-    $scope.refreshMetricData = function() {
-      if (!_.isEqual($scope.oldTarget, $scope.target)) {
-        $scope.oldTarget = angular.copy($scope.target);
-        $scope.paneCtrl.refresh();
-      }
-    };
-
-    $scope.inputMetric = function() {
-      $scope.target.expr += $scope.target.metric;
-      $scope.metric = '';
-    };
-
-    $scope.suggestMetrics = function(query, callback) {
-      $scope.datasource
-        .performSuggestQuery(query)
-        .then(callback);
-    };
-
-    $scope.linkToPrometheus = function() {
-      var range = Math.ceil(($scope.range.to.valueOf() - $scope.range.from.valueOf()) / 1000);
-      var endTime = $scope.range.to.utc().format('YYYY-MM-DD HH:mm');
-      var expr = {
-        expr: templateSrv.replace($scope.target.expr, $scope.panel.scopedVars),
-        range_input: range + 's',
-        end_input: endTime,
-        step_input: '',
-        stacked: $scope.panel.stack,
-        tab: 0
-      };
-      var hash = encodeURIComponent(JSON.stringify([expr]));
-      return $scope.datasource.directUrl + '/graph#' + hash;
-    };
-
-    $scope.init();
-  });
-
-});

+ 66 - 0
public/app/plugins/datasource/prometheus/query_ctrl.ts

@@ -0,0 +1,66 @@
+///<reference path="../../../headers/common.d.ts" />
+
+import angular from 'angular';
+import _ from 'lodash';
+import moment from 'moment';
+
+import * as dateMath from 'app/core/utils/datemath';
+
+function PrometheusQueryCtrl($scope, templateSrv) {
+  $scope.panelCtrl = $scope.ctrl;
+  $scope.panel = $scope.panelCtrl.panel;
+
+  $scope.init = function() {
+    var target = $scope.target;
+
+    target.expr = target.expr || '';
+    target.intervalFactor = target.intervalFactor || 2;
+
+    $scope.metric = '';
+    $scope.resolutions = _.map([1,2,3,4,5,10], function(f) {
+      return {factor: f, label: '1/' + f};
+    });
+
+    $scope.$on('typeahead-updated', function() {
+      $scope.$apply($scope.inputMetric);
+      $scope.refreshMetricData();
+    });
+  };
+
+  $scope.refreshMetricData = function() {
+    if (!_.isEqual($scope.oldTarget, $scope.target)) {
+      $scope.oldTarget = angular.copy($scope.target);
+      $scope.paneCtrl.refresh();
+    }
+  };
+
+  $scope.inputMetric = function() {
+    $scope.target.expr += $scope.target.metric;
+    $scope.metric = '';
+  };
+
+  $scope.suggestMetrics = function(query, callback) {
+    $scope.datasource
+    .performSuggestQuery(query)
+    .then(callback);
+  };
+
+  $scope.linkToPrometheus = function() {
+    var range = Math.ceil(($scope.range.to.valueOf() - $scope.range.from.valueOf()) / 1000);
+    var endTime = $scope.range.to.utc().format('YYYY-MM-DD HH:mm');
+    var expr = {
+      expr: templateSrv.replace($scope.target.expr, $scope.panel.scopedVars),
+      range_input: range + 's',
+      end_input: endTime,
+      step_input: '',
+      stacked: $scope.panel.stack,
+      tab: 0
+    };
+    var hash = encodeURIComponent(JSON.stringify([expr]));
+    return $scope.datasource.directUrl + '/graph#' + hash;
+  };
+
+  $scope.init();
+}
+
+export {PrometheusQueryCtrl};

+ 0 - 1
public/views/index.html

@@ -59,7 +59,6 @@
 	<script src="[[.AppSubUrl]]/public/vendor/npm/es5-shim/es5-shim.js"></script>
 	<script src="[[.AppSubUrl]]/public/vendor/npm/es6-shim/es6-shim.js"></script>
 	<script src="[[.AppSubUrl]]/public/vendor/npm/es6-promise/dist/es6-promise.js"></script>
-	<script src="[[.AppSubUrl]]/public/vendor/npm/systemjs/dist/system-polyfills.js"></script>
 	<script src="[[.AppSubUrl]]/public/vendor/npm/systemjs/dist/system.src.js"></script>
 	<script src="[[.AppSubUrl]]/public/app/system.conf.js"></script>
 	<script src="[[.AppSubUrl]]/public/app/boot.js"></script>