Commit 964f0861 by Torkel Ödegaard

more work on dashboard snapshots

parent a7675825
......@@ -41,6 +41,10 @@ func Register(r *macaron.Macaron) {
r.Get("/signup", Index)
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
r.Group("/api", func() {
// user
......
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)
}
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
}
package models
import "errors"
type OAuthType int
const (
......@@ -7,3 +9,5 @@ const (
GOOGLE
TWITTER
)
var ErrNotFound = errors.New("Not found")
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
}
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")
})
})
})
}
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)
}
......@@ -15,6 +15,7 @@ func AddMigrations(mg *Migrator) {
addDashboardMigration(mg)
addDataSourceMigration(mg)
addApiKeyMigrations(mg)
addDashboardSnapshotMigrations(mg)
}
func addMigrationLogMigrations(mg *Migrator) {
......
......@@ -5,6 +5,7 @@ define([
'./playlistCtrl',
'./rowCtrl',
'./sharePanelCtrl',
'./shareSnapshotCtrl',
'./submenuCtrl',
'./dashboardSrv',
'./keybindings',
......
......@@ -2,11 +2,11 @@
<div class="gf-box-header">
<div class="gf-box-title">
<i class="fa fa-share"></i>
Share
Share Dashboard
</div>
<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>
......@@ -45,4 +45,17 @@
</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>
......@@ -9,7 +9,7 @@ function (angular, _, require, config) {
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.editor = { index: 0 };
......@@ -81,6 +81,17 @@ function (angular, _, require, config) {
$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();
});
......
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);
};
});
});
<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>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment