2022-10-26 10:52:01 -05:00
|
|
|
package folderimpl
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2022-11-03 08:21:41 -05:00
|
|
|
"strings"
|
|
|
|
"time"
|
2022-10-26 10:52:01 -05:00
|
|
|
|
|
|
|
"github.com/grafana/grafana/pkg/infra/db"
|
|
|
|
"github.com/grafana/grafana/pkg/infra/log"
|
|
|
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
|
|
|
"github.com/grafana/grafana/pkg/services/folder"
|
2022-10-28 13:07:25 -05:00
|
|
|
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
2022-10-26 10:52:01 -05:00
|
|
|
"github.com/grafana/grafana/pkg/setting"
|
2022-11-03 08:21:41 -05:00
|
|
|
"github.com/grafana/grafana/pkg/util"
|
2022-10-26 10:52:01 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
type sqlStore struct {
|
|
|
|
db db.DB
|
|
|
|
log log.Logger
|
|
|
|
cfg *setting.Cfg
|
|
|
|
fm featuremgmt.FeatureManager
|
|
|
|
}
|
|
|
|
|
|
|
|
// sqlStore implements the store interface.
|
|
|
|
var _ store = (*sqlStore)(nil)
|
|
|
|
|
|
|
|
func ProvideStore(db db.DB, cfg *setting.Cfg, features featuremgmt.FeatureManager) *sqlStore {
|
|
|
|
return &sqlStore{db: db, log: log.New("folder-store"), cfg: cfg, fm: features}
|
|
|
|
}
|
|
|
|
|
2022-11-03 08:21:41 -05:00
|
|
|
func (ss *sqlStore) Create(ctx context.Context, cmd folder.CreateFolderCommand) (*folder.Folder, error) {
|
|
|
|
if cmd.UID == "" {
|
|
|
|
return nil, folder.ErrBadRequest.Errorf("missing UID")
|
2022-10-28 13:07:25 -05:00
|
|
|
}
|
2022-11-03 08:21:41 -05:00
|
|
|
|
|
|
|
var foldr *folder.Folder
|
2022-10-28 13:07:25 -05:00
|
|
|
err := ss.db.WithDbSession(ctx, func(sess *db.Session) error {
|
2022-11-03 08:21:41 -05:00
|
|
|
var sqlOrArgs []interface{}
|
|
|
|
if cmd.ParentUID == "" {
|
|
|
|
sql := "INSERT INTO folder(org_id, uid, title, description, created, updated) VALUES(?, ?, ?, ?, ?, ?)"
|
|
|
|
sqlOrArgs = []interface{}{sql, cmd.OrgID, cmd.UID, cmd.Title, cmd.Description, time.Now(), time.Now()}
|
|
|
|
} else {
|
|
|
|
if cmd.ParentUID != folder.GeneralFolderUID {
|
|
|
|
if _, err := ss.Get(ctx, folder.GetFolderQuery{
|
|
|
|
UID: &cmd.ParentUID,
|
|
|
|
OrgID: cmd.OrgID,
|
|
|
|
}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sql := "INSERT INTO folder(org_id, uid, parent_uid, title, description, created, updated) VALUES(?, ?, ?, ?, ?, ?, ?)"
|
|
|
|
sqlOrArgs = []interface{}{sql, cmd.OrgID, cmd.UID, cmd.ParentUID, cmd.Title, cmd.Description, time.Now(), time.Now()}
|
|
|
|
}
|
|
|
|
res, err := sess.Exec(sqlOrArgs...)
|
|
|
|
if err != nil {
|
|
|
|
return folder.ErrDatabaseError.Errorf("failed to insert folder: %w", err)
|
|
|
|
}
|
|
|
|
id, err := res.LastInsertId()
|
|
|
|
if err != nil {
|
|
|
|
return folder.ErrDatabaseError.Errorf("failed to get last inserted id: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
foldr, err = ss.Get(ctx, folder.GetFolderQuery{
|
|
|
|
ID: &id,
|
|
|
|
})
|
2022-10-28 13:07:25 -05:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
return foldr, err
|
2022-10-26 10:52:01 -05:00
|
|
|
}
|
|
|
|
|
2022-10-28 08:35:49 -05:00
|
|
|
func (ss *sqlStore) Delete(ctx context.Context, uid string, orgID int64) error {
|
|
|
|
return ss.db.WithDbSession(ctx, func(sess *db.Session) error {
|
2022-11-03 08:21:41 -05:00
|
|
|
_, err := sess.Exec("DELETE FROM folder WHERE uid=? AND org_id=?", uid, orgID)
|
|
|
|
if err != nil {
|
|
|
|
return folder.ErrDatabaseError.Errorf("failed to delete folder: %w", err)
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
affected, err := res.RowsAffected()
|
|
|
|
if err != nil {
|
|
|
|
return folder.ErrDatabaseError.Errorf("failed to get affected rows: %w", err)
|
|
|
|
}
|
|
|
|
if affected == 0 {
|
|
|
|
return folder.ErrFolderNotFound.Errorf("folder not found uid:%s org_id:%d", uid, orgID)
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
return nil
|
2022-10-28 08:35:49 -05:00
|
|
|
})
|
2022-10-26 10:52:01 -05:00
|
|
|
}
|
|
|
|
|
2022-11-03 08:21:41 -05:00
|
|
|
func (ss *sqlStore) Update(ctx context.Context, cmd folder.UpdateFolderCommand) (*folder.Folder, error) {
|
|
|
|
if cmd.Folder == nil {
|
|
|
|
return nil, folder.ErrBadRequest.Errorf("invalid update command: missing folder")
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd.Folder.Updated = time.Now()
|
2022-11-04 04:04:24 -05:00
|
|
|
existingUID := cmd.Folder.UID
|
2022-10-28 08:35:49 -05:00
|
|
|
err := ss.db.WithDbSession(ctx, func(sess *db.Session) error {
|
2022-11-04 04:04:24 -05:00
|
|
|
sql := strings.Builder{}
|
|
|
|
sql.Write([]byte("UPDATE folder SET "))
|
|
|
|
columnsToUpdate := []string{"updated = ?"}
|
|
|
|
args := []interface{}{cmd.Folder.Updated}
|
2022-11-03 08:21:41 -05:00
|
|
|
if cmd.NewDescription != nil {
|
2022-11-04 04:04:24 -05:00
|
|
|
columnsToUpdate = append(columnsToUpdate, "description = ?")
|
|
|
|
cmd.Folder.Description = *cmd.NewDescription
|
|
|
|
args = append(args, cmd.Folder.Description)
|
2022-11-03 08:21:41 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if cmd.NewTitle != nil {
|
2022-11-04 04:04:24 -05:00
|
|
|
columnsToUpdate = append(columnsToUpdate, "title = ?")
|
|
|
|
cmd.Folder.Title = *cmd.NewTitle
|
|
|
|
args = append(args, cmd.Folder.Title)
|
2022-11-03 08:21:41 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if cmd.NewUID != nil {
|
2022-11-04 04:04:24 -05:00
|
|
|
columnsToUpdate = append(columnsToUpdate, "uid = ?")
|
|
|
|
cmd.Folder.UID = *cmd.NewUID
|
|
|
|
args = append(args, cmd.Folder.UID)
|
2022-11-03 08:21:41 -05:00
|
|
|
}
|
|
|
|
|
2022-11-08 04:33:13 -06:00
|
|
|
if cmd.NewParentUID != nil {
|
|
|
|
columnsToUpdate = append(columnsToUpdate, "parent_uid = ?")
|
|
|
|
cmd.Folder.ParentUID = *cmd.NewParentUID
|
|
|
|
args = append(args, cmd.Folder.UID)
|
|
|
|
}
|
|
|
|
|
2022-11-04 04:04:24 -05:00
|
|
|
if len(columnsToUpdate) == 0 {
|
|
|
|
return folder.ErrBadRequest.Errorf("no columns to update")
|
|
|
|
}
|
|
|
|
|
|
|
|
sql.Write([]byte(strings.Join(columnsToUpdate, ", ")))
|
|
|
|
sql.Write([]byte(" WHERE uid = ? AND org_id = ?"))
|
|
|
|
args = append(args, existingUID, cmd.Folder.OrgID)
|
|
|
|
|
|
|
|
args = append([]interface{}{sql.String()}, args...)
|
|
|
|
|
|
|
|
res, err := sess.Exec(args...)
|
2022-11-03 08:21:41 -05:00
|
|
|
if err != nil {
|
|
|
|
return folder.ErrDatabaseError.Errorf("failed to update folder: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
affected, err := res.RowsAffected()
|
|
|
|
if err != nil {
|
|
|
|
return folder.ErrInternal.Errorf("failed to get affected row: %w", err)
|
|
|
|
}
|
|
|
|
if affected == 0 {
|
|
|
|
return folder.ErrInternal.Errorf("no folders are updated")
|
|
|
|
}
|
|
|
|
return nil
|
2022-10-28 08:35:49 -05:00
|
|
|
})
|
2022-10-26 10:52:01 -05:00
|
|
|
|
2022-10-28 08:35:49 -05:00
|
|
|
return cmd.Folder, err
|
2022-10-26 10:52:01 -05:00
|
|
|
}
|
|
|
|
|
2022-11-03 08:21:41 -05:00
|
|
|
func (ss *sqlStore) Get(ctx context.Context, q folder.GetFolderQuery) (*folder.Folder, error) {
|
|
|
|
foldr := &folder.Folder{}
|
2022-10-28 08:35:49 -05:00
|
|
|
err := ss.db.WithDbSession(ctx, func(sess *db.Session) error {
|
2022-11-03 08:21:41 -05:00
|
|
|
exists := false
|
|
|
|
var err error
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case q.ID != nil:
|
|
|
|
exists, err = sess.SQL("SELECT * FROM folder WHERE id = ?", q.ID).Get(foldr)
|
|
|
|
case q.Title != nil:
|
|
|
|
exists, err = sess.SQL("SELECT * FROM folder WHERE title = ? AND org_id = ?", q.Title, q.OrgID).Get(foldr)
|
|
|
|
case q.UID != nil:
|
|
|
|
exists, err = sess.SQL("SELECT * FROM folder WHERE uid = ? AND org_id = ?", q.UID, q.OrgID).Get(foldr)
|
|
|
|
default:
|
|
|
|
return folder.ErrBadRequest.Errorf("one of ID, UID, or Title must be included in the command")
|
|
|
|
}
|
2022-10-28 08:35:49 -05:00
|
|
|
if err != nil {
|
2022-11-03 08:21:41 -05:00
|
|
|
return folder.ErrDatabaseError.Errorf("failed to get folder: %w", err)
|
2022-10-28 08:35:49 -05:00
|
|
|
}
|
|
|
|
if !exists {
|
|
|
|
return folder.ErrFolderNotFound.Errorf("folder not found")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
return foldr, err
|
2022-10-26 10:52:01 -05:00
|
|
|
}
|
|
|
|
|
2022-11-03 08:21:41 -05:00
|
|
|
func (ss *sqlStore) GetParents(ctx context.Context, q folder.GetParentsQuery) ([]*folder.Folder, error) {
|
2022-10-28 13:07:25 -05:00
|
|
|
var folders []*folder.Folder
|
|
|
|
if ss.db.GetDBType() == migrator.MySQL {
|
2022-11-03 08:21:41 -05:00
|
|
|
return ss.getParentsMySQL(ctx, q)
|
2022-10-28 13:07:25 -05:00
|
|
|
}
|
|
|
|
|
2022-11-03 08:21:41 -05:00
|
|
|
recQuery := `
|
|
|
|
WITH RECURSIVE RecQry AS (
|
|
|
|
SELECT * FROM folder WHERE uid = ? AND org_id = ?
|
|
|
|
UNION ALL SELECT f.* FROM folder f INNER JOIN RecQry r ON f.uid = r.parent_uid and f.org_id = r.org_id
|
2022-10-28 13:07:25 -05:00
|
|
|
)
|
2022-11-03 08:21:41 -05:00
|
|
|
SELECT * FROM RecQry;
|
|
|
|
`
|
2022-10-28 13:07:25 -05:00
|
|
|
|
|
|
|
err := ss.db.WithDbSession(ctx, func(sess *db.Session) error {
|
2022-11-03 08:21:41 -05:00
|
|
|
err := sess.SQL(recQuery, q.UID, q.OrgID).Find(&folders)
|
2022-10-28 13:07:25 -05:00
|
|
|
if err != nil {
|
2022-11-03 08:21:41 -05:00
|
|
|
return folder.ErrDatabaseError.Errorf("failed to get folder parents: %w", err)
|
2022-10-28 13:07:25 -05:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
2022-11-03 08:21:41 -05:00
|
|
|
return util.Reverse(folders[1:]), err
|
2022-10-26 10:52:01 -05:00
|
|
|
}
|
|
|
|
|
2022-11-03 08:21:41 -05:00
|
|
|
func (ss *sqlStore) GetChildren(ctx context.Context, q folder.GetTreeQuery) ([]*folder.Folder, error) {
|
2022-10-28 08:35:49 -05:00
|
|
|
var folders []*folder.Folder
|
2022-10-28 13:07:25 -05:00
|
|
|
|
2022-10-28 08:35:49 -05:00
|
|
|
err := ss.db.WithDbSession(ctx, func(sess *db.Session) error {
|
2022-11-03 08:21:41 -05:00
|
|
|
sql := strings.Builder{}
|
|
|
|
sql.Write([]byte("SELECT * FROM folder WHERE parent_uid=? AND org_id=?"))
|
|
|
|
|
|
|
|
if q.Limit != 0 {
|
|
|
|
var offset int64 = 1
|
|
|
|
if q.Page != 0 {
|
|
|
|
offset = q.Page
|
|
|
|
}
|
|
|
|
sql.Write([]byte(ss.db.GetDialect().LimitOffset(q.Limit, offset)))
|
|
|
|
}
|
|
|
|
err := sess.SQL(sql.String(), q.UID, q.OrgID).Find(&folders)
|
|
|
|
if err != nil {
|
|
|
|
return folder.ErrDatabaseError.Errorf("failed to get folder children: %w", err)
|
|
|
|
}
|
|
|
|
return nil
|
2022-10-28 08:35:49 -05:00
|
|
|
})
|
|
|
|
return folders, err
|
2022-10-26 10:52:01 -05:00
|
|
|
}
|
2022-10-28 13:07:25 -05:00
|
|
|
|
2022-11-03 08:21:41 -05:00
|
|
|
func (ss *sqlStore) getParentsMySQL(ctx context.Context, cmd folder.GetParentsQuery) ([]*folder.Folder, error) {
|
2022-10-28 13:07:25 -05:00
|
|
|
var foldrs []*folder.Folder
|
|
|
|
var foldr *folder.Folder
|
|
|
|
err := ss.db.WithDbSession(ctx, func(sess *db.Session) error {
|
|
|
|
uid := cmd.UID
|
|
|
|
for uid != folder.GeneralFolderUID && len(foldrs) < 8 {
|
2022-11-03 08:21:41 -05:00
|
|
|
err := sess.Where("uid=? AND org_id=>", uid, cmd.OrgID).Find(foldr)
|
2022-10-28 13:07:25 -05:00
|
|
|
if err != nil {
|
2022-11-03 08:21:41 -05:00
|
|
|
return folder.ErrDatabaseError.Errorf("failed to get folder parents: %w", err)
|
2022-10-28 13:07:25 -05:00
|
|
|
}
|
|
|
|
foldrs = append(foldrs, foldr)
|
|
|
|
uid = foldr.ParentUID
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
return foldrs, err
|
|
|
|
}
|