diff --git a/pkg/api/api.go b/pkg/api/api.go index d184a03f58e..9c06512c6d3 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -176,7 +176,8 @@ func Register(r *macaron.Macaron) { r.Group("/playlists", func() { r.Get("/", wrap(SearchPlaylists)) r.Get("/:id", ValidateOrgPlaylist, wrap(GetPlaylist)) - r.Get("/:id/dashboards", ValidateOrgPlaylist, wrap(GetPlaylistDashboards)) + r.Get("/:id/playlistitems", ValidateOrgPlaylist, wrap(GetPlaylistItems)) + r.Get("/:id/playlistdashboards", ValidateOrgPlaylist, wrap(GetPlaylistDashboards)) r.Delete("/:id", reqEditorRole, ValidateOrgPlaylist, wrap(DeletePlaylist)) r.Put("/:id", reqEditorRole, bind(m.UpdatePlaylistQuery{}), ValidateOrgPlaylist, wrap(UpdatePlaylist)) r.Post("/", reqEditorRole, bind(m.CreatePlaylistQuery{}), wrap(CreatePlaylist)) diff --git a/pkg/api/playlist.go b/pkg/api/playlist.go index 44745f91143..b2d048efcc2 100644 --- a/pkg/api/playlist.go +++ b/pkg/api/playlist.go @@ -1,9 +1,12 @@ package api import ( + "errors" "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/middleware" m "github.com/grafana/grafana/pkg/models" + "strconv" ) func ValidateOrgPlaylist(c *middleware.Context) { @@ -52,19 +55,67 @@ func GetPlaylist(c *middleware.Context) Response { return ApiError(500, "Playlist not found", err) } - return Json(200, cmd.Result) + itemQuery := m.GetPlaylistItemsByIdQuery{PlaylistId: id} + if err := bus.Dispatch(&itemQuery); err != nil { + log.Warn("itemQuery failed: %v", err) + return ApiError(500, "Playlist items not found", err) + } + + playlistDTOs := make([]m.PlaylistItemDTO, 0) + + for _, item := range *itemQuery.Result { + playlistDTOs = append(playlistDTOs, m.PlaylistItemDTO{ + Id: item.Id, + PlaylistId: item.PlaylistId, + Type: item.Type, + Value: item.Value, + Order: item.Order, + }) + } + + dto := &m.PlaylistDTO{ + Id: cmd.Result.Id, + Title: cmd.Result.Title, + Timespan: cmd.Result.Timespan, + OrgId: cmd.Result.OrgId, + Items: playlistDTOs, + } + + return Json(200, dto) } -func GetPlaylistDashboards(c *middleware.Context) Response { - id := c.ParamsInt64(":id") +func LoadPlaylistItems(id int64) ([]m.PlaylistItem, error) { + itemQuery := m.GetPlaylistItemsByIdQuery{PlaylistId: id} + if err := bus.Dispatch(&itemQuery); err != nil { + log.Warn("itemQuery failed: %v", err) + return nil, errors.New("Playlist not found") + } - query := m.GetPlaylistDashboardsQuery{Id: id} - if err := bus.Dispatch(&query); err != nil { - return ApiError(500, "Playlist not found", err) + return *itemQuery.Result, nil +} + +func LoadPlaylistDashboards(id int64) ([]m.PlaylistDashboardDto, error) { + playlistItems, _ := LoadPlaylistItems(id) + + dashboardIds := make([]int64, 0) + + for _, i := range playlistItems { + dashboardId, _ := strconv.ParseInt(i.Value, 10, 64) + dashboardIds = append(dashboardIds, dashboardId) + } + + if len(dashboardIds) == 0 { + return make([]m.PlaylistDashboardDto, 0), nil + } + + dashboardQuery := m.GetPlaylistDashboardsQuery{DashboardIds: dashboardIds} + if err := bus.Dispatch(&dashboardQuery); err != nil { + log.Warn("dashboardquery failed: %v", err) + return nil, errors.New("Playlist not found") } dtos := make([]m.PlaylistDashboardDto, 0) - for _, item := range *query.Result { + for _, item := range *dashboardQuery.Result { dtos = append(dtos, m.PlaylistDashboardDto{ Id: item.Id, Slug: item.Slug, @@ -73,7 +124,43 @@ func GetPlaylistDashboards(c *middleware.Context) Response { }) } - return Json(200, dtos) + return dtos, nil +} + +func GetPlaylistItems(c *middleware.Context) Response { + id := c.ParamsInt64(":id") + + items, err := LoadPlaylistItems(id) + + if err != nil { + return ApiError(500, "Could not load playlist items", err) + } + + playlistDTOs := make([]m.PlaylistItemDTO, 0) + + for _, item := range items { + playlistDTOs = append(playlistDTOs, m.PlaylistItemDTO{ + Id: item.Id, + PlaylistId: item.PlaylistId, + Type: item.Type, + Value: item.Value, + Order: item.Order, + Title: item.Title, + }) + } + + return Json(200, playlistDTOs) +} + +func GetPlaylistDashboards(c *middleware.Context) Response { + id := c.ParamsInt64(":id") + + playlists, err := LoadPlaylistDashboards(id) + if err != nil { + return ApiError(500, "Could not load dashboards", err) + } + + return Json(200, playlists) } func DeletePlaylist(c *middleware.Context) Response { @@ -103,5 +190,25 @@ func UpdatePlaylist(c *middleware.Context, query m.UpdatePlaylistQuery) Response return ApiError(500, "Failed to save playlist", err) } + items, err := LoadPlaylistItems(query.Id) + + playlistDTOs := make([]m.PlaylistItemDTO, 0) + + for _, item := range items { + playlistDTOs = append(playlistDTOs, m.PlaylistItemDTO{ + Id: item.Id, + PlaylistId: item.PlaylistId, + Type: item.Type, + Value: item.Value, + Order: item.Order, + }) + } + + if err != nil { + return ApiError(500, "Failed to save playlist", err) + } + + query.Result.Items = playlistDTOs + return Json(200, query.Result) } diff --git a/pkg/models/playlist.go b/pkg/models/playlist.go index 84478459922..802a9262c9f 100644 --- a/pkg/models/playlist.go +++ b/pkg/models/playlist.go @@ -12,12 +12,27 @@ var ( // Playlist model type Playlist struct { - Id int64 `json:"id"` - Title string `json:"title"` - Type string `json:"type"` - Timespan string `json:"timespan"` - Data []int64 `json:"data"` - OrgId int64 `json:"-"` + Id int64 `json:"id"` + Title string `json:"title"` + Timespan string `json:"timespan"` + OrgId int64 `json:"-"` +} + +type PlaylistDTO struct { + Id int64 `json:"id"` + Title string `json:"title"` + Timespan string `json:"timespan"` + OrgId int64 `json:"-"` + Items []PlaylistItemDTO `json:"items"` +} + +type PlaylistItemDTO struct { + Id int64 `json:"id"` + PlaylistId int64 `json:"playlistid"` + Type string `json:"type"` + Title string `json:"title"` + Value string `json:"value"` + Order int `json:"order"` } type PlaylistDashboard struct { @@ -26,6 +41,15 @@ type PlaylistDashboard struct { Title string `json:"title"` } +type PlaylistItem struct { + Id int64 + PlaylistId int64 + Type string + Value string + Order int + Title string +} + func (this PlaylistDashboard) TableName() string { return "dashboard" } @@ -60,9 +84,9 @@ type UpdatePlaylistQuery struct { Title string Type string Timespan string - Data []int64 + Items []PlaylistItemDTO - Result *Playlist + Result *PlaylistDTO } type CreatePlaylistQuery struct { @@ -71,6 +95,7 @@ type CreatePlaylistQuery struct { Timespan string Data []int64 OrgId int64 + Items []PlaylistItemDTO Result *Playlist } @@ -80,9 +105,14 @@ type GetPlaylistByIdQuery struct { Result *Playlist } +type GetPlaylistItemsByIdQuery struct { + PlaylistId int64 + Result *[]PlaylistItem +} + type GetPlaylistDashboardsQuery struct { - Id int64 - Result *PlaylistDashboards + DashboardIds []int64 + Result *PlaylistDashboards } type DeletePlaylistQuery struct { diff --git a/pkg/services/sqlstore/dashboard.go b/pkg/services/sqlstore/dashboard.go index 547c24fc7df..bbec541589c 100644 --- a/pkg/services/sqlstore/dashboard.go +++ b/pkg/services/sqlstore/dashboard.go @@ -220,26 +220,6 @@ func DeleteDashboard(cmd *m.DeleteDashboardCommand) error { } } - var playlists = make(m.Playlists, 0) - err = sess.Where("data LIKE ?", fmt.Sprintf("%%%v%%", dashboard.Id)).Find(&playlists) - if err != nil { - return err - } - - for _, playlist := range playlists { - filteredData := make([]int64, 0) - for _, plDashboardId := range playlist.Data { - if plDashboardId != dashboard.Id { - filteredData = append(filteredData, plDashboardId) - } - } - playlist.Data = filteredData - _, err = sess.Id(playlist.Id).Cols("data").Update(playlist) - if err != nil { - return err - } - } - return nil }) } diff --git a/pkg/services/sqlstore/migrations/playlist_mig.go b/pkg/services/sqlstore/migrations/playlist_mig.go index d8625f301ae..c97fc1adb3d 100644 --- a/pkg/services/sqlstore/migrations/playlist_mig.go +++ b/pkg/services/sqlstore/migrations/playlist_mig.go @@ -8,8 +8,6 @@ func addPlaylistMigrations(mg *Migrator) { Columns: []*Column{ {Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true}, {Name: "title", Type: DB_NVarchar, Length: 255, Nullable: false}, - {Name: "type", Type: DB_NVarchar, Length: 255, Nullable: false}, - {Name: "data", Type: DB_Text, Nullable: false}, {Name: "timespan", Type: DB_NVarchar, Length: 255, Nullable: false}, {Name: "org_id", Type: DB_BigInt, Nullable: false}, }, @@ -17,4 +15,18 @@ func addPlaylistMigrations(mg *Migrator) { // create table mg.AddMigration("create playlist table v1", NewAddTableMigration(playlistV1)) + + playlistItemV1 := Table{ + Name: "playlist_item", + Columns: []*Column{ + {Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true}, + {Name: "playlist_id", Type: DB_BigInt, Nullable: false}, + {Name: "type", Type: DB_NVarchar, Length: 255, Nullable: false}, + {Name: "value", Type: DB_Text, Nullable: false}, + {Name: "title", Type: DB_Text, Nullable: false}, + {Name: "order", Type: DB_Int, Nullable: false}, + }, + } + + mg.AddMigration("create playlist item table v1", NewAddTableMigration(playlistItemV1)) } diff --git a/pkg/services/sqlstore/playlist.go b/pkg/services/sqlstore/playlist.go index 56f6ca23fcb..ff3f1753a3c 100644 --- a/pkg/services/sqlstore/playlist.go +++ b/pkg/services/sqlstore/playlist.go @@ -1,6 +1,7 @@ package sqlstore import ( + "fmt" "github.com/go-xorm/xorm" "github.com/grafana/grafana/pkg/bus" @@ -14,6 +15,7 @@ func init() { bus.AddHandler("sql", SearchPlaylists) bus.AddHandler("sql", GetPlaylist) bus.AddHandler("sql", GetPlaylistDashboards) + bus.AddHandler("sql", GetPlaylistItem) } func CreatePlaylist(query *m.CreatePlaylistQuery) error { @@ -21,14 +23,27 @@ func CreatePlaylist(query *m.CreatePlaylistQuery) error { playlist := m.Playlist{ Title: query.Title, - Type: query.Type, - Data: query.Data, Timespan: query.Timespan, OrgId: query.OrgId, } _, err = x.Insert(&playlist) + fmt.Printf("%v", playlist.Id) + + playlistItems := make([]m.PlaylistItem, 0) + for _, item := range query.Items { + playlistItems = append(playlistItems, m.PlaylistItem{ + PlaylistId: playlist.Id, + Type: item.Type, + Value: item.Value, + Order: item.Order, + Title: item.Title, + }) + } + + _, err = x.Insert(&playlistItems) + query.Result = &playlist return err } @@ -39,8 +54,6 @@ func UpdatePlaylist(query *m.UpdatePlaylistQuery) error { playlist := m.Playlist{ Id: query.Id, Title: query.Title, - Type: query.Type, - Data: query.Data, Timespan: query.Timespan, } @@ -50,9 +63,40 @@ func UpdatePlaylist(query *m.UpdatePlaylistQuery) error { return m.ErrPlaylistNotFound } - _, err = x.Id(query.Id).Cols("id", "title", "data", "timespan").Update(&playlist) + query.Result = &m.PlaylistDTO{ + Id: playlist.Id, + OrgId: playlist.OrgId, + Title: playlist.Title, + Timespan: playlist.Timespan, + } + + _, err = x.Id(query.Id).Cols("id", "title", "timespan").Update(&playlist) + + if err != nil { + return err + } + + rawSql := "DELETE FROM playlist_item WHERE playlist_id = ?" + _, err = x.Exec(rawSql, query.Id) + + if err != nil { + return err + } + + playlistItems := make([]m.PlaylistItem, 0) + + for _, item := range query.Items { + playlistItems = append(playlistItems, m.PlaylistItem{ + PlaylistId: playlist.Id, + Type: item.Type, + Value: item.Value, + Order: item.Order, + Title: item.Title, + }) + } + + _, err = x.Insert(&playlistItems) - query.Result = &playlist return err } @@ -63,6 +107,7 @@ func GetPlaylist(query *m.GetPlaylistByIdQuery) error { playlist := m.Playlist{} _, err := x.Id(query.Id).Get(&playlist) + query.Result = &playlist return err @@ -74,9 +119,17 @@ func DeletePlaylist(query *m.DeletePlaylistQuery) error { } return inTransaction(func(sess *xorm.Session) error { - var rawSql = "DELETE FROM playlist WHERE id = ?" - _, err := sess.Exec(rawSql, query.Id) - return err + var rawPlaylistSql = "DELETE FROM playlist WHERE id = ?" + _, err := sess.Exec(rawPlaylistSql, query.Id) + + if err != nil { + return err + } + + var rawItemSql = "DELETE FROM playlist_item WHERE playlist_id = ?" + _, err2 := sess.Exec(rawItemSql, query.Id) + + return err2 }) } @@ -96,31 +149,32 @@ func SearchPlaylists(query *m.PlaylistQuery) error { return err } +func GetPlaylistItem(query *m.GetPlaylistItemsByIdQuery) error { + if query.PlaylistId == 0 { + return m.ErrCommandValidationFailed + } + + var playlistItems = make([]m.PlaylistItem, 0) + err := x.Where("playlist_id=?", query.PlaylistId).Find(&playlistItems) + + query.Result = &playlistItems + + return err +} + func GetPlaylistDashboards(query *m.GetPlaylistDashboardsQuery) error { - if query.Id == 0 { + if len(query.DashboardIds) == 0 { return m.ErrCommandValidationFailed } var dashboards = make(m.PlaylistDashboards, 0) - var playlist = m.Playlist{} - - hasPlaylist, err := x.Id(query.Id).Get(&playlist) + err := x.In("id", query.DashboardIds).Find(&dashboards) query.Result = &dashboards if err != nil { return err } - if !hasPlaylist || len(playlist.Data) == 0 { - return nil - } - - err = x.In("id", playlist.Data).Find(&dashboards) - - if err != nil { - return err - } - return nil } diff --git a/public/app/features/playlist/partials/playlist.html b/public/app/features/playlist/partials/playlist.html index 71fec4abb9b..925b3699505 100644 --- a/public/app/features/playlist/partials/playlist.html +++ b/public/app/features/playlist/partials/playlist.html @@ -59,14 +59,14 @@
-
Search results ({{filteredDashboards.length}})
+
Search results ({{filteredPlaylistItems.length}})
- +
- {{dashboard.title}} + {{playlistItem.title}} - @@ -82,18 +82,18 @@
Playlist dashboards
- + @@ -109,11 +109,7 @@ - + ng-click="savePlaylist(playlist, playlistItems)">Save diff --git a/public/app/features/playlist/playlist_edit_ctrl.js b/public/app/features/playlist/playlist_edit_ctrl.js index feb7ac4cee2..38e6bd13156 100644 --- a/public/app/features/playlist/playlist_edit_ctrl.js +++ b/public/app/features/playlist/playlist_edit_ctrl.js @@ -10,12 +10,12 @@ function (angular, config, _) { module.controller('PlaylistEditCtrl', function($scope, playlistSrv, backendSrv, $location, $route) { $scope.timespan = config.playlist_timespan; - $scope.filteredDashboards = []; - $scope.foundDashboards = []; + $scope.filteredPlaylistItems = []; + $scope.foundPlaylistItems = []; $scope.searchQuery = ''; $scope.loading = false; $scope.playlist = {}; - $scope.dashboards = []; + $scope.playlistItems = []; if ($route.current.params.id) { var playlistId = $route.current.params.id; @@ -25,9 +25,9 @@ function (angular, config, _) { $scope.playlist = result; }); - backendSrv.get('/api/playlists/' + playlistId + '/dashboards') + backendSrv.get('/api/playlists/' + playlistId + '/playlistitems') .then(function(result) { - $scope.dashboards = result; + $scope.playlistItems = result; }); } @@ -43,43 +43,43 @@ function (angular, config, _) { backendSrv.search(query) .then(function(results) { - $scope.foundDashboards = results; - $scope.filterFoundDashboards(); + $scope.foundPlaylistItems = results; + $scope.filterFoundPlaylistItems(); }) .finally(function() { $scope.loading = false; }); }; - $scope.filterFoundDashboards = function() { - $scope.filteredDashboards = _.reject($scope.foundDashboards, function(dashboard) { - return _.findWhere($scope.dashboards, function(listDashboard) { - return listDashboard.id === dashboard.id; + $scope.filterFoundPlaylistItems = function() { + $scope.filteredPlaylistItems = _.reject($scope.foundPlaylistItems, function(playlistItem) { + return _.findWhere($scope.playlistItems, function(listPlaylistItem) { + return parseInt(listPlaylistItem.value) === playlistItem.id; }); }); }; - $scope.addDashboard = function(dashboard) { - $scope.dashboards.push(dashboard); - $scope.filterFoundDashboards(); + $scope.addPlaylistItem = function(playlistItem) { + playlistItem.value = playlistItem.id.toString(); + playlistItem.type = 'dashboard_by_id'; + playlistItem.order = $scope.playlistItems.length + 1; + + $scope.playlistItems.push(playlistItem); + $scope.filterFoundPlaylistItems(); + }; - $scope.removeDashboard = function(dashboard) { - _.remove($scope.dashboards, function(listedDashboard) { - return dashboard === listedDashboard; + $scope.removePlaylistItem = function(playlistItem) { + _.remove($scope.playlistItems, function(listedPlaylistItem) { + return playlistItem === listedPlaylistItem; }); - $scope.filterFoundDashboards(); + $scope.filterFoundPlaylistItems(); }; - $scope.savePlaylist = function(playlist, dashboards) { + $scope.savePlaylist = function(playlist, playlistItems) { var savePromise; - playlist.data = dashboards.map(function(dashboard) { - return dashboard.id; - }); - - // Hardcoding playlist type for this iteration - playlist.type = "dashboards"; + playlist.items = playlistItems; savePromise = playlist.id ? backendSrv.put('/api/playlists/' + playlist.id, playlist) @@ -90,7 +90,7 @@ function (angular, config, _) { $scope.appEvent('alert-success', ['Playlist saved', '']); $location.path('/playlists'); }, function() { - $scope.appEvent('alert-success', ['Unable to save playlist', '']); + $scope.appEvent('alert-error', ['Unable to save playlist', '']); }); }; @@ -98,16 +98,12 @@ function (angular, config, _) { return !$scope.playlist.id; }; - $scope.startPlaylist = function(playlist, dashboards) { - playlistSrv.start(dashboards, playlist.timespan); - }; - $scope.isPlaylistEmpty = function() { - return !$scope.dashboards.length; + return !$scope.playlistItems.length; }; $scope.isSearchResultsEmpty = function() { - return !$scope.foundDashboards.length; + return !$scope.foundPlaylistItems.length; }; $scope.isSearchQueryEmpty = function() { @@ -122,22 +118,22 @@ function (angular, config, _) { return $scope.loading; }; - $scope.moveDashboard = function(dashboard, offset) { - var currentPosition = $scope.dashboards.indexOf(dashboard); + $scope.movePlaylistItem = function(playlistItem, offset) { + var currentPosition = $scope.playlistItems.indexOf(playlistItem); var newPosition = currentPosition + offset; - if (newPosition >= 0 && newPosition < $scope.dashboards.length) { - $scope.dashboards.splice(currentPosition, 1); - $scope.dashboards.splice(newPosition, 0, dashboard); + if (newPosition >= 0 && newPosition < $scope.playlistItems.length) { + $scope.playlistItems.splice(currentPosition, 1); + $scope.playlistItems.splice(newPosition, 0, playlistItem); } }; - $scope.moveDashboardUp = function(dashboard) { - $scope.moveDashboard(dashboard, -1); + $scope.movePlaylistItemUp = function(playlistItem) { + $scope.moveDashboard(playlistItem, -1); }; - $scope.moveDashboardDown = function(dashboard) { - $scope.moveDashboard(dashboard, 1); + $scope.movePlaylistItemDown = function(playlistItem) { + $scope.moveDashboard(playlistItem, 1); }; $scope.search(); diff --git a/public/app/features/playlist/playlist_routes.js b/public/app/features/playlist/playlist_routes.js index c7481573357..24223c88cbc 100644 --- a/public/app/features/playlist/playlist_routes.js +++ b/public/app/features/playlist/playlist_routes.js @@ -31,7 +31,7 @@ function (angular) { return backendSrv.get('/api/playlists/' + playlistId) .then(function(playlist) { - return backendSrv.get('/api/playlists/' + playlistId + '/dashboards') + return backendSrv.get('/api/playlists/' + playlistId + '/playlistdashboards') .then(function(dashboards) { playlistSrv.start(dashboards, playlist.timespan); });
- {{dashboard.title}} + {{playlistItem.title}} - - -