فهرست منبع

more work on dashboard snapshots

Torkel Ödegaard 10 سال پیش
والد
کامیت
964f0861d6

+ 4 - 0
pkg/api/api.go

@@ -41,6 +41,10 @@ func Register(r *macaron.Macaron) {
 	r.Get("/signup", Index)
 	r.Get("/signup", Index)
 	r.Post("/api/user/signup", bind(m.CreateUserCommand{}), SignUp)
 	r.Post("/api/user/signup", bind(m.CreateUserCommand{}), SignUp)
 
 
+	// dashboard snapshots
+	r.Post("/api/snapshots/", bind(m.CreateDashboardSnapshotCommand{}), CreateDashboardSnapshot)
+	r.Get("/api/snapshots/:key", GetDashboardSnapshot)
+
 	// authed api
 	// authed api
 	r.Group("/api", func() {
 	r.Group("/api", func() {
 		// user
 		// user

+ 33 - 0
pkg/api/dashboard_snapshot.go

@@ -0,0 +1,33 @@
+package api
+
+import (
+	"github.com/grafana/grafana/pkg/bus"
+	"github.com/grafana/grafana/pkg/middleware"
+	m "github.com/grafana/grafana/pkg/models"
+	"github.com/grafana/grafana/pkg/util"
+)
+
+func CreateDashboardSnapshotCommand(c *middleware.Context, cmd m.CreateDashboardSnapshotCommand) {
+	cmd.Key = util.GetRandomString(20)
+
+	if err := bus.Dispatch(&cmd); err != nil {
+		c.JsonApiErr(500, "Failed to create snaphost", err)
+		return
+	}
+
+	c.JSON(200, util.DynMap{"key": cmd.Key})
+}
+
+func GetDashboardSnapshot(c *middleware.Context) {
+	key := c.Params(":key")
+
+	query := &m.GetDashboardSnapshotQuery{Key: key}
+
+	err := bus.Dispatch(query)
+	if err != nil {
+		c.JsonApiErr(500, "Failed to get dashboard snapshot", err)
+		return
+	}
+
+	c.JSON(200, query.Result)
+}

+ 33 - 0
pkg/models/dashboard_snapshot.go

@@ -0,0 +1,33 @@
+package models
+
+import "time"
+
+// DashboardSnapshot model
+type DashboardSnapshot struct {
+	Id   int64
+	Name string
+	Key  string
+
+	Expires time.Time
+	Created time.Time
+	Updated time.Time
+
+	Dashboard map[string]interface{}
+}
+
+// -----------------
+// COMMANDS
+
+type CreateDashboardSnapshotCommand struct {
+	Dashboard map[string]interface{} `json:"dashboard" binding:"Required"`
+
+	Key string `json:"-"`
+
+	Result *DashboardSnapshot
+}
+
+type GetDashboardSnapshotQuery struct {
+	Key string
+
+	Result *DashboardSnapshot
+}

+ 4 - 0
pkg/models/models.go

@@ -1,5 +1,7 @@
 package models
 package models
 
 
+import "errors"
+
 type OAuthType int
 type OAuthType int
 
 
 const (
 const (
@@ -7,3 +9,5 @@ const (
 	GOOGLE
 	GOOGLE
 	TWITTER
 	TWITTER
 )
 )
+
+var ErrNotFound = errors.New("Not found")

+ 46 - 0
pkg/services/sqlstore/dashboard_snapshot.go

@@ -0,0 +1,46 @@
+package sqlstore
+
+import (
+	"time"
+
+	"github.com/go-xorm/xorm"
+	"github.com/grafana/grafana/pkg/bus"
+	m "github.com/grafana/grafana/pkg/models"
+)
+
+func init() {
+	bus.AddHandler("sql", CreateDashboardSnapshot)
+	bus.AddHandler("sql", GetDashboardSnapshot)
+}
+
+func CreateDashboardSnapshot(cmd *m.CreateDashboardSnapshotCommand) error {
+	return inTransaction(func(sess *xorm.Session) error {
+
+		snapshot := &m.DashboardSnapshot{
+			Key:       cmd.Key,
+			Dashboard: cmd.Dashboard,
+			Expires:   time.Unix(0, 0),
+			Created:   time.Now(),
+			Updated:   time.Now(),
+		}
+
+		_, err := sess.Insert(snapshot)
+		cmd.Result = snapshot
+
+		return err
+	})
+}
+
+func GetDashboardSnapshot(query *m.GetDashboardSnapshotQuery) error {
+	var snapshot m.DashboardSnapshot
+	has, err := x.Where("key=?", query.Key).Get(&snapshot)
+
+	if err != nil {
+		return err
+	} else if has == false {
+		return m.ErrNotFound
+	}
+
+	query.Result = &snapshot
+	return nil
+}

+ 37 - 0
pkg/services/sqlstore/dashboard_snapshot_test.go

@@ -0,0 +1,37 @@
+package sqlstore
+
+import (
+	"testing"
+
+	. "github.com/smartystreets/goconvey/convey"
+
+	m "github.com/grafana/grafana/pkg/models"
+)
+
+func TestDashboardSnapshotDBAccess(t *testing.T) {
+
+	Convey("Testing DashboardSnapshot data access", t, func() {
+		InitTestDB(t)
+
+		Convey("Given saved snaphot", func() {
+			cmd := m.CreateDashboardSnapshotCommand{
+				Key: "hej",
+				Dashboard: map[string]interface{}{
+					"hello": "mupp",
+				},
+			}
+			err := CreateDashboardSnapshot(&cmd)
+			So(err, ShouldBeNil)
+
+			Convey("Should be able to get snaphot by key", func() {
+				query := m.GetDashboardSnapshotQuery{Key: "hej"}
+				err = GetDashboardSnapshot(&query)
+				So(err, ShouldBeNil)
+
+				So(query.Result, ShouldNotBeNil)
+				So(query.Result.Dashboard["hello"], ShouldEqual, "mupp")
+			})
+
+		})
+	})
+}

+ 24 - 0
pkg/services/sqlstore/migrations/dashboard_snapshot_mig.go

@@ -0,0 +1,24 @@
+package migrations
+
+import . "github.com/grafana/grafana/pkg/services/sqlstore/migrator"
+
+func addDashboardSnapshotMigrations(mg *Migrator) {
+	snapshotV3 := Table{
+		Name: "dashboard_snapshot",
+		Columns: []*Column{
+			{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
+			{Name: "name", Type: DB_NVarchar, Length: 255, Nullable: false},
+			{Name: "key", Type: DB_NVarchar, Length: 255, Nullable: false},
+			{Name: "dashboard", Type: DB_Text, Nullable: false},
+			{Name: "expires", Type: DB_DateTime, Nullable: false},
+			{Name: "created", Type: DB_DateTime, Nullable: false},
+			{Name: "updated", Type: DB_DateTime, Nullable: false},
+		},
+		Indices: []*Index{
+			{Cols: []string{"key"}, Type: UniqueIndex},
+		},
+	}
+
+	mg.AddMigration("create dashboard_snapshot table v3", NewAddTableMigration(snapshotV3))
+	addTableIndicesMigrations(mg, "v3", snapshotV3)
+}

+ 1 - 0
pkg/services/sqlstore/migrations/migrations.go

@@ -15,6 +15,7 @@ func AddMigrations(mg *Migrator) {
 	addDashboardMigration(mg)
 	addDashboardMigration(mg)
 	addDataSourceMigration(mg)
 	addDataSourceMigration(mg)
 	addApiKeyMigrations(mg)
 	addApiKeyMigrations(mg)
+	addDashboardSnapshotMigrations(mg)
 }
 }
 
 
 func addMigrationLogMigrations(mg *Migrator) {
 func addMigrationLogMigrations(mg *Migrator) {

+ 1 - 0
src/app/features/dashboard/all.js

@@ -5,6 +5,7 @@ define([
   './playlistCtrl',
   './playlistCtrl',
   './rowCtrl',
   './rowCtrl',
   './sharePanelCtrl',
   './sharePanelCtrl',
+  './shareSnapshotCtrl',
   './submenuCtrl',
   './submenuCtrl',
   './dashboardSrv',
   './dashboardSrv',
   './keybindings',
   './keybindings',

+ 15 - 2
src/app/features/dashboard/partials/shareDashboard.html

@@ -2,11 +2,11 @@
 	<div class="gf-box-header">
 	<div class="gf-box-header">
 		<div class="gf-box-title">
 		<div class="gf-box-title">
 			<i class="fa fa-share"></i>
 			<i class="fa fa-share"></i>
-			Share
+			Share Dashboard
 		</div>
 		</div>
 
 
 		<div ng-model="editor.index" bs-tabs style="text-transform:capitalize;">
 		<div ng-model="editor.index" bs-tabs style="text-transform:capitalize;">
-			<div ng-repeat="tab in ['Link']" data-title="{{tab}}">
+			<div ng-repeat="tab in ['Link', 'Snapshot sharing']" data-title="{{tab}}">
 			</div>
 			</div>
 		</div>
 		</div>
 
 
@@ -45,4 +45,17 @@
 		</div>
 		</div>
 	</div>
 	</div>
 
 
+	<div class="gf-box-body" ng-if="editor.index === 1" ng-controller="ShareSnapshotCtrl">
+		<h5>Share dashboard and data with anyone</h5>
+		<p>
+			<em>
+				This will create a snapshot of the dashboard and the data currently visible. It will be saved and you will
+				get a link, anyone with this link will be able view view the dashboard and the data currently visible.
+			</em>
+		</p>
+
+		<button class="btn btn-success btn" ng-click="snapshot()">Create snapshot</button>
+
+	</div>
+
 </div>
 </div>

+ 12 - 1
src/app/features/dashboard/sharePanelCtrl.js

@@ -9,7 +9,7 @@ function (angular, _, require, config) {
 
 
   var module = angular.module('grafana.controllers');
   var module = angular.module('grafana.controllers');
 
 
-  module.controller('SharePanelCtrl', function($scope, $location, $timeout, timeSrv, $element, templateSrv) {
+  module.controller('SharePanelCtrl', function($scope, $rootScope, $location, $timeout, timeSrv, $element, templateSrv) {
 
 
     $scope.init = function() {
     $scope.init = function() {
       $scope.editor = { index: 0 };
       $scope.editor = { index: 0 };
@@ -81,6 +81,17 @@ function (angular, _, require, config) {
       $scope.imageUrl += '&height=500';
       $scope.imageUrl += '&height=500';
     };
     };
 
 
+    $scope.snapshot = function() {
+      $scope.dashboard.snapshot = true;
+      $rootScope.$broadcast('refresh');
+
+      $timeout(function() {
+        $scope.exportDashboard();
+        $scope.dashboard.snapshot = false;
+        $scope.appEvent('dashboard-snapshot-cleanup');
+      }, 1000);
+    };
+
     $scope.init();
     $scope.init();
 
 
   });
   });

+ 30 - 0
src/app/features/dashboard/shareSnapshotCtrl.js

@@ -0,0 +1,30 @@
+define([
+  'angular',
+],
+function (angular) {
+  'use strict';
+
+  var module = angular.module('grafana.controllers');
+
+  module.controller('ShareSnapshotCtrl', function($scope, $rootScope, backendSrv, $timeout) {
+
+    $scope.snapshot = function() {
+      $scope.dashboard.snapshot = true;
+      $rootScope.$broadcast('refresh');
+
+      $timeout(function() {
+        var dash = angular.copy($scope.dashboard);
+        backendSrv.post('/api/snapshots/', {
+          dashboard: dash
+        }).then(function(results) {
+          console.log(results);
+        });
+
+        $scope.dashboard.snapshot = false;
+        $scope.appEvent('dashboard-snapshot-cleanup');
+      }, 2000);
+    };
+
+  });
+
+});

+ 0 - 18
src/app/partials/shareDashboard.html

@@ -1,18 +0,0 @@
-<div class="modal-body gf-box gf-box-no-margin">
-	<div class="gf-box-header">
-		<div class="gf-box-title">
-			<i class="fa fa-share-alt"></i>
-			Share dashboard
-		</div>
-
-		<button class="gf-box-header-close-btn" ng-click="dismiss();">
-			<i class="fa fa-remove"></i>
-		</button>
-	</div>
-
-	<div class="gf-box-body">
-		<label>Share this dashboard with this URL</label>
-		<input ng-model='share.url' type="text" style="width:90%" onclick="this.select()" onfocus="this.select()">
-	</div>
-
-</div>