| 
									
										
										
										
											2022-10-26 23:52:01 +08:00
										 |  |  | package folderimpl | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2022-11-03 21:21:41 +08:00
										 |  |  | 	"strings" | 
					
						
							|  |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2022-10-26 23:52:01 +08: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-29 02:07:25 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/sqlstore/migrator" | 
					
						
							| 
									
										
										
										
											2022-10-26 23:52:01 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/setting" | 
					
						
							| 
									
										
										
										
											2022-11-03 21:21:41 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/util" | 
					
						
							| 
									
										
										
										
											2022-10-26 23:52:01 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type sqlStore struct { | 
					
						
							|  |  |  | 	db  db.DB | 
					
						
							|  |  |  | 	log log.Logger | 
					
						
							|  |  |  | 	cfg *setting.Cfg | 
					
						
							| 
									
										
										
										
											2022-11-08 21:59:55 +08:00
										 |  |  | 	fm  featuremgmt.FeatureToggles | 
					
						
							| 
									
										
										
										
											2022-10-26 23:52:01 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // sqlStore implements the store interface.
 | 
					
						
							|  |  |  | var _ store = (*sqlStore)(nil) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-08 21:59:55 +08:00
										 |  |  | func ProvideStore(db db.DB, cfg *setting.Cfg, features featuremgmt.FeatureToggles) *sqlStore { | 
					
						
							| 
									
										
										
										
											2022-10-26 23:52:01 +08:00
										 |  |  | 	return &sqlStore{db: db, log: log.New("folder-store"), cfg: cfg, fm: features} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-03 21:21:41 +08: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-29 02:07:25 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-11-03 21:21:41 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	var foldr *folder.Folder | 
					
						
							| 
									
										
										
										
											2022-10-29 02:07:25 +08:00
										 |  |  | 	err := ss.db.WithDbSession(ctx, func(sess *db.Session) error { | 
					
						
							| 
									
										
										
										
											2022-11-03 21:21:41 +08: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-29 02:07:25 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	return foldr, err | 
					
						
							| 
									
										
										
										
											2022-10-26 23:52:01 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-28 21:35:49 +08: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 21:21:41 +08: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 21:35:49 +08:00
										 |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2022-10-26 23:52:01 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-03 21:21:41 +08: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 17:04:24 +08:00
										 |  |  | 	existingUID := cmd.Folder.UID | 
					
						
							| 
									
										
										
										
											2022-10-28 21:35:49 +08:00
										 |  |  | 	err := ss.db.WithDbSession(ctx, func(sess *db.Session) error { | 
					
						
							| 
									
										
										
										
											2022-11-04 17:04:24 +08:00
										 |  |  | 		sql := strings.Builder{} | 
					
						
							|  |  |  | 		sql.Write([]byte("UPDATE folder SET ")) | 
					
						
							|  |  |  | 		columnsToUpdate := []string{"updated = ?"} | 
					
						
							|  |  |  | 		args := []interface{}{cmd.Folder.Updated} | 
					
						
							| 
									
										
										
										
											2022-11-03 21:21:41 +08:00
										 |  |  | 		if cmd.NewDescription != nil { | 
					
						
							| 
									
										
										
										
											2022-11-04 17:04:24 +08:00
										 |  |  | 			columnsToUpdate = append(columnsToUpdate, "description = ?") | 
					
						
							|  |  |  | 			cmd.Folder.Description = *cmd.NewDescription | 
					
						
							|  |  |  | 			args = append(args, cmd.Folder.Description) | 
					
						
							| 
									
										
										
										
											2022-11-03 21:21:41 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if cmd.NewTitle != nil { | 
					
						
							| 
									
										
										
										
											2022-11-04 17:04:24 +08:00
										 |  |  | 			columnsToUpdate = append(columnsToUpdate, "title = ?") | 
					
						
							|  |  |  | 			cmd.Folder.Title = *cmd.NewTitle | 
					
						
							|  |  |  | 			args = append(args, cmd.Folder.Title) | 
					
						
							| 
									
										
										
										
											2022-11-03 21:21:41 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if cmd.NewUID != nil { | 
					
						
							| 
									
										
										
										
											2022-11-04 17:04:24 +08:00
										 |  |  | 			columnsToUpdate = append(columnsToUpdate, "uid = ?") | 
					
						
							|  |  |  | 			cmd.Folder.UID = *cmd.NewUID | 
					
						
							|  |  |  | 			args = append(args, cmd.Folder.UID) | 
					
						
							| 
									
										
										
										
											2022-11-03 21:21:41 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-08 18:33:13 +08:00
										 |  |  | 		if cmd.NewParentUID != nil { | 
					
						
							|  |  |  | 			columnsToUpdate = append(columnsToUpdate, "parent_uid = ?") | 
					
						
							|  |  |  | 			cmd.Folder.ParentUID = *cmd.NewParentUID | 
					
						
							|  |  |  | 			args = append(args, cmd.Folder.UID) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-04 17:04:24 +08: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 21:21:41 +08: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 21:35:49 +08:00
										 |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2022-10-26 23:52:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-28 21:35:49 +08:00
										 |  |  | 	return cmd.Folder, err | 
					
						
							| 
									
										
										
										
											2022-10-26 23:52:01 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-03 21:21:41 +08:00
										 |  |  | func (ss *sqlStore) Get(ctx context.Context, q folder.GetFolderQuery) (*folder.Folder, error) { | 
					
						
							|  |  |  | 	foldr := &folder.Folder{} | 
					
						
							| 
									
										
										
										
											2022-10-28 21:35:49 +08:00
										 |  |  | 	err := ss.db.WithDbSession(ctx, func(sess *db.Session) error { | 
					
						
							| 
									
										
										
										
											2022-11-03 21:21:41 +08: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 21:35:49 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2022-11-03 21:21:41 +08:00
										 |  |  | 			return folder.ErrDatabaseError.Errorf("failed to get folder: %w", err) | 
					
						
							| 
									
										
										
										
											2022-10-28 21:35:49 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if !exists { | 
					
						
							|  |  |  | 			return folder.ErrFolderNotFound.Errorf("folder not found") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	return foldr, err | 
					
						
							| 
									
										
										
										
											2022-10-26 23:52:01 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-03 21:21:41 +08:00
										 |  |  | func (ss *sqlStore) GetParents(ctx context.Context, q folder.GetParentsQuery) ([]*folder.Folder, error) { | 
					
						
							| 
									
										
										
										
											2022-10-29 02:07:25 +08:00
										 |  |  | 	var folders []*folder.Folder | 
					
						
							|  |  |  | 	if ss.db.GetDBType() == migrator.MySQL { | 
					
						
							| 
									
										
										
										
											2022-11-03 21:21:41 +08:00
										 |  |  | 		return ss.getParentsMySQL(ctx, q) | 
					
						
							| 
									
										
										
										
											2022-10-29 02:07:25 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-03 21:21:41 +08: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-29 02:07:25 +08:00
										 |  |  | 		) | 
					
						
							| 
									
										
										
										
											2022-11-03 21:21:41 +08:00
										 |  |  | 		SELECT * FROM RecQry; | 
					
						
							|  |  |  | 	` | 
					
						
							| 
									
										
										
										
											2022-10-29 02:07:25 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	err := ss.db.WithDbSession(ctx, func(sess *db.Session) error { | 
					
						
							| 
									
										
										
										
											2022-11-03 21:21:41 +08:00
										 |  |  | 		err := sess.SQL(recQuery, q.UID, q.OrgID).Find(&folders) | 
					
						
							| 
									
										
										
										
											2022-10-29 02:07:25 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2022-11-03 21:21:41 +08:00
										 |  |  | 			return folder.ErrDatabaseError.Errorf("failed to get folder parents: %w", err) | 
					
						
							| 
									
										
										
										
											2022-10-29 02:07:25 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2022-11-03 21:21:41 +08:00
										 |  |  | 	return util.Reverse(folders[1:]), err | 
					
						
							| 
									
										
										
										
											2022-10-26 23:52:01 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-03 21:21:41 +08:00
										 |  |  | func (ss *sqlStore) GetChildren(ctx context.Context, q folder.GetTreeQuery) ([]*folder.Folder, error) { | 
					
						
							| 
									
										
										
										
											2022-10-28 21:35:49 +08:00
										 |  |  | 	var folders []*folder.Folder | 
					
						
							| 
									
										
										
										
											2022-10-29 02:07:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-28 21:35:49 +08:00
										 |  |  | 	err := ss.db.WithDbSession(ctx, func(sess *db.Session) error { | 
					
						
							| 
									
										
										
										
											2022-11-03 21:21:41 +08: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 21:35:49 +08:00
										 |  |  | 	}) | 
					
						
							|  |  |  | 	return folders, err | 
					
						
							| 
									
										
										
										
											2022-10-26 23:52:01 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2022-10-29 02:07:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-03 21:21:41 +08:00
										 |  |  | func (ss *sqlStore) getParentsMySQL(ctx context.Context, cmd folder.GetParentsQuery) ([]*folder.Folder, error) { | 
					
						
							| 
									
										
										
										
											2022-10-29 02:07:25 +08: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 21:21:41 +08:00
										 |  |  | 			err := sess.Where("uid=? AND org_id=>", uid, cmd.OrgID).Find(foldr) | 
					
						
							| 
									
										
										
										
											2022-10-29 02:07:25 +08:00
										 |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-11-03 21:21:41 +08:00
										 |  |  | 				return folder.ErrDatabaseError.Errorf("failed to get folder parents: %w", err) | 
					
						
							| 
									
										
										
										
											2022-10-29 02:07:25 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			foldrs = append(foldrs, foldr) | 
					
						
							|  |  |  | 			uid = foldr.ParentUID | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	return foldrs, err | 
					
						
							|  |  |  | } |