Browse Source

feat(timepicker): more work on getting new time formats to work in all data sources

Torkel Ödegaard 10 years ago
parent
commit
a30f73fe36

+ 1 - 2
public/app/components/kbn.js

@@ -1,9 +1,8 @@
 define([
   'jquery',
   'lodash',
-  'moment'
 ],
-function($, _, moment) {
+function($, _) {
   'use strict';
 
   var kbn = {};

+ 1 - 2
public/app/core/directives/ng_model_on_blur.js

@@ -2,9 +2,8 @@ define([
   'kbn',
   'app/core/core_module',
   'app/core/utils/rangeutil',
-  'moment',
 ],
-function (kbn, coreModule, rangeUtil, moment) {
+function (kbn, coreModule, rangeUtil) {
   'use strict';
 
   coreModule.directive('ngModelOnblur', function() {

+ 12 - 8
public/app/core/utils/rangeutil.ts

@@ -65,21 +65,21 @@ _.each(rangeOptions, function (frame) {
   // now/d
   // if no to <expr> then to now is assumed
   function describeTextRange(expr: string) {
-    let rangeExpr = 'now-' + expr + ' to now';
-    if (expr.indexOf('now') === 0) {
-      rangeExpr = expr + ' to now';
+    if (expr.indexOf('now') === -1) {
+      expr = 'now-' + expr;
     }
 
-    let opt = rangeIndex[rangeExpr];
+    let opt = rangeIndex[expr + ' to now'];
     if (opt) {
       return opt;
     }
 
-    opt = {from: 'now-' + expr, to: 'now'};
+    opt = {from: expr, to: 'now'};
 
-    if (/^\d+\w$/.test(expr)) {
-      let unit = expr[expr.length - 1];
-      let amount = parseInt(expr.substring(0, expr.length - 1));
+    let parts = /^now-(\d+)(\w)/.exec(expr);
+    if (parts) {
+      let unit = parts[2];
+      let amount = parseInt(parts[1]);
       let span = spans[unit];
       if (span) {
         opt.display = 'Last ' + amount + ' ' + span.display;
@@ -100,6 +100,10 @@ _.each(rangeOptions, function (frame) {
     if (option) {
       return option.display;
     }
+    if (range.to === 'now') {
+      return describeTextRange(range.from).display;
+    }
+
     return "NA";
   }
 

+ 2 - 5
public/app/features/dashboard/timeSrv.js

@@ -123,13 +123,10 @@ define([
       var _t = this.time;
 
       if(parse === false) {
-        return {
-          from: _t.from,
-          to: _t.to
-        };
+        return { from: _t.from, to: _t.to };
       } else {
         var _from = _t.from;
-        var _to = _t.to || new Date();
+        var _to = _t.to || moment();
 
         return {
           from: dateMath.parse(_from, false),

+ 7 - 16
public/app/features/dashboard/timepicker/timepicker.ts

@@ -35,14 +35,14 @@ export class TimePickerCtrl {
 
   init() {
     this.$scope.panel = this.$scope.dashboard.timepicker;
+    this.$scope.panel.now = false;
 
     _.defaults(this.$scope.panel, TimePickerCtrl.defaults);
 
-    var time = this.timeSrv.timeRange(true);
-    this.$scope.panel.now = false;
+    var time = this.timeSrv.timeRange();
+    var timeRaw = this.timeSrv.timeRange(false);
 
-    var unparsed = this.timeSrv.timeRange(false);
-    if (_.isString(unparsed.to) && unparsed.to.indexOf('now') === 0) {
+    if (_.isString(timeRaw.to) && timeRaw.to.indexOf('now') === 0) {
       this.$scope.panel.now = true;
     }
 
@@ -97,23 +97,14 @@ export class TimePickerCtrl {
     this.$scope.refreshMenuLeftSide = this.$scope.time.rangeString.length < 10;
   }
 
-  cloneTime(time) {
-    var _n = { from: _.clone(time.from), to: _.clone(time.to) };
-
-    // Create new dates as _.clone is shallow.
-    _n.from.date = new Date(_n.from.date);
-    _n.to.date = new Date(_n.to.date);
-    return _n;
-  }
-
   customTime() {
     // Assume the form is valid since we're setting it to something valid
     this.$scope.input.$setValidity("dummy", true);
-    this.$scope.temptime = this.cloneTime(this.$scope.time);
+    this.$scope.temptime = angular.copy(this.$scope.time);
     this.$scope.temptime.now = this.$scope.panel.now;
 
-    this.$scope.temptime.from.date.setHours(0, 0, 0, 0);
-    this.$scope.temptime.to.date.setHours(0, 0, 0, 0);
+    // this.$scope.temptime.from.date.setHours(0, 0, 0, 0);
+    // this.$scope.temptime.to.date.setHours(0, 0, 0, 0);
 
     // Date picker needs the date to be at the start of the day
     if (new Date().getTimezoneOffset() < 0) {

+ 9 - 8
public/app/features/panel/panelHelper.js

@@ -48,7 +48,8 @@ function (angular, dateMath, rangeUtil, _, kbn, $) {
 
     this.updateTimeRange = function(scope) {
       scope.range = timeSrv.timeRange();
-      scope.rangeUnparsed = timeSrv.timeRange(false);
+      scope.rangeRaw = timeSrv.timeRange(false);
+
       this.applyPanelTimeOverrides(scope);
 
       if (scope.panel.maxDataPoints) {
@@ -57,6 +58,7 @@ function (angular, dateMath, rangeUtil, _, kbn, $) {
       else {
         scope.resolution = Math.ceil($(window).width() * (scope.panel.span / 12));
       }
+
       scope.interval = kbn.calculateInterval(scope.range, scope.resolution, scope.panel.interval);
     };
 
@@ -71,11 +73,11 @@ function (angular, dateMath, rangeUtil, _, kbn, $) {
           return;
         }
 
-        if (_.isString(scope.rangeUnparsed.from)) {
+        if (_.isString(scope.rangeRaw.from)) {
           var timeFromDate = dateMath.parse(timeFromInfo.from);
           scope.panelMeta.timeInfo = timeFromInfo.display;
-          scope.rangeUnparsed.from = timeFromInfo.from;
-          scope.rangeUnparsed.to = timeFromInfo.to;
+          scope.rangeRaw.from = timeFromInfo.from;
+          scope.rangeRaw.to = timeFromInfo.to;
           scope.range.from = timeFromDate;
         }
       }
@@ -92,7 +94,7 @@ function (angular, dateMath, rangeUtil, _, kbn, $) {
         scope.range.from = dateMath.parseDateMath(timeShift, scope.range.from, false);
         scope.range.to = dateMath.parseDateMath(timeShift, scope.range.to, true);
 
-        scope.rangeUnparsed = scope.range;
+        scope.rangeRaw = scope.range;
       }
 
       if (scope.panel.hideTimeOverride) {
@@ -102,9 +104,8 @@ function (angular, dateMath, rangeUtil, _, kbn, $) {
 
     this.issueMetricQuery = function(scope, datasource) {
       var metricsQuery = {
-        range: scope.rangeUnparsed,
-        timeFrom: scope.range.valueOf(),
-        timeTo: scope.range.valueOf(),
+        range: scope.range,
+        rangeRaw: scope.rangeRaw,
         interval: scope.interval,
         targets: scope.panel.targets,
         format: scope.panel.renderer === 'png' ? 'png' : 'json',

+ 1 - 1
public/app/panels/graph/module.js

@@ -129,7 +129,7 @@ function (angular, $, _, kbn, moment, TimeSeries, PanelMeta) {
     $scope.refreshData = function(datasource) {
       panelHelper.updateTimeRange($scope);
 
-      $scope.annotationsPromise = annotationsSrv.getAnnotations($scope.rangeUnparsed, $scope.dashboard);
+      $scope.annotationsPromise = annotationsSrv.getAnnotations($scope.rangeRaw, $scope.dashboard);
 
       return panelHelper.issueMetricQuery($scope, datasource)
         .then($scope.dataHandler, function(err) {

+ 4 - 4
public/app/plugins/datasource/elasticsearch/datasource.js

@@ -152,13 +152,13 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
       var target;
       var sentTargets = [];
 
-      var header = this.getQueryHeader(options.timeFrom, options.timeTo);
+      var header = this.getQueryHeader(options.range.from, options.range.to);
 
       for (var i = 0; i < options.targets.length; i++) {
         target = options.targets[i];
         if (target.hide) {return;}
 
-        var esQuery = this.queryBuilder.build(target, options.timeFrom, options.timeTo);
+        var esQuery = this.queryBuilder.build(target);
         payload += header + '\n';
         payload += angular.toJson(esQuery) + '\n';
 
@@ -166,8 +166,8 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
       }
 
       payload = payload.replace(/\$interval/g, options.interval);
-      payload = payload.replace(/\$timeFrom/g, options.timeFrom);
-      payload = payload.replace(/\$timeTo/g, options.timeTo);
+      payload = payload.replace(/\$timeFrom/g, options.range.from.valueOf());
+      payload = payload.replace(/\$timeTo/g, options.range.to.valueOf());
       payload = payload.replace(/\$maxDataPoints/g, options.maxDataPoints);
       payload = templateSrv.replace(payload, options.scopedVars);
 

+ 18 - 17
public/app/plugins/datasource/graphite/datasource.js

@@ -29,8 +29,8 @@ function (angular, _, $, config, dateMath, moment) {
     GraphiteDatasource.prototype.query = function(options) {
       try {
         var graphOptions = {
-          from: this.translateTime(options.range.from, false),
-          until: this.translateTime(options.range.to, true),
+          from: this.translateTime(options.rangeRaw.from, false),
+          until: this.translateTime(options.rangeRaw.to, true),
           targets: options.targets,
           format: options.format,
           cacheTimeout: options.cacheTimeout || this.cacheTimeout,
@@ -135,7 +135,8 @@ function (angular, _, $, config, dateMath, moment) {
 
         return this.doGraphiteRequest({
           method: 'GET',
-          url: '/events/get_data?from=' + this.translateTime(options.range.from, false) + '&until=' + this.translateTime(options.range.to, true) + tags,
+          url: '/events/get_data?from=' + this.translateTime(options.range.from, false) +
+            '&until=' + this.translateTime(options.range.to, true) + tags,
         });
       }
       catch(err) {
@@ -159,16 +160,16 @@ function (angular, _, $, config, dateMath, moment) {
 
       date = moment.utc(date);
 
+      // graphite' s from filter is exclusive
+      // here we step back one minute in order
+      // to guarantee that we get all the data that
+      // exists for the specified range
       if (roundUp) {
         if (date.get('s')) {
           date.add(1, 'm');
         }
       }
       else if (roundUp === false) {
-        // graphite' s from filter is exclusive
-        // here we step back one minute in order
-        // to guarantee that we get all the data that
-        // exists for the specified range
         if (date.get('s')) {
           date.subtract(1, 'm');
         }
@@ -187,14 +188,14 @@ function (angular, _, $, config, dateMath, moment) {
       }
 
       return this.doGraphiteRequest({method: 'GET', url: '/metrics/find/?query=' + interpolated })
-        .then(function(results) {
-          return _.map(results.data, function(metric) {
-            return {
-              text: metric.text,
-              expandable: metric.expandable ? true : false
-            };
-          });
+      .then(function(results) {
+        return _.map(results.data, function(metric) {
+          return {
+            text: metric.text,
+            expandable: metric.expandable ? true : false
+          };
         });
+      });
     };
 
     GraphiteDatasource.prototype.testDatasource = function() {
@@ -205,9 +206,9 @@ function (angular, _, $, config, dateMath, moment) {
 
     GraphiteDatasource.prototype.listDashboards = function(query) {
       return this.doGraphiteRequest({ method: 'GET',  url: '/dashboard/find/', params: {query: query || ''} })
-        .then(function(results) {
-          return results.data.dashboards;
-        });
+      .then(function(results) {
+        return results.data.dashboards;
+      });
     };
 
     GraphiteDatasource.prototype.loadDashboard = function(dashName) {

+ 10 - 12
public/app/plugins/datasource/influxdb/datasource.js

@@ -1,13 +1,13 @@
 define([
   'angular',
   'lodash',
-  'kbn',
+  'app/core/utils/datemath',
   './influxSeries',
   './queryBuilder',
   './directives',
   './queryCtrl',
 ],
-function (angular, _, kbn, InfluxSeries, InfluxQueryBuilder) {
+function (angular, _, dateMath, InfluxSeries, InfluxQueryBuilder) {
   'use strict';
 
   var module = angular.module('grafana.services');
@@ -176,8 +176,8 @@ function (angular, _, kbn, InfluxSeries, InfluxQueryBuilder) {
     };
 
     function getTimeFilter(options) {
-      var from = getInfluxTime(options.range.from);
-      var until = getInfluxTime(options.range.to);
+      var from = getInfluxTime(options.rangeRaw.from);
+      var until = getInfluxTime(options.rangeRaw.to);
       var fromIsAbsolute = from[from.length-1] === 's';
 
       if (until === 'now()' && !fromIsAbsolute) {
@@ -189,17 +189,15 @@ function (angular, _, kbn, InfluxSeries, InfluxQueryBuilder) {
 
     function getInfluxTime(date) {
       if (_.isString(date)) {
-        if (date.indexOf('now') >= 0) {
+        if (date === 'now') {
+          return 'now()';
+        }
+        if (date.indexOf('now-') >= 0) {
           return date.replace('now', 'now()').replace('-', ' - ');
         }
-        date = kbn.parseDate(date);
+        date = dateMath.parse(date);
       }
-
-      return to_utc_epoch_seconds(date);
-    }
-
-    function to_utc_epoch_seconds(date) {
-      return (date.getTime() / 1000).toFixed(0) + 's';
+      return (date.valueOf() / 1000).toFixed(0) + 's';
     }
 
     return InfluxDatasource;

+ 18 - 62
public/app/plugins/datasource/influxdb_08/datasource.js

@@ -1,14 +1,14 @@
 define([
   'angular',
   'lodash',
-  'kbn',
+  'app/core/utils/datemath',
   './influxSeries',
   './queryBuilder',
   './directives',
   './queryCtrl',
   './funcEditor',
 ],
-function (angular, _, kbn, InfluxSeries, InfluxQueryBuilder) {
+function (angular, _, dateMath, InfluxSeries, InfluxQueryBuilder) {
   'use strict';
 
   var module = angular.module('grafana.services');
@@ -58,7 +58,7 @@ function (angular, _, kbn, InfluxSeries, InfluxQueryBuilder) {
     };
 
     InfluxDatasource.prototype.annotationQuery = function(annotation, rangeUnparsed) {
-      var timeFilter = getTimeFilter({ range: rangeUnparsed });
+      var timeFilter = getTimeFilter({ rangeRaw: rangeUnparsed });
       var query = annotation.query.replace('$timeFilter', timeFilter);
       query = templateSrv.replace(query);
 
@@ -187,13 +187,9 @@ function (angular, _, kbn, InfluxSeries, InfluxQueryBuilder) {
       return deferred.promise;
     };
 
-    InfluxDatasource.prototype._getDashboardInternal = function(id, isTemp) {
+    InfluxDatasource.prototype._getDashboardInternal = function(id) {
       var queryString = 'select dashboard from "grafana.dashboard_' + btoa(id) + '"';
 
-      if (isTemp) {
-        queryString = 'select dashboard from "grafana.temp_dashboard_' + btoa(id) + '"';
-      }
-
       return this._seriesQuery(queryString).then(function(results) {
         if (!results || !results.length) {
           return null;
@@ -208,55 +204,19 @@ function (angular, _, kbn, InfluxSeries, InfluxQueryBuilder) {
       });
     };
 
-    InfluxDatasource.prototype.getDashboard = function(id, isTemp) {
-      var self = this;
-      return this._getDashboardInternal(id, isTemp).then(function(dashboard) {
+    InfluxDatasource.prototype.getDashboard = function(id) {
+      return this._getDashboardInternal(id).then(function(dashboard) {
         if (dashboard !== null)  {
           return dashboard;
         }
-
-        // backward compatible load for unslugified ids
-        var slug = kbn.slugifyForUrl(id);
-        if (slug !== id) {
-          return self.getDashboard(slug, isTemp);
-        }
-
         throw "Dashboard not found";
       }, function(err) {
         throw  "Could not load dashboard, " + err.data;
       });
     };
 
-    InfluxDatasource.prototype.deleteDashboard = function(id) {
-      return this._seriesQuery('drop series "grafana.dashboard_' + btoa(id) + '"').then(function(results) {
-        if (!results) {
-          throw "Could not delete dashboard";
-        }
-        return id;
-      }, function(err) {
-        throw "Could not delete dashboard, " + err.data;
-      });
-    };
-
-    InfluxDatasource.prototype.searchDashboards = function(queryString) {
-      var influxQuery = 'select * from /grafana.dashboard_.*/ where ';
-
-      var tagsOnly = queryString.indexOf('tags!:') === 0;
-      if (tagsOnly) {
-        var tagsQuery = queryString.substring(6, queryString.length);
-        influxQuery = influxQuery + 'tags =~ /.*' + tagsQuery + '.*/i';
-      }
-      else {
-        var titleOnly = queryString.indexOf('title:') === 0;
-        if (titleOnly) {
-          var titleQuery = queryString.substring(6, queryString.length);
-          influxQuery = influxQuery + ' title =~ /.*' + titleQuery + '.*/i';
-        }
-        else {
-          influxQuery = influxQuery + '(tags =~ /.*' + queryString + '.*/i or title =~ /.*' + queryString + '.*/i)';
-        }
-      }
-
+    InfluxDatasource.prototype.searchDashboards = function() {
+      var influxQuery = 'select * from /grafana.dashboard_.*/ ';
       return this._seriesQuery(influxQuery).then(function(results) {
         var hits = { dashboards: [], tags: [], tagsOnly: false };
 
@@ -266,20 +226,17 @@ function (angular, _, kbn, InfluxSeries, InfluxQueryBuilder) {
 
         for (var i = 0; i < results.length; i++) {
           var dashCol = _.indexOf(results[i].columns, 'title');
-          var tagsCol = _.indexOf(results[i].columns, 'tags');
           var idCol = _.indexOf(results[i].columns, 'id');
 
           var hit =  {
             id: results[i].points[0][dashCol],
             title: results[i].points[0][dashCol],
-            tags: results[i].points[0][tagsCol].split(",")
           };
 
           if (idCol !== -1) {
             hit.id = results[i].points[0][idCol];
           }
 
-          hit.tags = hit.tags[0] ? hit.tags : [];
           hits.dashboards.push(hit);
         }
         return hits;
@@ -297,8 +254,8 @@ function (angular, _, kbn, InfluxSeries, InfluxQueryBuilder) {
     }
 
     function getTimeFilter(options) {
-      var from = getInfluxTime(options.range.from);
-      var until = getInfluxTime(options.range.to);
+      var from = getInfluxTime(options.rangeRaw.from);
+      var until = getInfluxTime(options.rangeRaw.to);
       var fromIsAbsolute = from[from.length-1] === 's';
 
       if (until === 'now()' && !fromIsAbsolute) {
@@ -310,18 +267,17 @@ function (angular, _, kbn, InfluxSeries, InfluxQueryBuilder) {
 
     function getInfluxTime(date) {
       if (_.isString(date)) {
-        return date.replace('now', 'now()');
+        if (date === 'now') {
+          return 'now()';
+        }
+        if (date.indexOf('now-') >= 0) {
+          return date.replace('now', 'now()');
+        }
+        date = dateMath.parse(date);
       }
-
-      return to_utc_epoch_seconds(date);
-    }
-
-    function to_utc_epoch_seconds(date) {
-      return (date.getTime() / 1000).toFixed(0) + 's';
+      return (date.valueOf() / 1000).toFixed(0) + 's';
     }
 
     return InfluxDatasource;
-
   });
-
 });

+ 1 - 0
public/test/specs/core/utils/datemath_specs.ts

@@ -116,6 +116,7 @@ describe("DateMath", () => {
       expect(date).to.equal(undefined);
     });
   });
+
 });
 
 export = {};

+ 19 - 2
public/test/specs/core/utils/rangeutil_specs.ts

@@ -6,8 +6,7 @@ import moment  = require('moment')
 
 describe("rangeUtil", () => {
 
-  describe("Can get range explained", () => {
-
+  describe("Can get range text described", () => {
     it('should handle simple old expression with only amount and unit', () => {
       var info = rangeUtil.describeTextRange('5m');
       expect(info.display).to.be('Last 5 minutes')
@@ -18,6 +17,12 @@ describe("rangeUtil", () => {
       expect(info.display).to.be('Last 1 hour')
     });
 
+    it('should handle non default amount', () => {
+      var info = rangeUtil.describeTextRange('13h');
+      expect(info.display).to.be('Last 13 hours')
+      expect(info.from).to.be('now-13h')
+    });
+
     it('should handle now/d', () => {
       var info = rangeUtil.describeTextRange('now/d');
       expect(info.display).to.be('The day so far');
@@ -27,7 +32,19 @@ describe("rangeUtil", () => {
       var info = rangeUtil.describeTextRange('now/w');
       expect(info.display).to.be('Week to date');
     });
+  });
+
+  describe("Can get date range described", () => {
+
+    it('Date range with simple ranges', () => {
+      var text = rangeUtil.describeTimeRange({from: 'now-1h', to: 'now'});
+      expect(text).to.be('Last 1 hour')
+    });
 
+    it('Date range with non matching default ranges', () => {
+      var text = rangeUtil.describeTimeRange({from: 'now-13h', to: 'now'});
+      expect(text).to.be('Last 13 hours')
+    });
 
   });
 

+ 4 - 2
public/test/specs/elasticsearch-specs.js

@@ -57,8 +57,10 @@ define([
         };
 
         ctx.ds.query({
-          timeFrom: moment(new Date(2015, 4, 30, 10)),
-          timeTo: moment(new Date(2015, 5, 1, 10)),
+          range: {
+            from: moment([2015, 4, 30, 10]),
+            to: moment([2015, 5, 1, 10])
+          },
           targets: [{ bucketAggs: [], metrics: [] }]
         });
 

+ 1 - 1
public/test/specs/graphiteDatasource-specs.js

@@ -17,7 +17,7 @@ define([
 
     describe('When querying influxdb with one target using query editor target spec', function() {
       var query = {
-        range: { from: 'now-1h', to: 'now' },
+        rangeRaw: { from: 'now-1h', to: 'now' },
         targets: [{ target: 'prod1.count' }, {target: 'prod2.count'}],
         maxDataPoints: 500,
       };

+ 2 - 3
public/test/specs/influxdb-datasource-specs.js

@@ -21,7 +21,7 @@ define([
       var urlExpected = "/series?p=mupp&q=select+mean(value)+from+%22test%22"+
                         "+where+time+%3E+now()-1h+group+by+time(1s)+order+asc";
       var query = {
-        range: { from: 'now-1h', to: 'now' },
+        rangeRaw: { from: 'now-1h', to: 'now' },
         targets: [{ series: 'test', column: 'value', function: 'mean' }],
         interval: '1s'
       };
@@ -54,7 +54,7 @@ define([
       var urlExpected = "/series?p=mupp&q=select+value+from+series"+
                         "+where+time+%3E+now()-1h";
       var query = {
-        range: { from: 'now-1h', to: 'now' },
+        rangeRaw: { from: 'now-1h', to: 'now' },
         targets: [{ query: "select value from series where $timeFilter", rawQuery: true }]
       };
 
@@ -97,6 +97,5 @@ define([
     });
 
   });
-
 });