mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
more work on dashboard snapshots
This commit is contained in:
parent
a76758255f
commit
964f0861d6
@ -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
|
||||
|
33
pkg/api/dashboard_snapshot.go
Normal file
33
pkg/api/dashboard_snapshot.go
Normal file
@ -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
pkg/models/dashboard_snapshot.go
Normal file
33
pkg/models/dashboard_snapshot.go
Normal file
@ -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
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
package models
|
||||
|
||||
import "errors"
|
||||
|
||||
type OAuthType int
|
||||
|
||||
const (
|
||||
@ -7,3 +9,5 @@ const (
|
||||
GOOGLE
|
||||
TWITTER
|
||||
)
|
||||
|
||||
var ErrNotFound = errors.New("Not found")
|
||||
|
46
pkg/services/sqlstore/dashboard_snapshot.go
Normal file
46
pkg/services/sqlstore/dashboard_snapshot.go
Normal file
@ -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
pkg/services/sqlstore/dashboard_snapshot_test.go
Normal file
37
pkg/services/sqlstore/dashboard_snapshot_test.go
Normal file
@ -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
pkg/services/sqlstore/migrations/dashboard_snapshot_mig.go
Normal file
24
pkg/services/sqlstore/migrations/dashboard_snapshot_mig.go
Normal file
@ -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)
|
||||
}
|
@ -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();
|
||||
|
||||
});
|
||||
|
30
src/app/features/dashboard/shareSnapshotCtrl.js
Normal file
30
src/app/features/dashboard/shareSnapshotCtrl.js
Normal file
@ -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);
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
});
|
@ -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>
|
Loading…
Reference in New Issue
Block a user