From 682d2a167512a048b846836b180e4e84de8940ae Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Torkel=20=C3=96degaard?= <torkel.odegaard@gmail.com>
Date: Wed, 10 Sep 2014 10:46:04 +0200
Subject: [PATCH] Dashboard: time range can now be read from URL parameters,
 will override dashboard saved time range, Closes #787, Closes #761

---
 CHANGELOG.md                    |  1 +
 src/app/services/timeSrv.js     | 36 ++++++++++++++++++++++++++---
 src/test/specs/timeSrv-specs.js | 41 ++++++++++++++++++++++++++++++++-
 3 files changed, 74 insertions(+), 4 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e05bc4c913f..a182bbf6cad 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -45,6 +45,7 @@
 - [Issue #685](https://github.com/grafana/grafana/issues/685). Dashboard: New config.js option to change/remove window title prefix.
 - [Issue #781](https://github.com/grafana/grafana/issues/781). Dashboard: Title URL is now slugified for greater URL readability, works with both ES & InfluxDB storage, is backward compatible
 - [Issue #785](https://github.com/grafana/grafana/issues/785). Elasticsearch: Support for full elasticsearch lucene search grammar when searching for dashboards, better async search
+- [Issue #787](https://github.com/grafana/grafana/issues/787). Dashboard: time range can now be read from URL parameters, will override dashboard saved time range
 
 **Fixes**
 - [Issue #696](https://github.com/grafana/grafana/issues/696). Graph: Fix for y-axis format 'none' when values are in scientific notation (ex 2.3e-13)
diff --git a/src/app/services/timeSrv.js b/src/app/services/timeSrv.js
index 7d81d8fe375..1ee9ecdf925 100644
--- a/src/app/services/timeSrv.js
+++ b/src/app/services/timeSrv.js
@@ -2,13 +2,14 @@ define([
   'angular',
   'lodash',
   'config',
-  'kbn'
-], function (angular, _, config, kbn) {
+  'kbn',
+  'moment'
+], function (angular, _, config, kbn, moment) {
   'use strict';
 
   var module = angular.module('grafana.services');
 
-  module.service('timeSrv', function($rootScope, $timeout, timer) {
+  module.service('timeSrv', function($rootScope, $timeout, $routeParams, timer) {
     var self = this;
 
     this.init = function(dashboard) {
@@ -17,11 +18,40 @@ define([
       this.dashboard = dashboard;
       this.time = dashboard.time;
 
+      this._initTimeFromUrl();
+
       if(this.dashboard.refresh) {
         this.set_interval(this.dashboard.refresh);
       }
     };
 
+    this._parseUrlParam = function(value) {
+      if (value.indexOf('now') !== -1) {
+        return value;
+      }
+      if (value.length === 8) {
+        return moment.utc(value, 'YYYYMMDD').toDate();
+      }
+      if (value.length === 15) {
+        return moment.utc(value, 'YYYYMMDDTHHmmss').toDate();
+      }
+      var epoch = parseInt(value);
+      if (!_.isNaN(epoch)) {
+        return new Date(epoch);
+      }
+
+      return null;
+    };
+
+    this._initTimeFromUrl = function() {
+      if ($routeParams.from) {
+        this.time.from = this._parseUrlParam($routeParams.from) || this.time.from;
+      }
+      if ($routeParams.to) {
+        this.time.to = this._parseUrlParam($routeParams.to) || this.time.to;
+      }
+    };
+
     this.set_interval = function (interval) {
       this.dashboard.refresh = interval;
       if (interval) {
diff --git a/src/test/specs/timeSrv-specs.js b/src/test/specs/timeSrv-specs.js
index a0bc053b1e1..1d8a158e27e 100644
--- a/src/test/specs/timeSrv-specs.js
+++ b/src/test/specs/timeSrv-specs.js
@@ -11,7 +11,7 @@ define([
     var _dashboard;
 
     beforeEach(module('grafana.services'));
-    beforeEach(ctx.providePhase());
+    beforeEach(ctx.providePhase(['$routeParams']));
     beforeEach(ctx.createService('timeSrv'));
 
     beforeEach(function() {
@@ -35,6 +35,45 @@ define([
       });
     });
 
+    describe('init time from url', function() {
+      it('should handle relative times', function() {
+        ctx.$routeParams.from = 'now-2d';
+        ctx.$routeParams.to = 'now';
+        ctx.service.init(_dashboard);
+        var time = ctx.service.timeRange(false);
+        expect(time.from).to.be('now-2d');
+        expect(time.to).to.be('now');
+      });
+
+      it('should handle formated dates', function() {
+        ctx.$routeParams.from = '20140410T052010';
+        ctx.$routeParams.to = '20140520T031022';
+        ctx.service.init(_dashboard);
+        var time = ctx.service.timeRange(true);
+        expect(time.from.getTime()).to.equal(new Date("2014-04-10T05:20:10Z").getTime());
+        expect(time.to.getTime()).to.equal(new Date("2014-05-20T03:10:22Z").getTime());
+      });
+
+      it('should handle formated dates without time', function() {
+        ctx.$routeParams.from = '20140410';
+        ctx.$routeParams.to = '20140520';
+        ctx.service.init(_dashboard);
+        var time = ctx.service.timeRange(true);
+        expect(time.from.getTime()).to.equal(new Date("2014-04-10T00:00:00Z").getTime());
+        expect(time.to.getTime()).to.equal(new Date("2014-05-20T00:00:00Z").getTime());
+      });
+
+      it('should handle epochs', function() {
+        ctx.$routeParams.from = '1410337646373';
+        ctx.$routeParams.to = '1410337665699';
+        ctx.service.init(_dashboard);
+        var time = ctx.service.timeRange(true);
+        expect(time.from.getTime()).to.equal(1410337646373);
+        expect(time.to.getTime()).to.equal(1410337665699);
+      });
+
+    });
+
     describe('setTime', function() {
       it('should return disable refresh for absolute times', function() {
         _dashboard.refresh = false;