diff --git a/pkg/api/api.go b/pkg/api/api.go index 99ab8a51ff2..6e7015ed889 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -69,9 +69,11 @@ func Register(r *macaron.Macaron) { r.Post("/api/user/password/reset", bind(dtos.ResetUserPasswordForm{}), wrap(ResetPassword)) // dashboard snapshots - r.Post("/api/snapshots/", bind(m.CreateDashboardSnapshotCommand{}), CreateDashboardSnapshot) r.Get("/dashboard/snapshot/*", Index) + r.Get("/dashboard/snapshots/", reqSignedIn, Index) + // api for dashboard snapshots + r.Post("/api/snapshots/", bind(m.CreateDashboardSnapshotCommand{}), CreateDashboardSnapshot) r.Get("/api/snapshot/shared-options/", GetSharingOptions) r.Get("/api/snapshots/:key", GetDashboardSnapshot) r.Get("/api/snapshots-delete/:key", DeleteDashboardSnapshot) @@ -183,6 +185,11 @@ func Register(r *macaron.Macaron) { r.Get("/tags", GetDashboardTags) }) + // Dashboard snapshots + r.Group("/dashboard/snapshots", func() { + r.Get("/", wrap(SearchDashboardSnapshots)) + }) + // Playlist r.Group("/playlists", func() { r.Get("/", wrap(SearchPlaylists)) diff --git a/pkg/api/dashboard_snapshot.go b/pkg/api/dashboard_snapshot.go index f2aee67205c..8ed848a7b11 100644 --- a/pkg/api/dashboard_snapshot.go +++ b/pkg/api/dashboard_snapshot.go @@ -36,7 +36,6 @@ func CreateDashboardSnapshot(c *middleware.Context, cmd m.CreateDashboardSnapsho cmd.DeleteKey = util.GetRandomString(32) cmd.OrgId = c.OrgId cmd.UserId = c.UserId - cmd.Name = c.Name metrics.M_Api_Dashboard_Snapshot_Create.Inc(1) } @@ -99,3 +98,43 @@ func DeleteDashboardSnapshot(c *middleware.Context) { c.JSON(200, util.DynMap{"message": "Snapshot deleted. It might take an hour before it's cleared from a CDN cache."}) } + +func SearchDashboardSnapshots(c *middleware.Context) Response { + query := c.Query("query") + limit := c.QueryInt("limit") + + if limit == 0 { + limit = 1000 + } + + searchQuery := m.GetDashboardSnapshotsQuery{ + Name: query, + Limit: limit, + OrgId: c.OrgId, + } + + err := bus.Dispatch(&searchQuery) + if err != nil { + return ApiError(500, "Search failed", err) + } + + dtos := make([]*m.DashboardSnapshotDTO, len(searchQuery.Result)) + for i, snapshot := range searchQuery.Result { + dtos[i] = &m.DashboardSnapshotDTO{ + Id: snapshot.Id, + Name: snapshot.Name, + Key: snapshot.Key, + DeleteKey: snapshot.DeleteKey, + OrgId: snapshot.OrgId, + UserId: snapshot.UserId, + External: snapshot.External, + ExternalUrl: snapshot.ExternalUrl, + Expires: snapshot.Expires, + Created: snapshot.Created, + Updated: snapshot.Updated, + } + } + + return Json(200, dtos) + //return Json(200, searchQuery.Result) +} diff --git a/pkg/api/index.go b/pkg/api/index.go index d3bdff12b09..09ef756f95a 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -60,6 +60,12 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) { Url: "/playlists", }) + data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{ + Text: "Snapshots", + Icon: "fa fa-fw fa-camera-retro", + Url: "/dashboard/snapshots", + }) + if c.OrgRole == m.ROLE_ADMIN { data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{ Text: "Data Sources", diff --git a/pkg/models/dashboard_snapshot.go b/pkg/models/dashboard_snapshot.go index cc563646833..9bfbd06c1ef 100644 --- a/pkg/models/dashboard_snapshot.go +++ b/pkg/models/dashboard_snapshot.go @@ -20,6 +20,22 @@ type DashboardSnapshot struct { Dashboard map[string]interface{} } +// DashboardSnapshotDTO without dashboard map +type DashboardSnapshotDTO struct { + Id int64 `json:"id"` + Name string `json:"name"` + Key string `json:"key"` + DeleteKey string `json:"deleteKey"` + OrgId int64 `json:"orgId"` + UserId int64 `json:"userId"` + External bool `json:"external"` + ExternalUrl string `json:"externalUrl"` + + Expires time.Time `json:"expires"` + Created time.Time `json:"created"` + Updated time.Time `json:"updated"` +} + // ----------------- // COMMANDS @@ -48,3 +64,13 @@ type GetDashboardSnapshotQuery struct { Result *DashboardSnapshot } + +type DashboardSnapshots []*DashboardSnapshot + +type GetDashboardSnapshotsQuery struct { + Name string + Limit int + OrgId int64 + + Result DashboardSnapshots +} diff --git a/pkg/services/sqlstore/dashboard_snapshot.go b/pkg/services/sqlstore/dashboard_snapshot.go index dc8862f2d57..fc94a91cce5 100644 --- a/pkg/services/sqlstore/dashboard_snapshot.go +++ b/pkg/services/sqlstore/dashboard_snapshot.go @@ -12,6 +12,7 @@ func init() { bus.AddHandler("sql", CreateDashboardSnapshot) bus.AddHandler("sql", GetDashboardSnapshot) bus.AddHandler("sql", DeleteDashboardSnapshot) + bus.AddHandler("sql", SearchDashboardSnapshots) } func CreateDashboardSnapshot(cmd *m.CreateDashboardSnapshotCommand) error { @@ -64,3 +65,18 @@ func GetDashboardSnapshot(query *m.GetDashboardSnapshotQuery) error { query.Result = &snapshot return nil } + +func SearchDashboardSnapshots(query *m.GetDashboardSnapshotsQuery) error { + var snapshots = make(m.DashboardSnapshots, 0) + + sess := x.Limit(query.Limit) + + if query.Name != "" { + sess.Where("name LIKE ?", query.Name) + } + + sess.Where("org_id = ?", query.OrgId) + err := sess.Find(&snapshots) + query.Result = snapshots + return err +} diff --git a/public/app/core/routes/all.js b/public/app/core/routes/all.js index ab3bef61ed7..6c6ec4f0724 100644 --- a/public/app/core/routes/all.js +++ b/public/app/core/routes/all.js @@ -137,6 +137,11 @@ define([ templateUrl: 'public/app/partials/reset_password.html', controller : 'ResetPasswordCtrl', }) + .when('/dashboard/snapshots', { + templateUrl: 'app/features/snapshot/partials/snapshots.html', + controller : 'SnapshotsCtrl', + controllerAs: 'ctrl', + }) .when('/apps', { templateUrl: 'public/app/features/apps/partials/list.html', controller: 'AppListCtrl', diff --git a/public/app/features/all.js b/public/app/features/all.js index d4436c9deaa..4ec4719ebdd 100644 --- a/public/app/features/all.js +++ b/public/app/features/all.js @@ -5,6 +5,7 @@ define([ './templating/templateSrv', './dashboard/all', './playlist/all', + './snapshot/all', './panel/all', './profile/profileCtrl', './profile/changePasswordCtrl', diff --git a/public/app/features/snapshot/all.ts b/public/app/features/snapshot/all.ts new file mode 100644 index 00000000000..521c7a4c111 --- /dev/null +++ b/public/app/features/snapshot/all.ts @@ -0,0 +1 @@ +import './snapshot_ctrl'; diff --git a/public/app/features/snapshot/partials/snapshots.html b/public/app/features/snapshot/partials/snapshots.html new file mode 100644 index 00000000000..4a1d44d38a4 --- /dev/null +++ b/public/app/features/snapshot/partials/snapshots.html @@ -0,0 +1,39 @@ + + +
+
+ +

Available snapshots

+ + + + + + + + + + + + + + + + +
NameSnapshot url
+ {{snapshot.name}} + + dashboard/snapshot/{{snapshot.key}} + + + + View + + + + + +
+ +
+
diff --git a/public/app/features/snapshot/snapshot_ctrl.ts b/public/app/features/snapshot/snapshot_ctrl.ts new file mode 100644 index 00000000000..7b38f9ad8eb --- /dev/null +++ b/public/app/features/snapshot/snapshot_ctrl.ts @@ -0,0 +1,42 @@ +/// + +import angular from 'angular'; +import _ from 'lodash'; + +export class SnapshotsCtrl { + + snapshots: any; + + /** @ngInject */ + constructor(private $rootScope, private backendSrv) { + this.backendSrv.get('/api/dashboard/snapshots').then(result => { + this.snapshots = result; + }); + } + + removeSnapshotConfirmed(snapshot) { + _.remove(this.snapshots, {key: snapshot.key}); + this.backendSrv.get('/api/snapshots-delete/' + snapshot.deleteKey) + .then(() => { + this.$rootScope.appEvent('alert-success', ['Snapshot deleted', '']); + }, () => { + this.$rootScope.appEvent('alert-error', ['Unable to delete snapshot', '']); + this.snapshots.push(snapshot); + }); + } + + removeSnapshot(snapshot) { + this.$rootScope.appEvent('confirm-modal', { + title: 'Confirm delete snapshot', + text: 'Are you sure you want to delete snapshot ' + snapshot.name + '?', + yesText: "Delete", + icon: "fa-warning", + onConfirm: () => { + this.removeSnapshotConfirmed(snapshot); + } + }); + } + +} + +angular.module('grafana.controllers').controller('SnapshotsCtrl', SnapshotsCtrl);