mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Chore: Introduce playlist service (#52252)
* Store: Introduce playlist service * Integrate playlist service * Update swagger
This commit is contained in:
parent
332639ce43
commit
fb379ae436
@ -2,7 +2,7 @@ package definitions
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/playlist"
|
||||
)
|
||||
|
||||
// swagger:route GET /playlists playlists searchPlaylists
|
||||
@ -121,7 +121,7 @@ type DeletePlaylistParams struct {
|
||||
type UpdatePlaylistParams struct {
|
||||
// in:body
|
||||
// required:true
|
||||
Body models.UpdatePlaylistCommand
|
||||
Body playlist.UpdatePlaylistCommand
|
||||
// in:path
|
||||
// required:true
|
||||
UID string `json:"uid"`
|
||||
@ -131,28 +131,28 @@ type UpdatePlaylistParams struct {
|
||||
type CreatePlaylistParams struct {
|
||||
// in:body
|
||||
// required:true
|
||||
Body models.CreatePlaylistCommand
|
||||
Body playlist.CreatePlaylistCommand
|
||||
}
|
||||
|
||||
// swagger:response searchPlaylistsResponse
|
||||
type SearchPlaylistsResponse struct {
|
||||
// The response message
|
||||
// in: body
|
||||
Body models.Playlists `json:"body"`
|
||||
Body playlist.Playlists `json:"body"`
|
||||
}
|
||||
|
||||
// swagger:response getPlaylistResponse
|
||||
type GetPlaylistResponse struct {
|
||||
// The response message
|
||||
// in: body
|
||||
Body *models.PlaylistDTO `json:"body"`
|
||||
Body *playlist.PlaylistDTO `json:"body"`
|
||||
}
|
||||
|
||||
// swagger:response getPlaylistItemsResponse
|
||||
type GetPlaylistItemsResponse struct {
|
||||
// The response message
|
||||
// in: body
|
||||
Body []models.PlaylistItemDTO `json:"body"`
|
||||
Body []playlist.PlaylistItemDTO `json:"body"`
|
||||
}
|
||||
|
||||
// swagger:response getPlaylistDashboardsResponse
|
||||
@ -166,12 +166,12 @@ type GetPlaylistDashboardsResponse struct {
|
||||
type UpdatePlaylistResponseResponse struct {
|
||||
// The response message
|
||||
// in: body
|
||||
Body *models.PlaylistDTO `json:"body"`
|
||||
Body *playlist.PlaylistDTO `json:"body"`
|
||||
}
|
||||
|
||||
// swagger:response createPlaylistResponse
|
||||
type CreatePlaylistResponse struct {
|
||||
// The response message
|
||||
// in: body
|
||||
Body *models.Playlist `json:"body"`
|
||||
Body *playlist.Playlist `json:"body"`
|
||||
}
|
||||
|
@ -58,6 +58,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/login"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert"
|
||||
"github.com/grafana/grafana/pkg/services/notifications"
|
||||
"github.com/grafana/grafana/pkg/services/playlist"
|
||||
"github.com/grafana/grafana/pkg/services/plugindashboards"
|
||||
pluginSettings "github.com/grafana/grafana/pkg/services/pluginsettings/service"
|
||||
pref "github.com/grafana/grafana/pkg/services/preference"
|
||||
@ -168,6 +169,7 @@ type HTTPServer struct {
|
||||
dashboardVersionService dashver.Service
|
||||
PublicDashboardsApi *publicdashboardsApi.Api
|
||||
starService star.Service
|
||||
playlistService playlist.Service
|
||||
CoremodelRegistry *registry.Generic
|
||||
CoremodelStaticRegistry *registry.Static
|
||||
kvStore kvstore.KVStore
|
||||
@ -206,7 +208,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
||||
avatarCacheServer *avatar.AvatarCacheServer, preferenceService pref.Service, entityEventsService store.EntityEventsService,
|
||||
teamsPermissionsService accesscontrol.TeamPermissionsService, folderPermissionsService accesscontrol.FolderPermissionsService,
|
||||
dashboardPermissionsService accesscontrol.DashboardPermissionsService, dashboardVersionService dashver.Service,
|
||||
starService star.Service, csrfService csrf.Service, coremodelRegistry *registry.Generic, coremodelStaticRegistry *registry.Static,
|
||||
starService star.Service, playlistService playlist.Service, csrfService csrf.Service, coremodelRegistry *registry.Generic, coremodelStaticRegistry *registry.Static,
|
||||
kvStore kvstore.KVStore, secretsMigrator secrets.Migrator, remoteSecretsCheck secretsKV.UseRemoteSecretsPluginCheck,
|
||||
publicDashboardsApi *publicdashboardsApi.Api, userService user.Service) (*HTTPServer, error) {
|
||||
web.Env = cfg.Env
|
||||
@ -289,6 +291,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
||||
dashboardPermissionsService: dashboardPermissionsService,
|
||||
dashboardVersionService: dashboardVersionService,
|
||||
starService: starService,
|
||||
playlistService: playlistService,
|
||||
CoremodelRegistry: coremodelRegistry,
|
||||
CoremodelStaticRegistry: coremodelStaticRegistry,
|
||||
kvStore: kvStore,
|
||||
|
@ -6,25 +6,26 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/playlist"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
)
|
||||
|
||||
func (hs *HTTPServer) ValidateOrgPlaylist(c *models.ReqContext) {
|
||||
uid := web.Params(c.Req)[":uid"]
|
||||
query := models.GetPlaylistByUidQuery{UID: uid, OrgId: c.OrgId}
|
||||
err := hs.SQLStore.GetPlaylist(c.Req.Context(), &query)
|
||||
query := playlist.GetPlaylistByUidQuery{UID: uid, OrgId: c.OrgId}
|
||||
p, err := hs.playlistService.Get(c.Req.Context(), &query)
|
||||
|
||||
if err != nil {
|
||||
c.JsonApiErr(404, "Playlist not found", err)
|
||||
return
|
||||
}
|
||||
|
||||
if query.Result.OrgId == 0 {
|
||||
if p.OrgId == 0 {
|
||||
c.JsonApiErr(404, "Playlist not found", err)
|
||||
return
|
||||
}
|
||||
|
||||
if query.Result.OrgId != c.OrgId {
|
||||
if p.OrgId != c.OrgId {
|
||||
c.JsonApiErr(403, "You are not allowed to edit/view playlist", nil)
|
||||
return
|
||||
}
|
||||
@ -38,53 +39,54 @@ func (hs *HTTPServer) SearchPlaylists(c *models.ReqContext) response.Response {
|
||||
limit = 1000
|
||||
}
|
||||
|
||||
searchQuery := models.GetPlaylistsQuery{
|
||||
searchQuery := playlist.GetPlaylistsQuery{
|
||||
Name: query,
|
||||
Limit: limit,
|
||||
OrgId: c.OrgId,
|
||||
}
|
||||
|
||||
err := hs.SQLStore.SearchPlaylists(c.Req.Context(), &searchQuery)
|
||||
playlists, err := hs.playlistService.Search(c.Req.Context(), &searchQuery)
|
||||
if err != nil {
|
||||
return response.Error(500, "Search failed", err)
|
||||
}
|
||||
|
||||
return response.JSON(http.StatusOK, searchQuery.Result)
|
||||
return response.JSON(http.StatusOK, playlists)
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) GetPlaylist(c *models.ReqContext) response.Response {
|
||||
uid := web.Params(c.Req)[":uid"]
|
||||
cmd := models.GetPlaylistByUidQuery{UID: uid, OrgId: c.OrgId}
|
||||
cmd := playlist.GetPlaylistByUidQuery{UID: uid, OrgId: c.OrgId}
|
||||
|
||||
if err := hs.SQLStore.GetPlaylist(c.Req.Context(), &cmd); err != nil {
|
||||
p, err := hs.playlistService.Get(c.Req.Context(), &cmd)
|
||||
if err != nil {
|
||||
return response.Error(500, "Playlist not found", err)
|
||||
}
|
||||
|
||||
playlistDTOs, _ := hs.LoadPlaylistItemDTOs(c.Req.Context(), uid, c.OrgId)
|
||||
|
||||
dto := &models.PlaylistDTO{
|
||||
Id: cmd.Result.Id,
|
||||
UID: cmd.Result.UID,
|
||||
Name: cmd.Result.Name,
|
||||
Interval: cmd.Result.Interval,
|
||||
OrgId: cmd.Result.OrgId,
|
||||
dto := &playlist.PlaylistDTO{
|
||||
Id: p.Id,
|
||||
UID: p.UID,
|
||||
Name: p.Name,
|
||||
Interval: p.Interval,
|
||||
OrgId: p.OrgId,
|
||||
Items: playlistDTOs,
|
||||
}
|
||||
|
||||
return response.JSON(http.StatusOK, dto)
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) LoadPlaylistItemDTOs(ctx context.Context, uid string, orgId int64) ([]models.PlaylistItemDTO, error) {
|
||||
func (hs *HTTPServer) LoadPlaylistItemDTOs(ctx context.Context, uid string, orgId int64) ([]playlist.PlaylistItemDTO, error) {
|
||||
playlistitems, err := hs.LoadPlaylistItems(ctx, uid, orgId)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
playlistDTOs := make([]models.PlaylistItemDTO, 0)
|
||||
playlistDTOs := make([]playlist.PlaylistItemDTO, 0)
|
||||
|
||||
for _, item := range playlistitems {
|
||||
playlistDTOs = append(playlistDTOs, models.PlaylistItemDTO{
|
||||
playlistDTOs = append(playlistDTOs, playlist.PlaylistItemDTO{
|
||||
Id: item.Id,
|
||||
PlaylistId: item.PlaylistId,
|
||||
Type: item.Type,
|
||||
@ -97,13 +99,14 @@ func (hs *HTTPServer) LoadPlaylistItemDTOs(ctx context.Context, uid string, orgI
|
||||
return playlistDTOs, nil
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) LoadPlaylistItems(ctx context.Context, uid string, orgId int64) ([]models.PlaylistItem, error) {
|
||||
itemQuery := models.GetPlaylistItemsByUidQuery{PlaylistUID: uid, OrgId: orgId}
|
||||
if err := hs.SQLStore.GetPlaylistItem(ctx, &itemQuery); err != nil {
|
||||
func (hs *HTTPServer) LoadPlaylistItems(ctx context.Context, uid string, orgId int64) ([]playlist.PlaylistItem, error) {
|
||||
itemQuery := playlist.GetPlaylistItemsByUidQuery{PlaylistUID: uid, OrgId: orgId}
|
||||
items, err := hs.playlistService.GetItems(ctx, &itemQuery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return *itemQuery.Result, nil
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) GetPlaylistItems(c *models.ReqContext) response.Response {
|
||||
@ -132,8 +135,8 @@ func (hs *HTTPServer) GetPlaylistDashboards(c *models.ReqContext) response.Respo
|
||||
func (hs *HTTPServer) DeletePlaylist(c *models.ReqContext) response.Response {
|
||||
uid := web.Params(c.Req)[":uid"]
|
||||
|
||||
cmd := models.DeletePlaylistCommand{UID: uid, OrgId: c.OrgId}
|
||||
if err := hs.SQLStore.DeletePlaylist(c.Req.Context(), &cmd); err != nil {
|
||||
cmd := playlist.DeletePlaylistCommand{UID: uid, OrgId: c.OrgId}
|
||||
if err := hs.playlistService.Delete(c.Req.Context(), &cmd); err != nil {
|
||||
return response.Error(500, "Failed to delete playlist", err)
|
||||
}
|
||||
|
||||
@ -141,28 +144,30 @@ func (hs *HTTPServer) DeletePlaylist(c *models.ReqContext) response.Response {
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) CreatePlaylist(c *models.ReqContext) response.Response {
|
||||
cmd := models.CreatePlaylistCommand{}
|
||||
cmd := playlist.CreatePlaylistCommand{}
|
||||
if err := web.Bind(c.Req, &cmd); err != nil {
|
||||
return response.Error(http.StatusBadRequest, "bad request data", err)
|
||||
}
|
||||
cmd.OrgId = c.OrgId
|
||||
|
||||
if err := hs.SQLStore.CreatePlaylist(c.Req.Context(), &cmd); err != nil {
|
||||
p, err := hs.playlistService.Create(c.Req.Context(), &cmd)
|
||||
if err != nil {
|
||||
return response.Error(500, "Failed to create playlist", err)
|
||||
}
|
||||
|
||||
return response.JSON(http.StatusOK, cmd.Result)
|
||||
return response.JSON(http.StatusOK, p)
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) UpdatePlaylist(c *models.ReqContext) response.Response {
|
||||
cmd := models.UpdatePlaylistCommand{}
|
||||
cmd := playlist.UpdatePlaylistCommand{}
|
||||
if err := web.Bind(c.Req, &cmd); err != nil {
|
||||
return response.Error(http.StatusBadRequest, "bad request data", err)
|
||||
}
|
||||
cmd.OrgId = c.OrgId
|
||||
cmd.UID = web.Params(c.Req)[":uid"]
|
||||
|
||||
if err := hs.SQLStore.UpdatePlaylist(c.Req.Context(), &cmd); err != nil {
|
||||
p, err := hs.playlistService.Update(c.Req.Context(), &cmd)
|
||||
if err != nil {
|
||||
return response.Error(500, "Failed to save playlist", err)
|
||||
}
|
||||
|
||||
@ -171,6 +176,6 @@ func (hs *HTTPServer) UpdatePlaylist(c *models.ReqContext) response.Response {
|
||||
return response.Error(500, "Failed to save playlist", err)
|
||||
}
|
||||
|
||||
cmd.Result.Items = playlistDTOs
|
||||
return response.JSON(http.StatusOK, cmd.Result)
|
||||
p.Items = playlistDTOs
|
||||
return response.JSON(http.StatusOK, p)
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ package server
|
||||
import (
|
||||
"github.com/google/wire"
|
||||
sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
|
||||
"github.com/grafana/grafana/pkg/services/playlist/playlistimpl"
|
||||
"github.com/grafana/grafana/pkg/services/store/sanitizer"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api"
|
||||
@ -286,6 +287,7 @@ var wireBasicSet = wire.NewSet(
|
||||
ossaccesscontrol.ProvideDashboardPermissions,
|
||||
wire.Bind(new(accesscontrol.DashboardPermissionsService), new(*ossaccesscontrol.DashboardPermissionsService)),
|
||||
starimpl.ProvideService,
|
||||
playlistimpl.ProvideService,
|
||||
dashverimpl.ProvideService,
|
||||
publicdashboardsService.ProvideService,
|
||||
wire.Bind(new(publicdashboards.Service), new(*publicdashboardsService.PublicDashboardServiceImpl)),
|
||||
|
95
pkg/services/playlist/model.go
Normal file
95
pkg/services/playlist/model.go
Normal file
@ -0,0 +1,95 @@
|
||||
package playlist
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Typed errors
|
||||
var (
|
||||
ErrPlaylistNotFound = errors.New("Playlist not found")
|
||||
ErrPlaylistFailedGenerateUniqueUid = errors.New("failed to generate unique playlist UID")
|
||||
ErrCommandValidationFailed = errors.New("command missing required fields")
|
||||
)
|
||||
|
||||
// Playlist model
|
||||
type Playlist struct {
|
||||
Id int64 `json:"id"`
|
||||
UID string `json:"uid" xorm:"uid"`
|
||||
Name string `json:"name"`
|
||||
Interval string `json:"interval"`
|
||||
OrgId int64 `json:"-"`
|
||||
}
|
||||
|
||||
type PlaylistDTO struct {
|
||||
Id int64 `json:"id"`
|
||||
UID string `json:"uid"`
|
||||
Name string `json:"name"`
|
||||
Interval string `json:"interval"`
|
||||
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 PlaylistItem struct {
|
||||
Id int64
|
||||
PlaylistId int64
|
||||
Type string
|
||||
Value string
|
||||
Order int
|
||||
Title string
|
||||
}
|
||||
|
||||
type Playlists []*Playlist
|
||||
|
||||
//
|
||||
// COMMANDS
|
||||
//
|
||||
|
||||
type UpdatePlaylistCommand struct {
|
||||
OrgId int64 `json:"-"`
|
||||
UID string `json:"uid"`
|
||||
Name string `json:"name" binding:"Required"`
|
||||
Interval string `json:"interval"`
|
||||
Items []PlaylistItemDTO `json:"items"`
|
||||
}
|
||||
|
||||
type CreatePlaylistCommand struct {
|
||||
Name string `json:"name" binding:"Required"`
|
||||
Interval string `json:"interval"`
|
||||
Items []PlaylistItemDTO `json:"items"`
|
||||
|
||||
OrgId int64 `json:"-"`
|
||||
}
|
||||
|
||||
type DeletePlaylistCommand struct {
|
||||
UID string
|
||||
OrgId int64
|
||||
}
|
||||
|
||||
//
|
||||
// QUERIES
|
||||
//
|
||||
|
||||
type GetPlaylistsQuery struct {
|
||||
Name string
|
||||
Limit int
|
||||
OrgId int64
|
||||
}
|
||||
|
||||
type GetPlaylistByUidQuery struct {
|
||||
UID string
|
||||
OrgId int64
|
||||
}
|
||||
|
||||
type GetPlaylistItemsByUidQuery struct {
|
||||
PlaylistUID string
|
||||
OrgId int64
|
||||
}
|
14
pkg/services/playlist/playlist.go
Normal file
14
pkg/services/playlist/playlist.go
Normal file
@ -0,0 +1,14 @@
|
||||
package playlist
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type Service interface {
|
||||
Create(context.Context, *CreatePlaylistCommand) (*Playlist, error)
|
||||
Update(context.Context, *UpdatePlaylistCommand) (*PlaylistDTO, error)
|
||||
Get(context.Context, *GetPlaylistByUidQuery) (*Playlist, error)
|
||||
GetItems(context.Context, *GetPlaylistItemsByUidQuery) ([]PlaylistItem, error)
|
||||
Search(context.Context, *GetPlaylistsQuery) (Playlists, error)
|
||||
Delete(ctx context.Context, cmd *DeletePlaylistCommand) error
|
||||
}
|
44
pkg/services/playlist/playlistimpl/playlist.go
Normal file
44
pkg/services/playlist/playlistimpl/playlist.go
Normal file
@ -0,0 +1,44 @@
|
||||
package playlistimpl
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/playlist"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/db"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
store store
|
||||
}
|
||||
|
||||
func ProvideService(db db.DB) playlist.Service {
|
||||
return &Service{
|
||||
store: &sqlStore{
|
||||
db: db,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) Create(ctx context.Context, cmd *playlist.CreatePlaylistCommand) (*playlist.Playlist, error) {
|
||||
return s.store.Insert(ctx, cmd)
|
||||
}
|
||||
|
||||
func (s *Service) Update(ctx context.Context, cmd *playlist.UpdatePlaylistCommand) (*playlist.PlaylistDTO, error) {
|
||||
return s.store.Update(ctx, cmd)
|
||||
}
|
||||
|
||||
func (s *Service) Get(ctx context.Context, q *playlist.GetPlaylistByUidQuery) (*playlist.Playlist, error) {
|
||||
return s.store.Get(ctx, q)
|
||||
}
|
||||
|
||||
func (s *Service) GetItems(ctx context.Context, q *playlist.GetPlaylistItemsByUidQuery) ([]playlist.PlaylistItem, error) {
|
||||
return s.store.GetItems(ctx, q)
|
||||
}
|
||||
|
||||
func (s *Service) Search(ctx context.Context, q *playlist.GetPlaylistsQuery) (playlist.Playlists, error) {
|
||||
return s.store.List(ctx, q)
|
||||
}
|
||||
|
||||
func (s *Service) Delete(ctx context.Context, cmd *playlist.DeletePlaylistCommand) error {
|
||||
return s.store.Delete(ctx, cmd)
|
||||
}
|
226
pkg/services/playlist/playlistimpl/store.go
Normal file
226
pkg/services/playlist/playlistimpl/store.go
Normal file
@ -0,0 +1,226 @@
|
||||
package playlistimpl
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/playlist"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/db"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
type store interface {
|
||||
Insert(context.Context, *playlist.CreatePlaylistCommand) (*playlist.Playlist, error)
|
||||
Delete(context.Context, *playlist.DeletePlaylistCommand) error
|
||||
Get(context.Context, *playlist.GetPlaylistByUidQuery) (*playlist.Playlist, error)
|
||||
GetItems(context.Context, *playlist.GetPlaylistItemsByUidQuery) ([]playlist.PlaylistItem, error)
|
||||
List(context.Context, *playlist.GetPlaylistsQuery) (playlist.Playlists, error)
|
||||
Update(context.Context, *playlist.UpdatePlaylistCommand) (*playlist.PlaylistDTO, error)
|
||||
}
|
||||
|
||||
type sqlStore struct {
|
||||
db db.DB
|
||||
}
|
||||
|
||||
func (s *sqlStore) Insert(ctx context.Context, cmd *playlist.CreatePlaylistCommand) (*playlist.Playlist, error) {
|
||||
p := playlist.Playlist{}
|
||||
err := s.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||
uid, err := generateAndValidateNewPlaylistUid(sess, cmd.OrgId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p = playlist.Playlist{
|
||||
Name: cmd.Name,
|
||||
Interval: cmd.Interval,
|
||||
OrgId: cmd.OrgId,
|
||||
UID: uid,
|
||||
}
|
||||
|
||||
_, err = sess.Insert(&p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
playlistItems := make([]playlist.PlaylistItem, 0)
|
||||
for _, item := range cmd.Items {
|
||||
playlistItems = append(playlistItems, playlist.PlaylistItem{
|
||||
PlaylistId: p.Id,
|
||||
Type: item.Type,
|
||||
Value: item.Value,
|
||||
Order: item.Order,
|
||||
Title: item.Title,
|
||||
})
|
||||
}
|
||||
|
||||
_, err = sess.Insert(&playlistItems)
|
||||
|
||||
return err
|
||||
})
|
||||
return &p, err
|
||||
}
|
||||
|
||||
func (s *sqlStore) Update(ctx context.Context, cmd *playlist.UpdatePlaylistCommand) (*playlist.PlaylistDTO, error) {
|
||||
dto := playlist.PlaylistDTO{}
|
||||
err := s.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||
p := playlist.Playlist{
|
||||
UID: cmd.UID,
|
||||
OrgId: cmd.OrgId,
|
||||
Name: cmd.Name,
|
||||
Interval: cmd.Interval,
|
||||
}
|
||||
|
||||
existingPlaylist := playlist.Playlist{UID: cmd.UID, OrgId: cmd.OrgId}
|
||||
_, err := sess.Get(&existingPlaylist)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.Id = existingPlaylist.Id
|
||||
|
||||
dto = playlist.PlaylistDTO{
|
||||
|
||||
Id: p.Id,
|
||||
UID: p.UID,
|
||||
OrgId: p.OrgId,
|
||||
Name: p.Name,
|
||||
Interval: p.Interval,
|
||||
}
|
||||
|
||||
_, err = sess.Where("id=?", p.Id).Cols("name", "interval").Update(&p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rawSQL := "DELETE FROM playlist_item WHERE playlist_id = ?"
|
||||
_, err = sess.Exec(rawSQL, p.Id)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
playlistItems := make([]models.PlaylistItem, 0)
|
||||
|
||||
for index, item := range cmd.Items {
|
||||
playlistItems = append(playlistItems, models.PlaylistItem{
|
||||
PlaylistId: p.Id,
|
||||
Type: item.Type,
|
||||
Value: item.Value,
|
||||
Order: index + 1,
|
||||
Title: item.Title,
|
||||
})
|
||||
}
|
||||
|
||||
_, err = sess.Insert(&playlistItems)
|
||||
return err
|
||||
})
|
||||
return &dto, err
|
||||
}
|
||||
|
||||
func (s *sqlStore) Get(ctx context.Context, query *playlist.GetPlaylistByUidQuery) (*playlist.Playlist, error) {
|
||||
if query.UID == "" || query.OrgId == 0 {
|
||||
return nil, playlist.ErrCommandValidationFailed
|
||||
}
|
||||
|
||||
p := playlist.Playlist{}
|
||||
err := s.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||
p = playlist.Playlist{UID: query.UID, OrgId: query.OrgId}
|
||||
exists, err := sess.Get(&p)
|
||||
if !exists {
|
||||
return playlist.ErrPlaylistNotFound
|
||||
}
|
||||
|
||||
return err
|
||||
})
|
||||
return &p, err
|
||||
}
|
||||
|
||||
func (s *sqlStore) Delete(ctx context.Context, cmd *playlist.DeletePlaylistCommand) error {
|
||||
if cmd.UID == "" || cmd.OrgId == 0 {
|
||||
return playlist.ErrCommandValidationFailed
|
||||
}
|
||||
|
||||
return s.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||
playlist := playlist.Playlist{UID: cmd.UID, OrgId: cmd.OrgId}
|
||||
_, err := sess.Get(&playlist)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var rawPlaylistSQL = "DELETE FROM playlist WHERE uid = ? and org_id = ?"
|
||||
_, err = sess.Exec(rawPlaylistSQL, cmd.UID, cmd.OrgId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var rawItemSQL = "DELETE FROM playlist_item WHERE playlist_id = ?"
|
||||
_, err = sess.Exec(rawItemSQL, playlist.Id)
|
||||
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (s *sqlStore) List(ctx context.Context, query *playlist.GetPlaylistsQuery) (playlist.Playlists, error) {
|
||||
playlists := make(playlist.Playlists, 0)
|
||||
if query.OrgId == 0 {
|
||||
return playlists, playlist.ErrCommandValidationFailed
|
||||
}
|
||||
|
||||
err := s.db.WithDbSession(ctx, func(dbSess *sqlstore.DBSession) error {
|
||||
sess := dbSess.Limit(query.Limit)
|
||||
|
||||
if query.Name != "" {
|
||||
sess.Where("name LIKE ?", "%"+query.Name+"%")
|
||||
}
|
||||
|
||||
sess.Where("org_id = ?", query.OrgId)
|
||||
err := sess.Find(&playlists)
|
||||
|
||||
return err
|
||||
})
|
||||
return playlists, err
|
||||
}
|
||||
|
||||
func (s *sqlStore) GetItems(ctx context.Context, query *playlist.GetPlaylistItemsByUidQuery) ([]playlist.PlaylistItem, error) {
|
||||
var playlistItems = make([]playlist.PlaylistItem, 0)
|
||||
err := s.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||
if query.PlaylistUID == "" || query.OrgId == 0 {
|
||||
return models.ErrCommandValidationFailed
|
||||
}
|
||||
|
||||
// getQuery the playlist Id
|
||||
getQuery := &playlist.GetPlaylistByUidQuery{UID: query.PlaylistUID, OrgId: query.OrgId}
|
||||
p, err := s.Get(ctx, getQuery)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = sess.Where("playlist_id=?", p.Id).Find(&playlistItems)
|
||||
|
||||
return err
|
||||
})
|
||||
return playlistItems, err
|
||||
}
|
||||
|
||||
// generateAndValidateNewPlaylistUid generates a playlistUID and verifies that
|
||||
// the uid isn't already in use. This is deliberately overly cautious, since users
|
||||
// can also specify playlist uids during provisioning.
|
||||
func generateAndValidateNewPlaylistUid(sess *sqlstore.DBSession, orgId int64) (string, error) {
|
||||
for i := 0; i < 3; i++ {
|
||||
uid := generateNewUid()
|
||||
|
||||
playlist := models.Playlist{OrgId: orgId, UID: uid}
|
||||
exists, err := sess.Get(&playlist)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return uid, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", models.ErrPlaylistFailedGenerateUniqueUid
|
||||
}
|
||||
|
||||
var generateNewUid func() string = util.GenerateShortUID
|
82
pkg/services/playlist/playlistimpl/store_test.go
Normal file
82
pkg/services/playlist/playlistimpl/store_test.go
Normal file
@ -0,0 +1,82 @@
|
||||
package playlistimpl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/playlist"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestIntegrationPlaylistDataAccess(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
ss := sqlstore.InitTestDB(t)
|
||||
playlistStore := sqlStore{db: ss}
|
||||
|
||||
t.Run("Can create playlist", func(t *testing.T) {
|
||||
items := []playlist.PlaylistItemDTO{
|
||||
{Title: "graphite", Value: "graphite", Type: "dashboard_by_tag"},
|
||||
{Title: "Backend response times", Value: "3", Type: "dashboard_by_id"},
|
||||
}
|
||||
cmd := playlist.CreatePlaylistCommand{Name: "NYC office", Interval: "10m", OrgId: 1, Items: items}
|
||||
p, err := playlistStore.Insert(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
uid := p.UID
|
||||
|
||||
t.Run("Can get playlist items", func(t *testing.T) {
|
||||
get := &playlist.GetPlaylistItemsByUidQuery{PlaylistUID: uid, OrgId: 1}
|
||||
storedPlaylistItems, err := playlistStore.GetItems(context.Background(), get)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(storedPlaylistItems), len(items))
|
||||
})
|
||||
|
||||
t.Run("Can update playlist", func(t *testing.T) {
|
||||
items := []playlist.PlaylistItemDTO{
|
||||
{Title: "influxdb", Value: "influxdb", Type: "dashboard_by_tag"},
|
||||
{Title: "Backend response times", Value: "2", Type: "dashboard_by_id"},
|
||||
}
|
||||
query := playlist.UpdatePlaylistCommand{Name: "NYC office ", OrgId: 1, UID: uid, Interval: "10s", Items: items}
|
||||
_, err = playlistStore.Update(context.Background(), &query)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("Can remove playlist", func(t *testing.T) {
|
||||
deleteQuery := playlist.DeletePlaylistCommand{UID: uid, OrgId: 1}
|
||||
err = playlistStore.Delete(context.Background(), &deleteQuery)
|
||||
require.NoError(t, err)
|
||||
|
||||
getQuery := playlist.GetPlaylistByUidQuery{UID: uid, OrgId: 1}
|
||||
p, err := playlistStore.Get(context.Background(), &getQuery)
|
||||
require.Error(t, err)
|
||||
require.Equal(t, uid, p.UID, "playlist should've been removed")
|
||||
require.ErrorIs(t, err, playlist.ErrPlaylistNotFound)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Delete playlist that doesn't exist", func(t *testing.T) {
|
||||
deleteQuery := playlist.DeletePlaylistCommand{UID: "654312", OrgId: 1}
|
||||
err := playlistStore.Delete(context.Background(), &deleteQuery)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("Delete playlist with invalid command yields error", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
cmd playlist.DeletePlaylistCommand
|
||||
}{
|
||||
{desc: "none", cmd: playlist.DeletePlaylistCommand{}},
|
||||
{desc: "no OrgId", cmd: playlist.DeletePlaylistCommand{UID: "1"}},
|
||||
{desc: "no Uid", cmd: playlist.DeletePlaylistCommand{OrgId: 1}},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
err := playlistStore.Delete(context.Background(), &tc.cmd)
|
||||
require.EqualError(t, err, playlist.ErrCommandValidationFailed.Error())
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
43
pkg/services/playlist/playlisttest/fake.go
Normal file
43
pkg/services/playlist/playlisttest/fake.go
Normal file
@ -0,0 +1,43 @@
|
||||
package playlisttest
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/playlist"
|
||||
)
|
||||
|
||||
type FakePlaylistService struct {
|
||||
ExpectedPlaylist *playlist.Playlist
|
||||
ExpectedPlaylistDTO *playlist.PlaylistDTO
|
||||
ExpectedPlaylistItems []playlist.PlaylistItem
|
||||
ExpectedPlaylists playlist.Playlists
|
||||
ExpectedError error
|
||||
}
|
||||
|
||||
func NewPlaylistServiveFake() *FakePlaylistService {
|
||||
return &FakePlaylistService{}
|
||||
}
|
||||
|
||||
func (f *FakePlaylistService) Create(context.Context, *playlist.CreatePlaylistCommand) (*playlist.Playlist, error) {
|
||||
return f.ExpectedPlaylist, f.ExpectedError
|
||||
}
|
||||
|
||||
func (f *FakePlaylistService) Update(context.Context, *playlist.UpdatePlaylistCommand) (*playlist.PlaylistDTO, error) {
|
||||
return f.ExpectedPlaylistDTO, f.ExpectedError
|
||||
}
|
||||
|
||||
func (f *FakePlaylistService) Get(context.Context, *playlist.GetPlaylistByUidQuery) (*playlist.Playlist, error) {
|
||||
return f.ExpectedPlaylist, f.ExpectedError
|
||||
}
|
||||
|
||||
func (f *FakePlaylistService) GetItems(context.Context, *playlist.GetPlaylistItemsByUidQuery) ([]playlist.PlaylistItem, error) {
|
||||
return f.ExpectedPlaylistItems, f.ExpectedError
|
||||
}
|
||||
|
||||
func (f *FakePlaylistService) Search(context.Context, *playlist.GetPlaylistsQuery) (playlist.Playlists, error) {
|
||||
return f.ExpectedPlaylists, f.ExpectedError
|
||||
}
|
||||
|
||||
func (f *FakePlaylistService) Delete(ctx context.Context, cmd *playlist.DeletePlaylistCommand) error {
|
||||
return f.ExpectedError
|
||||
}
|
@ -72,11 +72,17 @@ type Store interface {
|
||||
GetGlobalQuotaByTarget(ctx context.Context, query *models.GetGlobalQuotaByTargetQuery) error
|
||||
WithTransactionalDbSession(ctx context.Context, callback DBTransactionFunc) error
|
||||
InTransaction(ctx context.Context, fn func(ctx context.Context) error) error
|
||||
// deprecated
|
||||
CreatePlaylist(ctx context.Context, cmd *models.CreatePlaylistCommand) error
|
||||
// deprecated
|
||||
UpdatePlaylist(ctx context.Context, cmd *models.UpdatePlaylistCommand) error
|
||||
// deprecated
|
||||
GetPlaylist(ctx context.Context, query *models.GetPlaylistByUidQuery) error
|
||||
// deprecated
|
||||
DeletePlaylist(ctx context.Context, cmd *models.DeletePlaylistCommand) error
|
||||
// deprecated
|
||||
SearchPlaylists(ctx context.Context, query *models.GetPlaylistsQuery) error
|
||||
// deprecated
|
||||
GetPlaylistItem(ctx context.Context, query *models.GetPlaylistItemsByUidQuery) error
|
||||
GetAlertById(ctx context.Context, query *models.GetAlertByIdQuery) error
|
||||
GetAllAlertQueryHandler(ctx context.Context, query *models.GetAllAlertsQuery) error
|
||||
|
@ -8535,6 +8535,14 @@
|
||||
"tags": ["provisioning"],
|
||||
"summary": "Get all the contact points.",
|
||||
"operationId": "RouteGetContactpoints",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Filter by name",
|
||||
"name": "name",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "ContactPoints",
|
||||
@ -8874,6 +8882,20 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"consumes": ["application/json"],
|
||||
"tags": ["provisioning"],
|
||||
"summary": "Clears the notification policy tree.",
|
||||
"operationId": "RouteResetPolicyTree",
|
||||
"responses": {
|
||||
"202": {
|
||||
"description": "Ack",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/Ack"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/provisioning/templates": {
|
||||
@ -10250,7 +10272,7 @@
|
||||
"$ref": "#/definitions/ScheduleDTO"
|
||||
},
|
||||
"state": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/State"
|
||||
},
|
||||
"templateVars": {
|
||||
"type": "object"
|
||||
@ -10268,9 +10290,6 @@
|
||||
"CreatePlaylistCommand": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Result": {
|
||||
"$ref": "#/definitions/Playlist"
|
||||
},
|
||||
"interval": {
|
||||
"type": "string"
|
||||
},
|
||||
@ -15496,9 +15515,8 @@
|
||||
"type": "string"
|
||||
},
|
||||
"URL": {
|
||||
"description": "The general form represented is:\n\n[scheme:][//[userinfo@]host][/]path[?query][#fragment]\n\nURLs that do not start with a slash after the scheme are interpreted as:\n\nscheme:opaque[?query][#fragment]\n\nNote that the Path field is stored in decoded form: /%47%6f%2f becomes /Go/.\nA consequence is that it is impossible to tell which slashes in the Path were\nslashes in the raw URL and which were %2f. This distinction is rarely important,\nbut when it is, the code should use RawPath, an optional field which only gets\nset if the default encoding is different from Path.\n\nURL's String method uses the EscapedPath method to obtain the path. See the\nEscapedPath method for more details.",
|
||||
"type": "object",
|
||||
"title": "A URL represents a parsed URL (technically, a URI reference).",
|
||||
"title": "URL is a custom URL type that allows validation at configuration load time.",
|
||||
"properties": {
|
||||
"ForceQuery": {
|
||||
"type": "boolean"
|
||||
@ -15768,9 +15786,6 @@
|
||||
"UpdatePlaylistCommand": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Result": {
|
||||
"$ref": "#/definitions/PlaylistDTO"
|
||||
},
|
||||
"interval": {
|
||||
"type": "string"
|
||||
},
|
||||
@ -16298,6 +16313,7 @@
|
||||
}
|
||||
},
|
||||
"alertGroup": {
|
||||
"description": "AlertGroup alert group",
|
||||
"type": "object",
|
||||
"required": ["alerts", "labels", "receiver"],
|
||||
"properties": {
|
||||
@ -16461,6 +16477,7 @@
|
||||
}
|
||||
},
|
||||
"gettableSilence": {
|
||||
"description": "GettableSilence gettable silence",
|
||||
"type": "object",
|
||||
"required": ["comment", "createdBy", "endsAt", "matchers", "startsAt", "id", "status", "updatedAt"],
|
||||
"properties": {
|
||||
@ -16500,7 +16517,6 @@
|
||||
}
|
||||
},
|
||||
"gettableSilences": {
|
||||
"description": "GettableSilences gettable silences",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/gettableSilence"
|
||||
@ -16634,7 +16650,6 @@
|
||||
}
|
||||
},
|
||||
"receiver": {
|
||||
"description": "Receiver receiver",
|
||||
"type": "object",
|
||||
"required": ["name"],
|
||||
"properties": {
|
||||
|
@ -9380,7 +9380,7 @@
|
||||
"$ref": "#/definitions/ScheduleDTO"
|
||||
},
|
||||
"state": {
|
||||
"type": "string"
|
||||
"$ref": "#/definitions/State"
|
||||
},
|
||||
"templateVars": {
|
||||
"type": "object"
|
||||
@ -9398,9 +9398,6 @@
|
||||
"CreatePlaylistCommand": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Result": {
|
||||
"$ref": "#/definitions/Playlist"
|
||||
},
|
||||
"interval": {
|
||||
"type": "string"
|
||||
},
|
||||
@ -12676,9 +12673,6 @@
|
||||
"UpdatePlaylistCommand": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Result": {
|
||||
"$ref": "#/definitions/PlaylistDTO"
|
||||
},
|
||||
"interval": {
|
||||
"type": "string"
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user