From 8f067a5ed221157d1384975df8c57d888c57008d Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Tue, 19 Jan 2016 01:37:36 -0800 Subject: [PATCH 01/20] Added backend functionality for searching snapshots --- pkg/api/api.go | 11 +++++++++-- pkg/api/dashboard_snapshot.go | 22 +++++++++++++++++++++ pkg/api/index.go | 6 ++++++ pkg/models/dashboard_snapshot.go | 10 ++++++++++ pkg/services/sqlstore/dashboard_snapshot.go | 17 ++++++++++++++++ 5 files changed, 64 insertions(+), 2 deletions(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index ea434ed71da..4f4a1ddd72f 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -68,9 +68,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/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) @@ -182,6 +184,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 521dee29a63..dddb5ed436a 100644 --- a/pkg/api/dashboard_snapshot.go +++ b/pkg/api/dashboard_snapshot.go @@ -98,3 +98,25 @@ 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) + } + + return Json(200, searchQuery.Result) +} diff --git a/pkg/api/index.go b/pkg/api/index.go index d3bdff12b09..ca2f9320215 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 e8f37e2a236..c9221f42815 100644 --- a/pkg/models/dashboard_snapshot.go +++ b/pkg/models/dashboard_snapshot.go @@ -47,3 +47,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 f4611050a77..6b71a0e26b3 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 { @@ -63,3 +64,19 @@ 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 +} From 1ab11540104c0036ef54527722f9da70a35a66c7 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Tue, 19 Jan 2016 04:09:57 -0800 Subject: [PATCH 02/20] Optimized backend queries --- pkg/api/api.go | 3 +-- pkg/api/index.go | 2 +- pkg/services/sqlstore/dashboard_snapshot.go | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index 4f4a1ddd72f..c42bb376df2 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -69,7 +69,6 @@ func Register(r *macaron.Macaron) { // dashboard snapshots r.Get("/dashboard/snapshot/*", Index) - r.Get("/dashboard/snapshots/", reqSignedIn, Index) // api for dashboard snapshots r.Post("/api/snapshots/", bind(m.CreateDashboardSnapshotCommand{}), CreateDashboardSnapshot) @@ -184,7 +183,7 @@ func Register(r *macaron.Macaron) { r.Get("/tags", GetDashboardTags) }) - // dashboard snapshots + // Dashboard snapshots r.Group("/dashboard/snapshots", func() { r.Get("/", wrap(SearchDashboardSnapshots)) }) diff --git a/pkg/api/index.go b/pkg/api/index.go index ca2f9320215..3423651922a 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -63,7 +63,7 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) { data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{ Text: "Snapshots", Icon: "fa fa-fw fa-camera-retro", - Url: "/dashboard/snapshots", + Url: "/snapshots", }) if c.OrgRole == m.ROLE_ADMIN { diff --git a/pkg/services/sqlstore/dashboard_snapshot.go b/pkg/services/sqlstore/dashboard_snapshot.go index 6b71a0e26b3..eef2898a7c0 100644 --- a/pkg/services/sqlstore/dashboard_snapshot.go +++ b/pkg/services/sqlstore/dashboard_snapshot.go @@ -68,7 +68,7 @@ func GetDashboardSnapshot(query *m.GetDashboardSnapshotQuery) error { func SearchDashboardSnapshots(query *m.GetDashboardSnapshotsQuery) error { var snapshots = make(m.DashboardSnapshots, 0) - sess := x.Limit(query.Limit) + sess := x.Cols("name,key,delete_key").Limit(query.Limit) if query.Name != "" { sess.Where("name LIKE ?", query.Name) @@ -77,6 +77,5 @@ func SearchDashboardSnapshots(query *m.GetDashboardSnapshotsQuery) error { sess.Where("org_id = ?", query.OrgId) err := sess.Find(&snapshots) query.Result = snapshots - return err } From bcb44b7b31f747fc936dee819c467000954d94d6 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Tue, 19 Jan 2016 05:02:22 -0800 Subject: [PATCH 03/20] UI and backend connectivity implemented --- pkg/api/api.go | 1 + pkg/api/index.go | 2 +- pkg/services/sqlstore/dashboard_snapshot.go | 2 +- public/app/features/all.js | 1 + public/app/features/snapshot/all.js | 4 ++ .../features/snapshot/partials/snapshots.html | 39 +++++++++++++++++ public/app/features/snapshot/snapshot_ctrl.js | 43 +++++++++++++++++++ .../app/features/snapshot/snapshot_routes.js | 18 ++++++++ 8 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 public/app/features/snapshot/all.js create mode 100644 public/app/features/snapshot/partials/snapshots.html create mode 100644 public/app/features/snapshot/snapshot_ctrl.js create mode 100644 public/app/features/snapshot/snapshot_routes.js diff --git a/pkg/api/api.go b/pkg/api/api.go index c42bb376df2..034d9adc180 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -69,6 +69,7 @@ func Register(r *macaron.Macaron) { // dashboard snapshots r.Get("/dashboard/snapshot/*", Index) + r.Get("/dashboard/snapshots/", reqSignedIn, Index) // api for dashboard snapshots r.Post("/api/snapshots/", bind(m.CreateDashboardSnapshotCommand{}), CreateDashboardSnapshot) diff --git a/pkg/api/index.go b/pkg/api/index.go index 3423651922a..ca2f9320215 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -63,7 +63,7 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) { data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{ Text: "Snapshots", Icon: "fa fa-fw fa-camera-retro", - Url: "/snapshots", + Url: "/dashboard/snapshots", }) if c.OrgRole == m.ROLE_ADMIN { diff --git a/pkg/services/sqlstore/dashboard_snapshot.go b/pkg/services/sqlstore/dashboard_snapshot.go index eef2898a7c0..64e7e31898c 100644 --- a/pkg/services/sqlstore/dashboard_snapshot.go +++ b/pkg/services/sqlstore/dashboard_snapshot.go @@ -68,7 +68,7 @@ func GetDashboardSnapshot(query *m.GetDashboardSnapshotQuery) error { func SearchDashboardSnapshots(query *m.GetDashboardSnapshotsQuery) error { var snapshots = make(m.DashboardSnapshots, 0) - sess := x.Cols("name,key,delete_key").Limit(query.Limit) + sess := x.Limit(query.Limit) if query.Name != "" { sess.Where("name LIKE ?", query.Name) 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.js b/public/app/features/snapshot/all.js new file mode 100644 index 00000000000..45cb9eb594f --- /dev/null +++ b/public/app/features/snapshot/all.js @@ -0,0 +1,4 @@ +define([ + './snapshot_ctrl', + './snapshot_routes' +], function () {}); diff --git a/public/app/features/snapshot/partials/snapshots.html b/public/app/features/snapshot/partials/snapshots.html new file mode 100644 index 00000000000..58b6c872617 --- /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.js b/public/app/features/snapshot/snapshot_ctrl.js new file mode 100644 index 00000000000..345638f4770 --- /dev/null +++ b/public/app/features/snapshot/snapshot_ctrl.js @@ -0,0 +1,43 @@ +define([ + 'angular', + 'lodash' +], +function (angular, _) { + 'use strict'; + + var module = angular.module('grafana.controllers'); + + module.controller('SnapshotsCtrl', function($scope, $location, backendSrv) { + backendSrv.get('/api/dashboard/snapshots') + .then(function(result) { + $scope.snapshots = result; + }); + + $scope.removeSnapshotConfirmed = function(snapshot) { + _.remove($scope.snapshots, {Key: snapshot.Key}); + + backendSrv.get('/api/snapshots-delete/' + snapshot.DeleteKey) + .then(function() { + $scope.appEvent('alert-success', ['Snapshot deleted', '']); + }, function() { + $scope.appEvent('alert-error', ['Unable to delete snapshot', '']); + $scope.snapshots.push(snapshot); + }); + }; + + $scope.removeSnapshot = function(snapshot) { + + $scope.appEvent('confirm-modal', { + title: 'Confirm delete snapshot', + text: 'Are you sure you want to delete snapshot ' + snapshot.Name + '?', + yesText: "Delete", + icon: "fa-warning", + onConfirm: function() { + $scope.removeSnapshotConfirmed(snapshot); + } + }); + + }; + + }); +}); diff --git a/public/app/features/snapshot/snapshot_routes.js b/public/app/features/snapshot/snapshot_routes.js new file mode 100644 index 00000000000..74de0634282 --- /dev/null +++ b/public/app/features/snapshot/snapshot_routes.js @@ -0,0 +1,18 @@ +define([ + 'angular', + 'app/core/config', + 'lodash' +], +function (angular) { + 'use strict'; + + var module = angular.module('grafana.routes'); + + module.config(function($routeProvider) { + $routeProvider + .when('/dashboard/snapshots', { + templateUrl: 'app/features/snapshot/partials/snapshots.html', + controller : 'SnapshotsCtrl' + }); + }); +}); From ca55d1f31526408ba9255578aa9b12f84984ae23 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Tue, 19 Jan 2016 05:05:24 -0800 Subject: [PATCH 04/20] Minor bug fixes --- pkg/api/api.go | 16 +++++------ pkg/api/dashboard_snapshot.go | 30 ++++++++++----------- pkg/api/index.go | 10 +++---- pkg/models/dashboard_snapshot.go | 8 +++--- pkg/services/sqlstore/dashboard_snapshot.go | 20 +++++++------- 5 files changed, 42 insertions(+), 42 deletions(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index 034d9adc180..9477288dca6 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -68,11 +68,11 @@ func Register(r *macaron.Macaron) { r.Post("/api/user/password/reset", bind(dtos.ResetUserPasswordForm{}), wrap(ResetPassword)) // dashboard snapshots - r.Get("/dashboard/snapshot/*", Index) - r.Get("/dashboard/snapshots/", reqSignedIn, Index) + r.Get("/dashboard/snapshot/*", Index) + r.Get("/dashboard/snapshots/", reqSignedIn, Index) - // api for dashboard snapshots - r.Post("/api/snapshots/", bind(m.CreateDashboardSnapshotCommand{}), CreateDashboardSnapshot) + // 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) @@ -184,10 +184,10 @@ func Register(r *macaron.Macaron) { r.Get("/tags", GetDashboardTags) }) - // Dashboard snapshots - r.Group("/dashboard/snapshots", func() { - r.Get("/", wrap(SearchDashboardSnapshots)) - }) + // Dashboard snapshots + r.Group("/dashboard/snapshots", func() { + r.Get("/", wrap(SearchDashboardSnapshots)) + }) // Playlist r.Group("/playlists", func() { diff --git a/pkg/api/dashboard_snapshot.go b/pkg/api/dashboard_snapshot.go index 8eb9da60231..2d1fbce782c 100644 --- a/pkg/api/dashboard_snapshot.go +++ b/pkg/api/dashboard_snapshot.go @@ -101,23 +101,23 @@ func DeleteDashboardSnapshot(c *middleware.Context) { } func SearchDashboardSnapshots(c *middleware.Context) Response { - query := c.Query("query") - limit := c.QueryInt("limit") + query := c.Query("query") + limit := c.QueryInt("limit") - if limit == 0 { - limit = 1000 - } + if limit == 0 { + limit = 1000 + } - searchQuery := m.GetDashboardSnapshotsQuery{ - Name: query, - Limit: limit, - OrgId: c.OrgId, - } + searchQuery := m.GetDashboardSnapshotsQuery{ + Name: query, + Limit: limit, + OrgId: c.OrgId, + } - err := bus.Dispatch(&searchQuery) - if err != nil { - return ApiError(500, "Search failed", err) - } + err := bus.Dispatch(&searchQuery) + if err != nil { + return ApiError(500, "Search failed", err) + } - return Json(200, searchQuery.Result) + return Json(200, searchQuery.Result) } diff --git a/pkg/api/index.go b/pkg/api/index.go index ca2f9320215..09ef756f95a 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -60,11 +60,11 @@ 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", - }) + 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{ diff --git a/pkg/models/dashboard_snapshot.go b/pkg/models/dashboard_snapshot.go index 65163e6b119..8c1a0f47a06 100644 --- a/pkg/models/dashboard_snapshot.go +++ b/pkg/models/dashboard_snapshot.go @@ -52,9 +52,9 @@ type GetDashboardSnapshotQuery struct { type DashboardSnapshots []*DashboardSnapshot type GetDashboardSnapshotsQuery struct { - Name string - Limit int - OrgId int64 + Name string + Limit int + OrgId int64 - Result DashboardSnapshots + Result DashboardSnapshots } diff --git a/pkg/services/sqlstore/dashboard_snapshot.go b/pkg/services/sqlstore/dashboard_snapshot.go index b7ffa0b9912..fc94a91cce5 100644 --- a/pkg/services/sqlstore/dashboard_snapshot.go +++ b/pkg/services/sqlstore/dashboard_snapshot.go @@ -12,7 +12,7 @@ func init() { bus.AddHandler("sql", CreateDashboardSnapshot) bus.AddHandler("sql", GetDashboardSnapshot) bus.AddHandler("sql", DeleteDashboardSnapshot) - bus.AddHandler("sql", SearchDashboardSnapshots) + bus.AddHandler("sql", SearchDashboardSnapshots) } func CreateDashboardSnapshot(cmd *m.CreateDashboardSnapshotCommand) error { @@ -67,16 +67,16 @@ func GetDashboardSnapshot(query *m.GetDashboardSnapshotQuery) error { } func SearchDashboardSnapshots(query *m.GetDashboardSnapshotsQuery) error { - var snapshots = make(m.DashboardSnapshots, 0) + var snapshots = make(m.DashboardSnapshots, 0) - sess := x.Limit(query.Limit) + sess := x.Limit(query.Limit) - if query.Name != "" { - sess.Where("name LIKE ?", query.Name) - } + if query.Name != "" { + sess.Where("name LIKE ?", query.Name) + } - sess.Where("org_id = ?", query.OrgId) - err := sess.Find(&snapshots) - query.Result = snapshots - return err + sess.Where("org_id = ?", query.OrgId) + err := sess.Find(&snapshots) + query.Result = snapshots + return err } From 6e99eed417acb9279feefdb4d2ea6cedd39116bf Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Tue, 19 Jan 2016 17:00:54 -0800 Subject: [PATCH 05/20] Moved snapshot route to core routes --- public/app/core/routes/all.js | 4 ++++ public/app/features/snapshot/all.js | 1 - .../app/features/snapshot/snapshot_routes.js | 18 ------------------ 3 files changed, 4 insertions(+), 19 deletions(-) delete mode 100644 public/app/features/snapshot/snapshot_routes.js diff --git a/public/app/core/routes/all.js b/public/app/core/routes/all.js index cc4d73ef708..ba993134455 100644 --- a/public/app/core/routes/all.js +++ b/public/app/core/routes/all.js @@ -132,6 +132,10 @@ define([ templateUrl: 'app/partials/reset_password.html', controller : 'ResetPasswordCtrl', }) + .when('/dashboard/snapshots', { + templateUrl: 'app/features/snapshot/partials/snapshots.html', + controller : 'SnapshotsCtrl' + }) .when('/apps', { templateUrl: 'app/features/apps/partials/list.html', controller: 'AppListCtrl', diff --git a/public/app/features/snapshot/all.js b/public/app/features/snapshot/all.js index 45cb9eb594f..b0f66edc414 100644 --- a/public/app/features/snapshot/all.js +++ b/public/app/features/snapshot/all.js @@ -1,4 +1,3 @@ define([ './snapshot_ctrl', - './snapshot_routes' ], function () {}); diff --git a/public/app/features/snapshot/snapshot_routes.js b/public/app/features/snapshot/snapshot_routes.js deleted file mode 100644 index 74de0634282..00000000000 --- a/public/app/features/snapshot/snapshot_routes.js +++ /dev/null @@ -1,18 +0,0 @@ -define([ - 'angular', - 'app/core/config', - 'lodash' -], -function (angular) { - 'use strict'; - - var module = angular.module('grafana.routes'); - - module.config(function($routeProvider) { - $routeProvider - .when('/dashboard/snapshots', { - templateUrl: 'app/features/snapshot/partials/snapshots.html', - controller : 'SnapshotsCtrl' - }); - }); -}); From e26cd21048b631235c98e141184b9986d0fc2458 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Tue, 19 Jan 2016 21:04:53 -0800 Subject: [PATCH 06/20] Converted ctrl to typescript --- public/app/core/routes/all.js | 3 +- public/app/features/snapshot/all.js | 3 -- public/app/features/snapshot/all.ts | 1 + .../features/snapshot/partials/snapshots.html | 6 +-- public/app/features/snapshot/snapshot_ctrl.js | 43 ------------------- public/app/features/snapshot/snapshot_ctrl.ts | 20 +++++++++ 6 files changed, 26 insertions(+), 50 deletions(-) delete mode 100644 public/app/features/snapshot/all.js create mode 100644 public/app/features/snapshot/all.ts delete mode 100644 public/app/features/snapshot/snapshot_ctrl.js create mode 100644 public/app/features/snapshot/snapshot_ctrl.ts diff --git a/public/app/core/routes/all.js b/public/app/core/routes/all.js index ba993134455..521e8008f98 100644 --- a/public/app/core/routes/all.js +++ b/public/app/core/routes/all.js @@ -134,7 +134,8 @@ define([ }) .when('/dashboard/snapshots', { templateUrl: 'app/features/snapshot/partials/snapshots.html', - controller : 'SnapshotsCtrl' + controller : 'SnapshotsCtrl', + controllerAs: 'ctrl', }) .when('/apps', { templateUrl: 'app/features/apps/partials/list.html', diff --git a/public/app/features/snapshot/all.js b/public/app/features/snapshot/all.js deleted file mode 100644 index b0f66edc414..00000000000 --- a/public/app/features/snapshot/all.js +++ /dev/null @@ -1,3 +0,0 @@ -define([ - './snapshot_ctrl', -], function () {}); 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 index 58b6c872617..9d1b688fd35 100644 --- a/public/app/features/snapshot/partials/snapshots.html +++ b/public/app/features/snapshot/partials/snapshots.html @@ -1,7 +1,7 @@
-
+

Available snapshots

@@ -14,7 +14,7 @@ - + {{snapshot.Name}} @@ -28,7 +28,7 @@ - + diff --git a/public/app/features/snapshot/snapshot_ctrl.js b/public/app/features/snapshot/snapshot_ctrl.js deleted file mode 100644 index 345638f4770..00000000000 --- a/public/app/features/snapshot/snapshot_ctrl.js +++ /dev/null @@ -1,43 +0,0 @@ -define([ - 'angular', - 'lodash' -], -function (angular, _) { - 'use strict'; - - var module = angular.module('grafana.controllers'); - - module.controller('SnapshotsCtrl', function($scope, $location, backendSrv) { - backendSrv.get('/api/dashboard/snapshots') - .then(function(result) { - $scope.snapshots = result; - }); - - $scope.removeSnapshotConfirmed = function(snapshot) { - _.remove($scope.snapshots, {Key: snapshot.Key}); - - backendSrv.get('/api/snapshots-delete/' + snapshot.DeleteKey) - .then(function() { - $scope.appEvent('alert-success', ['Snapshot deleted', '']); - }, function() { - $scope.appEvent('alert-error', ['Unable to delete snapshot', '']); - $scope.snapshots.push(snapshot); - }); - }; - - $scope.removeSnapshot = function(snapshot) { - - $scope.appEvent('confirm-modal', { - title: 'Confirm delete snapshot', - text: 'Are you sure you want to delete snapshot ' + snapshot.Name + '?', - yesText: "Delete", - icon: "fa-warning", - onConfirm: function() { - $scope.removeSnapshotConfirmed(snapshot); - } - }); - - }; - - }); -}); diff --git a/public/app/features/snapshot/snapshot_ctrl.ts b/public/app/features/snapshot/snapshot_ctrl.ts new file mode 100644 index 00000000000..b194fbf9119 --- /dev/null +++ b/public/app/features/snapshot/snapshot_ctrl.ts @@ -0,0 +1,20 @@ +/// + +import angular from 'angular'; +import _ from 'lodash'; + +export class SnapshotsCtrl { + snapshots: any[]; + + /** @ngInject */ + constructor(private backendSrv: any) {} + + init() { + this.backendSrv.get('/api/dashboard/snapshots').then(snapshots => { + this.snapshots = snapshots; + }); + console.log(this.snapshots); + } +} + +angular.module('grafana.controllers').controller('SnapshotsCtrl', SnapshotsCtrl); From 281ec60085ef1d659360a03083ea7d95cb4893b3 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Tue, 19 Jan 2016 21:52:13 -0800 Subject: [PATCH 07/20] UI and backend working --- pkg/api/dashboard_snapshot.go | 1 - .../features/snapshot/partials/snapshots.html | 6 +-- public/app/features/snapshot/snapshot_ctrl.ts | 37 +++++++++++++++---- 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/pkg/api/dashboard_snapshot.go b/pkg/api/dashboard_snapshot.go index 2d1fbce782c..99c8d4dd85f 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) } diff --git a/public/app/features/snapshot/partials/snapshots.html b/public/app/features/snapshot/partials/snapshots.html index 9d1b688fd35..58b6c872617 100644 --- a/public/app/features/snapshot/partials/snapshots.html +++ b/public/app/features/snapshot/partials/snapshots.html @@ -1,7 +1,7 @@
-
+

Available snapshots

@@ -14,7 +14,7 @@ - + {{snapshot.Name}} @@ -28,7 +28,7 @@ - + diff --git a/public/app/features/snapshot/snapshot_ctrl.ts b/public/app/features/snapshot/snapshot_ctrl.ts index b194fbf9119..60b09bf892e 100644 --- a/public/app/features/snapshot/snapshot_ctrl.ts +++ b/public/app/features/snapshot/snapshot_ctrl.ts @@ -4,16 +4,39 @@ import angular from 'angular'; import _ from 'lodash'; export class SnapshotsCtrl { - snapshots: any[]; /** @ngInject */ - constructor(private backendSrv: any) {} + constructor(backendSrv, $scope) { + $scope.init = function() { + backendSrv.get('/api/dashboard/snapshots').then(function(result) { + $scope.snapshots = result; + }); + }; - init() { - this.backendSrv.get('/api/dashboard/snapshots').then(snapshots => { - this.snapshots = snapshots; - }); - console.log(this.snapshots); + $scope.removeSnapshot = function(snapshot) { + $scope.appEvent('confirm-modal', { + title: 'Confirm delete snapshot', + text: 'Are you sure you want to delete snapshot ' + snapshot.Name + '?', + yesText: "Delete", + icon: "fa-warning", + onConfirm: function() { + $scope.removeSnapshotConfirmed(snapshot); + } + }); + }; + + $scope.removeSnapshotConfirmed = function(snapshot) { + _.remove($scope.snapshots, {Key: snapshot.Key}); + backendSrv.get('/api/snapshots-delete/' + snapshot.DeleteKey) + .then(function() { + $scope.appEvent('alert-success', ['Snapshot deleted', '']); + }, function() { + $scope.appEvent('alert-error', ['Unable to delete snapshot', '']); + $scope.snapshots.push(snapshot); + }); + }; + + $scope.init(); } } From aa1d28835dfd1ef114fa151e3c1b20930d46dfde Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Tue, 19 Jan 2016 23:23:44 -0800 Subject: [PATCH 08/20] Fixed angularjs variable names --- pkg/api/dashboard_snapshot.go | 20 ++++++++++++++++++- pkg/models/dashboard_snapshot.go | 16 +++++++++++++++ .../features/snapshot/partials/snapshots.html | 6 +++--- public/app/features/snapshot/snapshot_ctrl.ts | 6 +++--- 4 files changed, 41 insertions(+), 7 deletions(-) diff --git a/pkg/api/dashboard_snapshot.go b/pkg/api/dashboard_snapshot.go index 99c8d4dd85f..8ed848a7b11 100644 --- a/pkg/api/dashboard_snapshot.go +++ b/pkg/api/dashboard_snapshot.go @@ -118,5 +118,23 @@ func SearchDashboardSnapshots(c *middleware.Context) Response { return ApiError(500, "Search failed", err) } - return Json(200, searchQuery.Result) + 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/models/dashboard_snapshot.go b/pkg/models/dashboard_snapshot.go index 8c1a0f47a06..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 diff --git a/public/app/features/snapshot/partials/snapshots.html b/public/app/features/snapshot/partials/snapshots.html index 58b6c872617..e1fcf16f0ef 100644 --- a/public/app/features/snapshot/partials/snapshots.html +++ b/public/app/features/snapshot/partials/snapshots.html @@ -16,13 +16,13 @@ - {{snapshot.Name}} + {{snapshot.name}} - dashboard/snapshot/{{snapshot.Key}} + dashboard/snapshot/{{snapshot.key}} - + View diff --git a/public/app/features/snapshot/snapshot_ctrl.ts b/public/app/features/snapshot/snapshot_ctrl.ts index 60b09bf892e..5b27016f0aa 100644 --- a/public/app/features/snapshot/snapshot_ctrl.ts +++ b/public/app/features/snapshot/snapshot_ctrl.ts @@ -16,7 +16,7 @@ export class SnapshotsCtrl { $scope.removeSnapshot = function(snapshot) { $scope.appEvent('confirm-modal', { title: 'Confirm delete snapshot', - text: 'Are you sure you want to delete snapshot ' + snapshot.Name + '?', + text: 'Are you sure you want to delete snapshot ' + snapshot.name + '?', yesText: "Delete", icon: "fa-warning", onConfirm: function() { @@ -26,8 +26,8 @@ export class SnapshotsCtrl { }; $scope.removeSnapshotConfirmed = function(snapshot) { - _.remove($scope.snapshots, {Key: snapshot.Key}); - backendSrv.get('/api/snapshots-delete/' + snapshot.DeleteKey) + _.remove($scope.snapshots, {key: snapshot.key}); + backendSrv.get('/api/snapshots-delete/' + snapshot.deleteKey) .then(function() { $scope.appEvent('alert-success', ['Snapshot deleted', '']); }, function() { From a854f0c4d4a1392fd71c36d58ac206e8c9240b5a Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Wed, 20 Jan 2016 02:35:24 -0800 Subject: [PATCH 09/20] Switched snapshot ctrl to angularjs2 --- .../features/snapshot/partials/snapshots.html | 4 +- public/app/features/snapshot/snapshot_ctrl.ts | 63 +++++++++---------- 2 files changed, 33 insertions(+), 34 deletions(-) diff --git a/public/app/features/snapshot/partials/snapshots.html b/public/app/features/snapshot/partials/snapshots.html index e1fcf16f0ef..4a1d44d38a4 100644 --- a/public/app/features/snapshot/partials/snapshots.html +++ b/public/app/features/snapshot/partials/snapshots.html @@ -14,7 +14,7 @@ - + {{snapshot.name}} @@ -28,7 +28,7 @@ - + diff --git a/public/app/features/snapshot/snapshot_ctrl.ts b/public/app/features/snapshot/snapshot_ctrl.ts index 5b27016f0aa..7b38f9ad8eb 100644 --- a/public/app/features/snapshot/snapshot_ctrl.ts +++ b/public/app/features/snapshot/snapshot_ctrl.ts @@ -5,39 +5,38 @@ import _ from 'lodash'; export class SnapshotsCtrl { + snapshots: any; + /** @ngInject */ - constructor(backendSrv, $scope) { - $scope.init = function() { - backendSrv.get('/api/dashboard/snapshots').then(function(result) { - $scope.snapshots = result; - }); - }; - - $scope.removeSnapshot = function(snapshot) { - $scope.appEvent('confirm-modal', { - title: 'Confirm delete snapshot', - text: 'Are you sure you want to delete snapshot ' + snapshot.name + '?', - yesText: "Delete", - icon: "fa-warning", - onConfirm: function() { - $scope.removeSnapshotConfirmed(snapshot); - } - }); - }; - - $scope.removeSnapshotConfirmed = function(snapshot) { - _.remove($scope.snapshots, {key: snapshot.key}); - backendSrv.get('/api/snapshots-delete/' + snapshot.deleteKey) - .then(function() { - $scope.appEvent('alert-success', ['Snapshot deleted', '']); - }, function() { - $scope.appEvent('alert-error', ['Unable to delete snapshot', '']); - $scope.snapshots.push(snapshot); - }); - }; - - $scope.init(); + 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); From 753fd164d77030c57372ed7de60f3dfc3d3d3945 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Wed, 27 Jan 2016 16:33:32 -0800 Subject: [PATCH 10/20] Added createdBy in metadata ui and dashboard table --- pkg/api/dtos/models.go | 1 + pkg/models/dashboards.go | 14 +++++++++----- pkg/services/sqlstore/migrations/dashboard_mig.go | 5 +++++ .../app/features/dashboard/partials/settings.html | 11 +++++++++++ 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/pkg/api/dtos/models.go b/pkg/api/dtos/models.go index a5e8e74fb46..b93d36c0aee 100644 --- a/pkg/api/dtos/models.go +++ b/pkg/api/dtos/models.go @@ -42,6 +42,7 @@ type DashboardMeta struct { Created time.Time `json:"created"` Updated time.Time `json:"updated"` UpdatedBy string `json:"updatedBy"` + CreatedBy string `json:"createdBy"` } type DashboardFullWithMeta struct { diff --git a/pkg/models/dashboards.go b/pkg/models/dashboards.go index ddf5dd244f5..c9718dda295 100644 --- a/pkg/models/dashboards.go +++ b/pkg/models/dashboards.go @@ -34,6 +34,7 @@ type Dashboard struct { Updated time.Time UpdatedBy int64 + CreatedBy int64 Title string Data map[string]interface{} @@ -66,7 +67,7 @@ func (dash *Dashboard) GetTags() []string { return b } -func NewDashboardFromJson(data map[string]interface{}) *Dashboard { +func NewDashboardFromJson(data map[string]interface {}) *Dashboard { dash := &Dashboard{} dash.Data = data dash.Title = dash.Data["title"].(string) @@ -91,8 +92,11 @@ func NewDashboardFromJson(data map[string]interface{}) *Dashboard { // GetDashboardModel turns the command into the savable model func (cmd *SaveDashboardCommand) GetDashboardModel() *Dashboard { dash := NewDashboardFromJson(cmd.Dashboard) - dash.OrgId = cmd.OrgId - dash.UpdatedBy = cmd.UpdatedBy + if dash.Data["version"] == 0 { + dash.CreatedBy = cmd.UserId + } + dash.UpdatedBy = cmd.UserId + dash.OrgId = cmd.OrgId dash.UpdateSlug() return dash } @@ -114,9 +118,9 @@ func (dash *Dashboard) UpdateSlug() { type SaveDashboardCommand struct { Dashboard map[string]interface{} `json:"dashboard" binding:"Required"` - Overwrite bool `json:"overwrite"` + UserId int64 `json:"userId"` OrgId int64 `json:"-"` - UpdatedBy int64 `json:"-"` + Overwrite bool `json:"overwrite"` Result *Dashboard } diff --git a/pkg/services/sqlstore/migrations/dashboard_mig.go b/pkg/services/sqlstore/migrations/dashboard_mig.go index b94f1a7d6ac..43fb8e36cb2 100644 --- a/pkg/services/sqlstore/migrations/dashboard_mig.go +++ b/pkg/services/sqlstore/migrations/dashboard_mig.go @@ -97,4 +97,9 @@ func addDashboardMigration(mg *Migrator) { mg.AddMigration("Add column updated_by in dashboard - v2", NewAddColumnMigration(dashboardV2, &Column{ Name: "updated_by", Type: DB_Int, Nullable: true, })) + + // add column to store creator of a dashboard + mg.AddMigration("Add column created_by in dashboard - v2", NewAddColumnMigration(dashboardV2, &Column{ + Name: "created_by", Type: DB_Int, Nullable: true, + })) } diff --git a/public/app/features/dashboard/partials/settings.html b/public/app/features/dashboard/partials/settings.html index 730d2cadeb8..d1a02924f0b 100644 --- a/public/app/features/dashboard/partials/settings.html +++ b/public/app/features/dashboard/partials/settings.html @@ -151,6 +151,17 @@
+
+
    +
  • + Created by: +
  • +
  • + {{dashboardMeta.createdBy}} +
  • +
+
+
From 6a1192172dd75c9442070439ca48f2e185d2d9e6 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Wed, 27 Jan 2016 21:55:54 -0800 Subject: [PATCH 11/20] Api stores dashboard creator --- pkg/api/dashboard.go | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index 79485fa8cca..5effc1d06d7 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -49,18 +49,14 @@ func GetDashboard(c *middleware.Context) { dash := query.Result - // Finding the last updater of the dashboard - updater := "Anonymous" - if dash.UpdatedBy != 0 { - userQuery := m.GetUserByIdQuery{Id: dash.UpdatedBy} - userErr := bus.Dispatch(&userQuery) - if userErr != nil { - updater = "Unknown" - } else { - user := userQuery.Result - updater = user.Login - } - } + // Finding the last creator and updater of the dashboard + updater, creator := "Anonymous", "Anonymous" + if dash.UpdatedBy > 0 { + updater = getUserLogin(dash.UpdatedBy) + } + if dash.CreatedBy > 0 { + creator = getUserLogin(dash.CreatedBy) + } dto := dtos.DashboardFullWithMeta{ Dashboard: dash.Data, @@ -74,12 +70,24 @@ func GetDashboard(c *middleware.Context) { Created: dash.Created, Updated: dash.Updated, UpdatedBy: updater, + CreatedBy: creator, }, } c.JSON(200, dto) } +func getUserLogin(userId int64) string { + query := m.GetUserByIdQuery{Id: userId} + err := bus.Dispatch(&query) + if err != nil { + return "Anonymous" + } else { + user := query.Result + return user.Login + } +} + func DeleteDashboard(c *middleware.Context) { slug := c.Params(":slug") @@ -104,10 +112,10 @@ func PostDashboard(c *middleware.Context, cmd m.SaveDashboardCommand) { cmd.OrgId = c.OrgId if !c.IsSignedIn { - cmd.UpdatedBy = 0 + cmd.UserId = -1 } else { - cmd.UpdatedBy = c.UserId - } + cmd.UserId = c.UserId + } dash := cmd.GetDashboardModel() if dash.Id == 0 { From 58121d89fc876f43041e1d200ff68f19d6ee1528 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Wed, 27 Jan 2016 22:00:24 -0800 Subject: [PATCH 12/20] Updated http_api docs --- docs/sources/reference/http_api.md | 6 ++-- pkg/api/dashboard.go | 32 +++++++++---------- pkg/api/dtos/models.go | 2 +- pkg/models/dashboards.go | 16 +++++----- .../sqlstore/migrations/dashboard_mig.go | 8 ++--- 5 files changed, 33 insertions(+), 31 deletions(-) diff --git a/docs/sources/reference/http_api.md b/docs/sources/reference/http_api.md index 4b0dff6c4b6..5ffd03c4111 100644 --- a/docs/sources/reference/http_api.md +++ b/docs/sources/reference/http_api.md @@ -70,13 +70,15 @@ Creates a new dashboard or updates an existing dashboard. "schemaVersion": 6, "version": 0 }, - "overwrite": false + "overwrite": false, + "userId:": 3 } JSON Body schema: -- **dashboard** – The complete dashboard model, id = null to create a new dashboard +- **dashboard** – The complete dashboard model, id = null to create a new dashboard. - **overwrite** – Set to true if you want to overwrite existing dashboard with newer version or with same dashboard title. +- **userId** - Set userId if you want to record who created or updated a dashboard. **Example Response**: diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index 5effc1d06d7..8bcb7fc6b74 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -52,11 +52,11 @@ func GetDashboard(c *middleware.Context) { // Finding the last creator and updater of the dashboard updater, creator := "Anonymous", "Anonymous" if dash.UpdatedBy > 0 { - updater = getUserLogin(dash.UpdatedBy) - } - if dash.CreatedBy > 0 { - creator = getUserLogin(dash.CreatedBy) - } + updater = getUserLogin(dash.UpdatedBy) + } + if dash.CreatedBy > 0 { + creator = getUserLogin(dash.CreatedBy) + } dto := dtos.DashboardFullWithMeta{ Dashboard: dash.Data, @@ -70,7 +70,7 @@ func GetDashboard(c *middleware.Context) { Created: dash.Created, Updated: dash.Updated, UpdatedBy: updater, - CreatedBy: creator, + CreatedBy: creator, }, } @@ -78,14 +78,14 @@ func GetDashboard(c *middleware.Context) { } func getUserLogin(userId int64) string { - query := m.GetUserByIdQuery{Id: userId} - err := bus.Dispatch(&query) - if err != nil { - return "Anonymous" - } else { - user := query.Result - return user.Login - } + query := m.GetUserByIdQuery{Id: userId} + err := bus.Dispatch(&query) + if err != nil { + return "Anonymous" + } else { + user := query.Result + return user.Login + } } func DeleteDashboard(c *middleware.Context) { @@ -114,8 +114,8 @@ func PostDashboard(c *middleware.Context, cmd m.SaveDashboardCommand) { if !c.IsSignedIn { cmd.UserId = -1 } else { - cmd.UserId = c.UserId - } + cmd.UserId = c.UserId + } dash := cmd.GetDashboardModel() if dash.Id == 0 { diff --git a/pkg/api/dtos/models.go b/pkg/api/dtos/models.go index b93d36c0aee..b35b5c472b6 100644 --- a/pkg/api/dtos/models.go +++ b/pkg/api/dtos/models.go @@ -42,7 +42,7 @@ type DashboardMeta struct { Created time.Time `json:"created"` Updated time.Time `json:"updated"` UpdatedBy string `json:"updatedBy"` - CreatedBy string `json:"createdBy"` + CreatedBy string `json:"createdBy"` } type DashboardFullWithMeta struct { diff --git a/pkg/models/dashboards.go b/pkg/models/dashboards.go index c9718dda295..a85e98a8b91 100644 --- a/pkg/models/dashboards.go +++ b/pkg/models/dashboards.go @@ -34,7 +34,7 @@ type Dashboard struct { Updated time.Time UpdatedBy int64 - CreatedBy int64 + CreatedBy int64 Title string Data map[string]interface{} @@ -67,7 +67,7 @@ func (dash *Dashboard) GetTags() []string { return b } -func NewDashboardFromJson(data map[string]interface {}) *Dashboard { +func NewDashboardFromJson(data map[string]interface{}) *Dashboard { dash := &Dashboard{} dash.Data = data dash.Title = dash.Data["title"].(string) @@ -93,10 +93,10 @@ func NewDashboardFromJson(data map[string]interface {}) *Dashboard { func (cmd *SaveDashboardCommand) GetDashboardModel() *Dashboard { dash := NewDashboardFromJson(cmd.Dashboard) if dash.Data["version"] == 0 { - dash.CreatedBy = cmd.UserId - } - dash.UpdatedBy = cmd.UserId - dash.OrgId = cmd.OrgId + dash.CreatedBy = cmd.UserId + } + dash.UpdatedBy = cmd.UserId + dash.OrgId = cmd.OrgId dash.UpdateSlug() return dash } @@ -118,9 +118,9 @@ func (dash *Dashboard) UpdateSlug() { type SaveDashboardCommand struct { Dashboard map[string]interface{} `json:"dashboard" binding:"Required"` - UserId int64 `json:"userId"` + UserId int64 `json:"userId"` OrgId int64 `json:"-"` - Overwrite bool `json:"overwrite"` + Overwrite bool `json:"overwrite"` Result *Dashboard } diff --git a/pkg/services/sqlstore/migrations/dashboard_mig.go b/pkg/services/sqlstore/migrations/dashboard_mig.go index 43fb8e36cb2..a4a8629b331 100644 --- a/pkg/services/sqlstore/migrations/dashboard_mig.go +++ b/pkg/services/sqlstore/migrations/dashboard_mig.go @@ -98,8 +98,8 @@ func addDashboardMigration(mg *Migrator) { Name: "updated_by", Type: DB_Int, Nullable: true, })) - // add column to store creator of a dashboard - mg.AddMigration("Add column created_by in dashboard - v2", NewAddColumnMigration(dashboardV2, &Column{ - Name: "created_by", Type: DB_Int, Nullable: true, - })) + // add column to store creator of a dashboard + mg.AddMigration("Add column created_by in dashboard - v2", NewAddColumnMigration(dashboardV2, &Column{ + Name: "created_by", Type: DB_Int, Nullable: true, + })) } From b30dcce4bc6c4557ee8702902b702674d1666c17 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Thu, 28 Jan 2016 00:52:52 -0800 Subject: [PATCH 13/20] Added dtos and UI for more metadata --- pkg/api/dtos/models.go | 30 ++++---- .../features/dashboard/partials/settings.html | 76 +++++++++++++++---- 2 files changed, 78 insertions(+), 28 deletions(-) diff --git a/pkg/api/dtos/models.go b/pkg/api/dtos/models.go index b35b5c472b6..4617f89d3c9 100644 --- a/pkg/api/dtos/models.go +++ b/pkg/api/dtos/models.go @@ -30,19 +30,23 @@ type CurrentUser struct { } type DashboardMeta struct { - IsStarred bool `json:"isStarred,omitempty"` - IsHome bool `json:"isHome,omitempty"` - IsSnapshot bool `json:"isSnapshot,omitempty"` - Type string `json:"type,omitempty"` - CanSave bool `json:"canSave"` - CanEdit bool `json:"canEdit"` - CanStar bool `json:"canStar"` - Slug string `json:"slug"` - Expires time.Time `json:"expires"` - Created time.Time `json:"created"` - Updated time.Time `json:"updated"` - UpdatedBy string `json:"updatedBy"` - CreatedBy string `json:"createdBy"` + IsStarred bool `json:"isStarred,omitempty"` + IsHome bool `json:"isHome,omitempty"` + IsSnapshot bool `json:"isSnapshot,omitempty"` + Type string `json:"type,omitempty"` + CanSave bool `json:"canSave"` + CanEdit bool `json:"canEdit"` + CanStar bool `json:"canStar"` + Slug string `json:"slug"` + Expires time.Time `json:"expires"` + Created time.Time `json:"created"` + Updated time.Time `json:"updated"` + UpdatedBy string `json:"updatedBy"` + CreatedBy string `json:"createdBy"` + TotalRows int64 `json:"totalRows"` + TotalPanels int64 `json:"totalPanels"` + TotalQueries int64 `json:"totalQueries"` + Version int `json:"version"` } type DashboardFullWithMeta struct { diff --git a/public/app/features/dashboard/partials/settings.html b/public/app/features/dashboard/partials/settings.html index d1a02924f0b..579f3717ebc 100644 --- a/public/app/features/dashboard/partials/settings.html +++ b/public/app/features/dashboard/partials/settings.html @@ -115,9 +115,9 @@
-
-
-
Dashboard info
+
+
Dashboard info
+
  • @@ -130,17 +130,6 @@
-
    -
  • - Created at: -
  • -
  • - {{formatDate(dashboardMeta.created)}} -
  • -
-
-
-
  • Last updated by: @@ -150,6 +139,17 @@
+
+
+
    +
  • + Created at: +
  • +
  • + {{formatDate(dashboardMeta.created)}} +
  • +
+
    @@ -158,7 +158,53 @@
  • {{dashboardMeta.createdBy}} -
  • + +
+
+
+
+
+
+
    +
  • + Total rows: +
  • +
  • + {{dashboardMeta.totalRows}} +
  • +
+
+
+
+
    +
  • + Total panels: +
  • +
  • + {{dashboardMeta.totalPanels}} +
  • +
+
+
+
+
    +
  • + Total queries: +
  • +
  • + {{dashboardMeta.totalQueries}} +
  • +
+
+
+
+
    +
  • + Version: +
  • +
  • + {{dashboardMeta.version}} +
From 972ac99b7cccc56540597b5ada8524dabe284765 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Thu, 28 Jan 2016 09:53:19 -0800 Subject: [PATCH 14/20] Added more metadata --- docs/sources/reference/http_api.md | 4 +-- pkg/api/dashboard.go | 49 +++++++++++++++++++++++------- pkg/api/dtos/models.go | 8 ++--- 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/docs/sources/reference/http_api.md b/docs/sources/reference/http_api.md index 5ffd03c4111..70431565741 100644 --- a/docs/sources/reference/http_api.md +++ b/docs/sources/reference/http_api.md @@ -70,15 +70,13 @@ Creates a new dashboard or updates an existing dashboard. "schemaVersion": 6, "version": 0 }, - "overwrite": false, - "userId:": 3 + "overwrite": false } JSON Body schema: - **dashboard** – The complete dashboard model, id = null to create a new dashboard. - **overwrite** – Set to true if you want to overwrite existing dashboard with newer version or with same dashboard title. -- **userId** - Set userId if you want to record who created or updated a dashboard. **Example Response**: diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index 8bcb7fc6b74..cc635a108e3 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -49,7 +49,7 @@ func GetDashboard(c *middleware.Context) { dash := query.Result - // Finding the last creator and updater of the dashboard + // Finding creator and last updater of the dashboard updater, creator := "Anonymous", "Anonymous" if dash.UpdatedBy > 0 { updater = getUserLogin(dash.UpdatedBy) @@ -58,19 +58,26 @@ func GetDashboard(c *middleware.Context) { creator = getUserLogin(dash.CreatedBy) } + // Finding total panels and queries on the dashboard + totalRows, totalPanels, totalQueries := getTotalRowsPanelsAndQueries(dash.Data) + dto := dtos.DashboardFullWithMeta{ Dashboard: dash.Data, Meta: dtos.DashboardMeta{ - IsStarred: isStarred, - Slug: slug, - Type: m.DashTypeDB, - CanStar: c.IsSignedIn, - CanSave: c.OrgRole == m.ROLE_ADMIN || c.OrgRole == m.ROLE_EDITOR, - CanEdit: canEditDashboard(c.OrgRole), - Created: dash.Created, - Updated: dash.Updated, - UpdatedBy: updater, - CreatedBy: creator, + IsStarred: isStarred, + Slug: slug, + Type: m.DashTypeDB, + CanStar: c.IsSignedIn, + CanSave: c.OrgRole == m.ROLE_ADMIN || c.OrgRole == m.ROLE_EDITOR, + CanEdit: canEditDashboard(c.OrgRole), + Created: dash.Created, + Updated: dash.Updated, + UpdatedBy: updater, + CreatedBy: creator, + TotalRows: totalRows, + TotalPanels: totalPanels, + TotalQueries: totalQueries, + Version: dash.Version, }, } @@ -88,6 +95,26 @@ func getUserLogin(userId int64) string { } } +func getTotalRowsPanelsAndQueries(data map[string]interface{}) (int, int, int) { + totalRows, totalPanels, totalQueries := 0, 0, 0 + if rows, rowsOk := data["rows"]; rowsOk { + totalRows = len(rows.([]interface{})) + if totalRows > 0 { + for _, rowElement := range rows.([]interface{}) { + if panels, panelsOk := rowElement.(map[string]interface{})["panels"]; panelsOk { + totalPanels += len(panels.([]interface{})) + for _, panelElement := range panels.([]interface{}) { + if targets, targetsOk := panelElement.(map[string]interface{})["targets"]; targetsOk { + totalQueries += len(targets.([]interface{})) + } + } + } + } + } + } + return totalRows, totalPanels, totalQueries +} + func DeleteDashboard(c *middleware.Context) { slug := c.Params(":slug") diff --git a/pkg/api/dtos/models.go b/pkg/api/dtos/models.go index 4617f89d3c9..5fe23b4a53e 100644 --- a/pkg/api/dtos/models.go +++ b/pkg/api/dtos/models.go @@ -43,10 +43,10 @@ type DashboardMeta struct { Updated time.Time `json:"updated"` UpdatedBy string `json:"updatedBy"` CreatedBy string `json:"createdBy"` - TotalRows int64 `json:"totalRows"` - TotalPanels int64 `json:"totalPanels"` - TotalQueries int64 `json:"totalQueries"` - Version int `json:"version"` + TotalRows int `json:"totalRows"` + TotalPanels int `json:"totalPanels"` + TotalQueries int `json:"totalQueries"` + Version int `json:"version"` } type DashboardFullWithMeta struct { From 00a6efa15eb73f4fe5679506e830ebbdf70147a7 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Mon, 1 Feb 2016 23:26:11 -0800 Subject: [PATCH 15/20] Excluded total calculations from backend --- pkg/api/dashboard.go | 48 +++++-------------- pkg/api/dtos/models.go | 31 ++++++------ .../features/dashboard/partials/settings.html | 35 +------------- 3 files changed, 26 insertions(+), 88 deletions(-) diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index cc635a108e3..c0c7cd0e4f5 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -58,26 +58,20 @@ func GetDashboard(c *middleware.Context) { creator = getUserLogin(dash.CreatedBy) } - // Finding total panels and queries on the dashboard - totalRows, totalPanels, totalQueries := getTotalRowsPanelsAndQueries(dash.Data) - dto := dtos.DashboardFullWithMeta{ Dashboard: dash.Data, Meta: dtos.DashboardMeta{ - IsStarred: isStarred, - Slug: slug, - Type: m.DashTypeDB, - CanStar: c.IsSignedIn, - CanSave: c.OrgRole == m.ROLE_ADMIN || c.OrgRole == m.ROLE_EDITOR, - CanEdit: canEditDashboard(c.OrgRole), - Created: dash.Created, - Updated: dash.Updated, - UpdatedBy: updater, - CreatedBy: creator, - TotalRows: totalRows, - TotalPanels: totalPanels, - TotalQueries: totalQueries, - Version: dash.Version, + IsStarred: isStarred, + Slug: slug, + Type: m.DashTypeDB, + CanStar: c.IsSignedIn, + CanSave: c.OrgRole == m.ROLE_ADMIN || c.OrgRole == m.ROLE_EDITOR, + CanEdit: canEditDashboard(c.OrgRole), + Created: dash.Created, + Updated: dash.Updated, + UpdatedBy: updater, + CreatedBy: creator, + Version: dash.Version, }, } @@ -95,26 +89,6 @@ func getUserLogin(userId int64) string { } } -func getTotalRowsPanelsAndQueries(data map[string]interface{}) (int, int, int) { - totalRows, totalPanels, totalQueries := 0, 0, 0 - if rows, rowsOk := data["rows"]; rowsOk { - totalRows = len(rows.([]interface{})) - if totalRows > 0 { - for _, rowElement := range rows.([]interface{}) { - if panels, panelsOk := rowElement.(map[string]interface{})["panels"]; panelsOk { - totalPanels += len(panels.([]interface{})) - for _, panelElement := range panels.([]interface{}) { - if targets, targetsOk := panelElement.(map[string]interface{})["targets"]; targetsOk { - totalQueries += len(targets.([]interface{})) - } - } - } - } - } - } - return totalRows, totalPanels, totalQueries -} - func DeleteDashboard(c *middleware.Context) { slug := c.Params(":slug") diff --git a/pkg/api/dtos/models.go b/pkg/api/dtos/models.go index 5fe23b4a53e..83176e836e5 100644 --- a/pkg/api/dtos/models.go +++ b/pkg/api/dtos/models.go @@ -30,23 +30,20 @@ type CurrentUser struct { } type DashboardMeta struct { - IsStarred bool `json:"isStarred,omitempty"` - IsHome bool `json:"isHome,omitempty"` - IsSnapshot bool `json:"isSnapshot,omitempty"` - Type string `json:"type,omitempty"` - CanSave bool `json:"canSave"` - CanEdit bool `json:"canEdit"` - CanStar bool `json:"canStar"` - Slug string `json:"slug"` - Expires time.Time `json:"expires"` - Created time.Time `json:"created"` - Updated time.Time `json:"updated"` - UpdatedBy string `json:"updatedBy"` - CreatedBy string `json:"createdBy"` - TotalRows int `json:"totalRows"` - TotalPanels int `json:"totalPanels"` - TotalQueries int `json:"totalQueries"` - Version int `json:"version"` + IsStarred bool `json:"isStarred,omitempty"` + IsHome bool `json:"isHome,omitempty"` + IsSnapshot bool `json:"isSnapshot,omitempty"` + Type string `json:"type,omitempty"` + CanSave bool `json:"canSave"` + CanEdit bool `json:"canEdit"` + CanStar bool `json:"canStar"` + Slug string `json:"slug"` + Expires time.Time `json:"expires"` + Created time.Time `json:"created"` + Updated time.Time `json:"updated"` + UpdatedBy string `json:"updatedBy"` + CreatedBy string `json:"createdBy"` + Version int `json:"version"` } type DashboardFullWithMeta struct { diff --git a/public/app/features/dashboard/partials/settings.html b/public/app/features/dashboard/partials/settings.html index e5b42bbdfdd..939a23a2d1e 100644 --- a/public/app/features/dashboard/partials/settings.html +++ b/public/app/features/dashboard/partials/settings.html @@ -167,40 +167,7 @@
  • - Total rows: -
  • -
  • - {{dashboardMeta.totalRows}} -
  • -
-
-
-
-
    -
  • - Total panels: -
  • -
  • - {{dashboardMeta.totalPanels}} -
  • -
-
-
-
-
    -
  • - Total queries: -
  • -
  • - {{dashboardMeta.totalQueries}} -
  • -
-
-
-
-
    -
  • - Version: + Current version:
  • {{dashboardMeta.version}} From c6ab63ec479cd263d59d03b4b646e8019d5516cb Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Tue, 2 Feb 2016 11:50:01 -0800 Subject: [PATCH 16/20] Calculated total stats in frontend --- .../app/features/dashboard/dashboardCtrl.js | 20 +++++++++++ .../features/dashboard/partials/settings.html | 33 +++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/public/app/features/dashboard/dashboardCtrl.js b/public/app/features/dashboard/dashboardCtrl.js index ea0d91019b8..cc305e4aed4 100644 --- a/public/app/features/dashboard/dashboardCtrl.js +++ b/public/app/features/dashboard/dashboardCtrl.js @@ -57,6 +57,7 @@ function (angular, $, config, moment) { dashboardKeybindings.shortcuts($scope); + $scope.evaluateTotalStats(); $scope.updateSubmenuVisibility(); $scope.setWindowTitleAndTheme(); @@ -67,6 +68,25 @@ function (angular, $, config, moment) { }); }; + $scope.evaluateTotalStats = function() { + $scope.dashboard.totalRows = 0; + $scope.dashboard.totalPanels = 0; + $scope.dashboard.totalQueries = 0; + if ($scope.dashboard.rows) { + $scope.dashboard.totalRows = $scope.dashboard.rows.length; + for (var i=0; i<$scope.dashboard.rows.length; i++) { + if ($scope.dashboard.rows[i].panels) { + $scope.dashboard.totalPanels += $scope.dashboard.rows[i].panels.length; + for (var j=0; j<$scope.dashboard.rows[i].panels.length; j++) { + if ($scope.dashboard.rows[i].panels[j].targets) { + $scope.dashboard.totalQueries += $scope.dashboard.rows[i].panels[j].targets.length; + } + } + } + } + } + }; + $scope.updateSubmenuVisibility = function() { $scope.submenuEnabled = $scope.dashboard.isSubmenuFeaturesEnabled(); }; diff --git a/public/app/features/dashboard/partials/settings.html b/public/app/features/dashboard/partials/settings.html index 939a23a2d1e..d27d3322692 100644 --- a/public/app/features/dashboard/partials/settings.html +++ b/public/app/features/dashboard/partials/settings.html @@ -164,6 +164,39 @@
+
+
    +
  • + Total Rows: +
  • +
  • + {{dashboard.totalRows}} +
  • +
+
+
+
+
    +
  • + Total Panels: +
  • +
  • + {{dashboard.totalPanels}} +
  • +
+
+
+
+
    +
  • + Total Queries: +
  • +
  • + {{dashboard.totalQueries}} +
  • +
+
+
  • From 73ec4864343036b16d4aaf5ba448c0314ae56eb3 Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Thu, 14 Jan 2016 19:02:04 +0900 Subject: [PATCH 17/20] (cloudwatch) custom metrics support --- pkg/api/cloudwatch/metrics.go | 175 +++++++++++++++++- .../datasource/cloudwatch/datasource.js | 14 +- .../cloudwatch/query_parameter_ctrl.js | 4 +- 3 files changed, 177 insertions(+), 16 deletions(-) diff --git a/pkg/api/cloudwatch/metrics.go b/pkg/api/cloudwatch/metrics.go index 7f8b30bf312..4be6b65ee2b 100644 --- a/pkg/api/cloudwatch/metrics.go +++ b/pkg/api/cloudwatch/metrics.go @@ -3,7 +3,14 @@ package cloudwatch import ( "encoding/json" "sort" + "strings" + "sync" + "time" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awsutil" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/grafana/grafana/pkg/middleware" "github.com/grafana/grafana/pkg/util" ) @@ -11,6 +18,14 @@ import ( var metricsMap map[string][]string var dimensionsMap map[string][]string +type CustomMetricsCache struct { + Expire time.Time + Cache []string +} + +var customMetricsMetricsMap map[string]map[string]map[string]*CustomMetricsCache +var customMetricsDimensionsMap map[string]map[string]map[string]*CustomMetricsCache + func init() { metricsMap = map[string][]string{ "AWS/AutoScaling": {"GroupMinSize", "GroupMaxSize", "GroupDesiredCapacity", "GroupInServiceInstances", "GroupPendingInstances", "GroupStandbyInstances", "GroupTerminatingInstances", "GroupTotalInstances"}, @@ -85,6 +100,9 @@ func init() { "AWS/WAF": {"Rule", "WebACL"}, "AWS/WorkSpaces": {"DirectoryId", "WorkspaceId"}, } + + customMetricsMetricsMap = make(map[string]map[string]map[string]*CustomMetricsCache) + customMetricsDimensionsMap = make(map[string]map[string]map[string]*CustomMetricsCache) } // Whenever this list is updated, frontend list should also be updated. @@ -127,10 +145,19 @@ func handleGetMetrics(req *cwRequest, c *middleware.Context) { json.Unmarshal(req.Body, reqParam) - namespaceMetrics, exists := metricsMap[reqParam.Parameters.Namespace] - if !exists { - c.JsonApiErr(404, "Unable to find namespace "+reqParam.Parameters.Namespace, nil) - return + var namespaceMetrics []string + if !isCustomMetrics(reqParam.Parameters.Namespace) { + var exists bool + if namespaceMetrics, exists = metricsMap[reqParam.Parameters.Namespace]; !exists { + c.JsonApiErr(404, "Unable to find namespace "+reqParam.Parameters.Namespace, nil) + return + } + } else { + var err error + if namespaceMetrics, err = getMetricsForCustomMetrics(req, reqParam.Parameters.Namespace); err != nil { + c.JsonApiErr(500, "Unable to call AWS API", err) + return + } } sort.Sort(sort.StringSlice(namespaceMetrics)) @@ -151,10 +178,19 @@ func handleGetDimensions(req *cwRequest, c *middleware.Context) { json.Unmarshal(req.Body, reqParam) - dimensionValues, exists := dimensionsMap[reqParam.Parameters.Namespace] - if !exists { - c.JsonApiErr(404, "Unable to find dimension "+reqParam.Parameters.Namespace, nil) - return + var dimensionValues []string + if !isCustomMetrics(reqParam.Parameters.Namespace) { + var exists bool + if dimensionValues, exists = dimensionsMap[reqParam.Parameters.Namespace]; !exists { + c.JsonApiErr(404, "Unable to find dimension "+reqParam.Parameters.Namespace, nil) + return + } + } else { + var err error + if dimensionValues, err = getDimensionsForCustomMetrics(req, reqParam.Parameters.Namespace); err != nil { + c.JsonApiErr(500, "Unable to call AWS API", err) + return + } } sort.Sort(sort.StringSlice(dimensionValues)) @@ -165,3 +201,126 @@ func handleGetDimensions(req *cwRequest, c *middleware.Context) { c.JSON(200, result) } + +func getAllMetrics(req *cwRequest, namespace string) (cloudwatch.ListMetricsOutput, error) { + cfg := &aws.Config{ + Region: aws.String(req.Region), + Credentials: getCredentials(req.DataSource.Database), + } + + svc := cloudwatch.New(session.New(cfg), cfg) + + params := &cloudwatch.ListMetricsInput{ + Namespace: aws.String(namespace), + } + + var resp cloudwatch.ListMetricsOutput + err := svc.ListMetricsPages(params, + func(page *cloudwatch.ListMetricsOutput, lastPage bool) bool { + metrics, _ := awsutil.ValuesAtPath(page, "Metrics") + for _, metric := range metrics { + resp.Metrics = append(resp.Metrics, metric.(*cloudwatch.Metric)) + } + return !lastPage + }) + if err != nil { + return resp, err + } + + return resp, nil +} + +var metricsCacheLock sync.Mutex + +func getMetricsForCustomMetrics(req *cwRequest, namespace string) ([]string, error) { + result, err := getAllMetrics(req, namespace) + if err != nil { + return []string{}, err + } + + metricsCacheLock.Lock() + defer metricsCacheLock.Unlock() + + database := req.DataSource.Database + region := req.Region + if _, ok := customMetricsMetricsMap[database]; !ok { + customMetricsMetricsMap[database] = make(map[string]map[string]*CustomMetricsCache) + } + if _, ok := customMetricsMetricsMap[database][region]; !ok { + customMetricsMetricsMap[database][region] = make(map[string]*CustomMetricsCache) + } + if _, ok := customMetricsMetricsMap[database][region][namespace]; !ok { + customMetricsMetricsMap[database][region][namespace] = &CustomMetricsCache{} + customMetricsMetricsMap[database][region][namespace].Cache = make([]string, 0) + } + + if customMetricsMetricsMap[database][region][namespace].Expire.After(time.Now()) { + return customMetricsMetricsMap[database][region][namespace].Cache, nil + } + customMetricsMetricsMap[database][region][namespace].Cache = make([]string, 0) + customMetricsMetricsMap[database][region][namespace].Expire = time.Now().Add(5 * time.Minute) + + for _, metric := range result.Metrics { + if isDuplicate(customMetricsMetricsMap[database][region][namespace].Cache, *metric.MetricName) { + continue + } + customMetricsMetricsMap[database][region][namespace].Cache = append(customMetricsMetricsMap[database][region][namespace].Cache, *metric.MetricName) + } + + return customMetricsMetricsMap[database][region][namespace].Cache, nil +} + +var dimensionsCacheLock sync.Mutex + +func getDimensionsForCustomMetrics(req *cwRequest, namespace string) ([]string, error) { + result, err := getAllMetrics(req, namespace) + if err != nil { + return []string{}, err + } + + dimensionsCacheLock.Lock() + defer dimensionsCacheLock.Unlock() + + database := req.DataSource.Database + region := req.Region + if _, ok := customMetricsDimensionsMap[database]; !ok { + customMetricsDimensionsMap[database] = make(map[string]map[string]*CustomMetricsCache) + } + if _, ok := customMetricsDimensionsMap[database][region]; !ok { + customMetricsDimensionsMap[database][region] = make(map[string]*CustomMetricsCache) + } + if _, ok := customMetricsDimensionsMap[database][region][namespace]; !ok { + customMetricsDimensionsMap[database][region][namespace] = &CustomMetricsCache{} + customMetricsDimensionsMap[database][region][namespace].Cache = make([]string, 0) + } + + if customMetricsDimensionsMap[database][region][namespace].Expire.After(time.Now()) { + return customMetricsDimensionsMap[database][region][namespace].Cache, nil + } + customMetricsDimensionsMap[database][region][namespace].Cache = make([]string, 0) + customMetricsDimensionsMap[database][region][namespace].Expire = time.Now().Add(5 * time.Minute) + + for _, metric := range result.Metrics { + for _, dimension := range metric.Dimensions { + if isDuplicate(customMetricsDimensionsMap[database][region][namespace].Cache, *dimension.Name) { + continue + } + customMetricsDimensionsMap[database][region][namespace].Cache = append(customMetricsDimensionsMap[database][region][namespace].Cache, *dimension.Name) + } + } + + return customMetricsDimensionsMap[database][region][namespace].Cache, nil +} + +func isDuplicate(nameList []string, target string) bool { + for _, name := range nameList { + if name == target { + return true + } + } + return false +} + +func isCustomMetrics(namespace string) bool { + return strings.Index(namespace, "AWS/") != 0 +} diff --git a/public/app/plugins/datasource/cloudwatch/datasource.js b/public/app/plugins/datasource/cloudwatch/datasource.js index 119c4a83167..c77e9f95bd6 100644 --- a/public/app/plugins/datasource/cloudwatch/datasource.js +++ b/public/app/plugins/datasource/cloudwatch/datasource.js @@ -90,18 +90,20 @@ function (angular, _, moment, dateMath) { return this.awsRequest({action: '__GetNamespaces'}); }; - this.getMetrics = function(namespace) { + this.getMetrics = function(namespace, region) { return this.awsRequest({ action: '__GetMetrics', + region: region, parameters: { namespace: templateSrv.replace(namespace) } }); }; - this.getDimensionKeys = function(namespace) { + this.getDimensionKeys = function(namespace, region) { return this.awsRequest({ action: '__GetDimensions', + region: region, parameters: { namespace: templateSrv.replace(namespace) } @@ -164,14 +166,14 @@ function (angular, _, moment, dateMath) { return this.getNamespaces(); } - var metricNameQuery = query.match(/^metrics\(([^\)]+?)\)/); + var metricNameQuery = query.match(/^metrics\(([^\)]+?)(,\s?([^,]+?))?\)/); if (metricNameQuery) { - return this.getMetrics(metricNameQuery[1]); + return this.getMetrics(metricNameQuery[1], metricNameQuery[3]); } - var dimensionKeysQuery = query.match(/^dimension_keys\(([^\)]+?)\)/); + var dimensionKeysQuery = query.match(/^dimension_keys\(([^\)]+?)(,\s?([^,]+?))?\)/); if (dimensionKeysQuery) { - return this.getDimensionKeys(dimensionKeysQuery[1]); + return this.getDimensionKeys(dimensionKeysQuery[1], dimensionKeysQuery[3]); } var dimensionValuesQuery = query.match(/^dimension_values\(([^,]+?),\s?([^,]+?),\s?([^,]+?),\s?([^,]+?)\)/); diff --git a/public/app/plugins/datasource/cloudwatch/query_parameter_ctrl.js b/public/app/plugins/datasource/cloudwatch/query_parameter_ctrl.js index dc027f60aef..c239291ed8e 100644 --- a/public/app/plugins/datasource/cloudwatch/query_parameter_ctrl.js +++ b/public/app/plugins/datasource/cloudwatch/query_parameter_ctrl.js @@ -102,7 +102,7 @@ function (angular, _) { var query = $q.when([]); if (segment.type === 'key' || segment.type === 'plus-button') { - query = $scope.datasource.getDimensionKeys($scope.target.namespace); + query = $scope.datasource.getDimensionKeys($scope.target.namespace, $scope.target.region); } else if (segment.type === 'value') { var dimensionKey = $scope.dimSegments[$index-2].value; query = $scope.datasource.getDimensionValues(target.region, target.namespace, target.metricName, dimensionKey, {}); @@ -160,7 +160,7 @@ function (angular, _) { }; $scope.getMetrics = function() { - return $scope.datasource.metricFindQuery('metrics(' + $scope.target.namespace + ')') + return $scope.datasource.metricFindQuery('metrics(' + $scope.target.namespace + ',' + $scope.target.region + ')') .then($scope.transformToSegments(true)); }; From b90a078be00e32638a5f364b5626c40315e2bf12 Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Fri, 15 Jan 2016 12:07:31 +0900 Subject: [PATCH 18/20] (cloudwatch) change implementation to testable --- pkg/api/cloudwatch/metrics.go | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/pkg/api/cloudwatch/metrics.go b/pkg/api/cloudwatch/metrics.go index 4be6b65ee2b..7a98386347c 100644 --- a/pkg/api/cloudwatch/metrics.go +++ b/pkg/api/cloudwatch/metrics.go @@ -154,7 +154,7 @@ func handleGetMetrics(req *cwRequest, c *middleware.Context) { } } else { var err error - if namespaceMetrics, err = getMetricsForCustomMetrics(req, reqParam.Parameters.Namespace); err != nil { + if namespaceMetrics, err = getMetricsForCustomMetrics(req.Region, reqParam.Parameters.Namespace, req.DataSource.Database, getAllMetrics); err != nil { c.JsonApiErr(500, "Unable to call AWS API", err) return } @@ -187,7 +187,7 @@ func handleGetDimensions(req *cwRequest, c *middleware.Context) { } } else { var err error - if dimensionValues, err = getDimensionsForCustomMetrics(req, reqParam.Parameters.Namespace); err != nil { + if dimensionValues, err = getDimensionsForCustomMetrics(req.Region, reqParam.Parameters.Namespace, req.DataSource.Database, getAllMetrics); err != nil { c.JsonApiErr(500, "Unable to call AWS API", err) return } @@ -202,10 +202,10 @@ func handleGetDimensions(req *cwRequest, c *middleware.Context) { c.JSON(200, result) } -func getAllMetrics(req *cwRequest, namespace string) (cloudwatch.ListMetricsOutput, error) { +func getAllMetrics(region string, namespace string, database string) (cloudwatch.ListMetricsOutput, error) { cfg := &aws.Config{ - Region: aws.String(req.Region), - Credentials: getCredentials(req.DataSource.Database), + Region: aws.String(region), + Credentials: getCredentials(database), } svc := cloudwatch.New(session.New(cfg), cfg) @@ -232,8 +232,8 @@ func getAllMetrics(req *cwRequest, namespace string) (cloudwatch.ListMetricsOutp var metricsCacheLock sync.Mutex -func getMetricsForCustomMetrics(req *cwRequest, namespace string) ([]string, error) { - result, err := getAllMetrics(req, namespace) +func getMetricsForCustomMetrics(region string, namespace string, database string, getAllMetrics func(string, string, string) (cloudwatch.ListMetricsOutput, error)) ([]string, error) { + result, err := getAllMetrics(region, namespace, database) if err != nil { return []string{}, err } @@ -241,8 +241,6 @@ func getMetricsForCustomMetrics(req *cwRequest, namespace string) ([]string, err metricsCacheLock.Lock() defer metricsCacheLock.Unlock() - database := req.DataSource.Database - region := req.Region if _, ok := customMetricsMetricsMap[database]; !ok { customMetricsMetricsMap[database] = make(map[string]map[string]*CustomMetricsCache) } @@ -272,8 +270,8 @@ func getMetricsForCustomMetrics(req *cwRequest, namespace string) ([]string, err var dimensionsCacheLock sync.Mutex -func getDimensionsForCustomMetrics(req *cwRequest, namespace string) ([]string, error) { - result, err := getAllMetrics(req, namespace) +func getDimensionsForCustomMetrics(region string, namespace string, database string, getAllMetrics func(string, string, string) (cloudwatch.ListMetricsOutput, error)) ([]string, error) { + result, err := getAllMetrics(region, namespace, database) if err != nil { return []string{}, err } @@ -281,8 +279,6 @@ func getDimensionsForCustomMetrics(req *cwRequest, namespace string) ([]string, dimensionsCacheLock.Lock() defer dimensionsCacheLock.Unlock() - database := req.DataSource.Database - region := req.Region if _, ok := customMetricsDimensionsMap[database]; !ok { customMetricsDimensionsMap[database] = make(map[string]map[string]*CustomMetricsCache) } From ccb063df0658661fdfa4b088fbab82c9ec2dd3e4 Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Fri, 15 Jan 2016 12:50:58 +0900 Subject: [PATCH 19/20] (cloudwatch) add test for custom metrics --- pkg/api/cloudwatch/metrics_test.go | 63 ++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 pkg/api/cloudwatch/metrics_test.go diff --git a/pkg/api/cloudwatch/metrics_test.go b/pkg/api/cloudwatch/metrics_test.go new file mode 100644 index 00000000000..ec39452e116 --- /dev/null +++ b/pkg/api/cloudwatch/metrics_test.go @@ -0,0 +1,63 @@ +package cloudwatch + +import ( + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/cloudwatch" + . "github.com/smartystreets/goconvey/convey" +) + +func TestCloudWatchMetrics(t *testing.T) { + + Convey("When calling getMetricsForCustomMetrics", t, func() { + region := "us-east-1" + namespace := "Foo" + database := "default" + f := func(region string, namespace string, database string) (cloudwatch.ListMetricsOutput, error) { + return cloudwatch.ListMetricsOutput{ + Metrics: []*cloudwatch.Metric{ + { + MetricName: aws.String("Test_MetricName"), + Dimensions: []*cloudwatch.Dimension{ + { + Name: aws.String("Test_DimensionName"), + }, + }, + }, + }, + }, nil + } + metrics, _ := getMetricsForCustomMetrics(region, namespace, database, f) + + Convey("Should contain Test_MetricName", func() { + So(metrics, ShouldContain, "Test_MetricName") + }) + }) + + Convey("When calling getDimensionsForCustomMetrics", t, func() { + region := "us-east-1" + namespace := "Foo" + database := "default" + f := func(region string, namespace string, database string) (cloudwatch.ListMetricsOutput, error) { + return cloudwatch.ListMetricsOutput{ + Metrics: []*cloudwatch.Metric{ + { + MetricName: aws.String("Test_MetricName"), + Dimensions: []*cloudwatch.Dimension{ + { + Name: aws.String("Test_DimensionName"), + }, + }, + }, + }, + }, nil + } + dimensionKeys, _ := getDimensionsForCustomMetrics(region, namespace, database, f) + + Convey("Should contain Test_DimensionName", func() { + So(dimensionKeys, ShouldContain, "Test_DimensionName") + }) + }) + +} From b5a5b7b912e6052485e6ba3a437dc20f94b324b5 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Thu, 4 Feb 2016 13:34:23 -0800 Subject: [PATCH 20/20] Removed totalStats from PR --- .../app/features/dashboard/dashboardCtrl.js | 20 ----------- .../features/dashboard/partials/settings.html | 35 ------------------- 2 files changed, 55 deletions(-) diff --git a/public/app/features/dashboard/dashboardCtrl.js b/public/app/features/dashboard/dashboardCtrl.js index cc305e4aed4..ea0d91019b8 100644 --- a/public/app/features/dashboard/dashboardCtrl.js +++ b/public/app/features/dashboard/dashboardCtrl.js @@ -57,7 +57,6 @@ function (angular, $, config, moment) { dashboardKeybindings.shortcuts($scope); - $scope.evaluateTotalStats(); $scope.updateSubmenuVisibility(); $scope.setWindowTitleAndTheme(); @@ -68,25 +67,6 @@ function (angular, $, config, moment) { }); }; - $scope.evaluateTotalStats = function() { - $scope.dashboard.totalRows = 0; - $scope.dashboard.totalPanels = 0; - $scope.dashboard.totalQueries = 0; - if ($scope.dashboard.rows) { - $scope.dashboard.totalRows = $scope.dashboard.rows.length; - for (var i=0; i<$scope.dashboard.rows.length; i++) { - if ($scope.dashboard.rows[i].panels) { - $scope.dashboard.totalPanels += $scope.dashboard.rows[i].panels.length; - for (var j=0; j<$scope.dashboard.rows[i].panels.length; j++) { - if ($scope.dashboard.rows[i].panels[j].targets) { - $scope.dashboard.totalQueries += $scope.dashboard.rows[i].panels[j].targets.length; - } - } - } - } - } - }; - $scope.updateSubmenuVisibility = function() { $scope.submenuEnabled = $scope.dashboard.isSubmenuFeaturesEnabled(); }; diff --git a/public/app/features/dashboard/partials/settings.html b/public/app/features/dashboard/partials/settings.html index d27d3322692..7af2abfe076 100644 --- a/public/app/features/dashboard/partials/settings.html +++ b/public/app/features/dashboard/partials/settings.html @@ -162,41 +162,6 @@
-
-
-
-
    -
  • - Total Rows: -
  • -
  • - {{dashboard.totalRows}} -
  • -
-
-
-
-
    -
  • - Total Panels: -
  • -
  • - {{dashboard.totalPanels}} -
  • -
-
-
-
-
    -
  • - Total Queries: -
  • -
  • - {{dashboard.totalQueries}} -
  • -
-
-