2022-01-28 10:55:09 -06:00
|
|
|
package queryhistory
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2022-03-07 05:28:04 -06:00
|
|
|
"errors"
|
2022-04-14 02:33:41 -05:00
|
|
|
"fmt"
|
2022-01-28 10:55:09 -06:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/grafana/grafana/pkg/models"
|
|
|
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
|
|
|
"github.com/grafana/grafana/pkg/util"
|
|
|
|
)
|
|
|
|
|
2022-02-04 09:14:36 -06:00
|
|
|
func (s QueryHistoryService) createQuery(ctx context.Context, user *models.SignedInUser, cmd CreateQueryInQueryHistoryCommand) (QueryHistoryDTO, error) {
|
2022-01-28 10:55:09 -06:00
|
|
|
queryHistory := QueryHistory{
|
2022-02-04 09:14:36 -06:00
|
|
|
OrgID: user.OrgId,
|
|
|
|
UID: util.GenerateShortUID(),
|
2022-01-28 10:55:09 -06:00
|
|
|
Queries: cmd.Queries,
|
2022-02-04 09:14:36 -06:00
|
|
|
DatasourceUID: cmd.DatasourceUID,
|
2022-01-28 10:55:09 -06:00
|
|
|
CreatedBy: user.UserId,
|
|
|
|
CreatedAt: time.Now().Unix(),
|
|
|
|
Comment: "",
|
|
|
|
}
|
|
|
|
|
|
|
|
err := s.SQLStore.WithDbSession(ctx, func(session *sqlstore.DBSession) error {
|
|
|
|
_, err := session.Insert(&queryHistory)
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
if err != nil {
|
2022-02-04 09:14:36 -06:00
|
|
|
return QueryHistoryDTO{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
dto := QueryHistoryDTO{
|
|
|
|
UID: queryHistory.UID,
|
|
|
|
DatasourceUID: queryHistory.DatasourceUID,
|
|
|
|
CreatedBy: queryHistory.CreatedBy,
|
|
|
|
CreatedAt: queryHistory.CreatedAt,
|
|
|
|
Comment: queryHistory.Comment,
|
|
|
|
Queries: queryHistory.Queries,
|
|
|
|
Starred: false,
|
2022-01-28 10:55:09 -06:00
|
|
|
}
|
|
|
|
|
2022-02-04 09:14:36 -06:00
|
|
|
return dto, nil
|
|
|
|
}
|
|
|
|
|
2022-03-07 05:28:04 -06:00
|
|
|
func (s QueryHistoryService) searchQueries(ctx context.Context, user *models.SignedInUser, query SearchInQueryHistoryQuery) (QueryHistorySearchResult, error) {
|
|
|
|
var dtos []QueryHistoryDTO
|
|
|
|
var allQueries []interface{}
|
|
|
|
|
|
|
|
if len(query.DatasourceUIDs) == 0 {
|
|
|
|
return QueryHistorySearchResult{}, errors.New("no selected data source for query history search")
|
|
|
|
}
|
|
|
|
|
|
|
|
if query.Page <= 0 {
|
|
|
|
query.Page = 1
|
|
|
|
}
|
|
|
|
|
|
|
|
if query.Limit <= 0 {
|
|
|
|
query.Limit = 100
|
|
|
|
}
|
|
|
|
|
|
|
|
if query.Sort == "" {
|
|
|
|
query.Sort = "time-desc"
|
|
|
|
}
|
|
|
|
|
|
|
|
err := s.SQLStore.WithDbSession(ctx, func(session *sqlstore.DBSession) error {
|
|
|
|
dtosBuilder := sqlstore.SQLBuilder{}
|
|
|
|
dtosBuilder.Write(`SELECT
|
|
|
|
query_history.uid,
|
|
|
|
query_history.datasource_uid,
|
|
|
|
query_history.created_by,
|
|
|
|
query_history.created_at AS created_at,
|
|
|
|
query_history.comment,
|
|
|
|
query_history.queries,
|
|
|
|
`)
|
|
|
|
writeStarredSQL(query, s.SQLStore, &dtosBuilder)
|
|
|
|
writeFiltersSQL(query, user, s.SQLStore, &dtosBuilder)
|
|
|
|
writeSortSQL(query, s.SQLStore, &dtosBuilder)
|
|
|
|
writeLimitSQL(query, s.SQLStore, &dtosBuilder)
|
|
|
|
writeOffsetSQL(query, s.SQLStore, &dtosBuilder)
|
|
|
|
|
|
|
|
err := session.SQL(dtosBuilder.GetSQLString(), dtosBuilder.GetParams()...).Find(&dtos)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
countBuilder := sqlstore.SQLBuilder{}
|
|
|
|
countBuilder.Write(`SELECT
|
|
|
|
`)
|
|
|
|
writeStarredSQL(query, s.SQLStore, &countBuilder)
|
|
|
|
writeFiltersSQL(query, user, s.SQLStore, &countBuilder)
|
|
|
|
err = session.SQL(countBuilder.GetSQLString(), countBuilder.GetParams()...).Find(&allQueries)
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return QueryHistorySearchResult{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
response := QueryHistorySearchResult{
|
|
|
|
QueryHistory: dtos,
|
|
|
|
TotalCount: len(allQueries),
|
|
|
|
Page: query.Page,
|
|
|
|
PerPage: query.Limit,
|
|
|
|
}
|
|
|
|
|
|
|
|
return response, nil
|
|
|
|
}
|
|
|
|
|
2022-02-04 09:14:36 -06:00
|
|
|
func (s QueryHistoryService) deleteQuery(ctx context.Context, user *models.SignedInUser, UID string) (int64, error) {
|
|
|
|
var queryID int64
|
2022-02-23 10:03:04 -06:00
|
|
|
err := s.SQLStore.WithTransactionalDbSession(ctx, func(session *sqlstore.DBSession) error {
|
|
|
|
// Try to unstar the query first
|
|
|
|
_, err := session.Table("query_history_star").Where("user_id = ? AND query_uid = ?", user.UserId, UID).Delete(QueryHistoryStar{})
|
|
|
|
if err != nil {
|
|
|
|
s.log.Error("Failed to unstar query while deleting it from query history", "query", UID, "user", user.UserId, "error", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Then delete it
|
2022-02-04 09:14:36 -06:00
|
|
|
id, err := session.Where("org_id = ? AND created_by = ? AND uid = ?", user.OrgId, user.UserId, UID).Delete(QueryHistory{})
|
2022-02-23 10:03:04 -06:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-02-04 09:14:36 -06:00
|
|
|
if id == 0 {
|
|
|
|
return ErrQueryNotFound
|
|
|
|
}
|
2022-02-23 10:03:04 -06:00
|
|
|
|
2022-02-04 09:14:36 -06:00
|
|
|
queryID = id
|
2022-02-23 10:03:04 -06:00
|
|
|
return nil
|
2022-02-04 09:14:36 -06:00
|
|
|
})
|
|
|
|
|
|
|
|
return queryID, err
|
2022-01-28 10:55:09 -06:00
|
|
|
}
|
2022-02-15 08:43:17 -06:00
|
|
|
|
|
|
|
func (s QueryHistoryService) patchQueryComment(ctx context.Context, user *models.SignedInUser, UID string, cmd PatchQueryCommentInQueryHistoryCommand) (QueryHistoryDTO, error) {
|
|
|
|
var queryHistory QueryHistory
|
2022-02-23 10:03:04 -06:00
|
|
|
var isStarred bool
|
|
|
|
|
2022-02-15 08:43:17 -06:00
|
|
|
err := s.SQLStore.WithTransactionalDbSession(ctx, func(session *sqlstore.DBSession) error {
|
|
|
|
exists, err := session.Where("org_id = ? AND created_by = ? AND uid = ?", user.OrgId, user.UserId, UID).Get(&queryHistory)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !exists {
|
|
|
|
return ErrQueryNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
queryHistory.Comment = cmd.Comment
|
|
|
|
_, err = session.ID(queryHistory.ID).Update(queryHistory)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-02-23 10:03:04 -06:00
|
|
|
starred, err := session.Table("query_history_star").Where("user_id = ? AND query_uid = ?", user.UserId, UID).Exist()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
isStarred = starred
|
2022-02-15 08:43:17 -06:00
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return QueryHistoryDTO{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
dto := QueryHistoryDTO{
|
|
|
|
UID: queryHistory.UID,
|
|
|
|
DatasourceUID: queryHistory.DatasourceUID,
|
|
|
|
CreatedBy: queryHistory.CreatedBy,
|
|
|
|
CreatedAt: queryHistory.CreatedAt,
|
|
|
|
Comment: queryHistory.Comment,
|
|
|
|
Queries: queryHistory.Queries,
|
2022-02-23 10:03:04 -06:00
|
|
|
Starred: isStarred,
|
|
|
|
}
|
|
|
|
|
|
|
|
return dto, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s QueryHistoryService) starQuery(ctx context.Context, user *models.SignedInUser, UID string) (QueryHistoryDTO, error) {
|
|
|
|
var queryHistory QueryHistory
|
|
|
|
var isStarred bool
|
|
|
|
|
|
|
|
err := s.SQLStore.WithTransactionalDbSession(ctx, func(session *sqlstore.DBSession) error {
|
|
|
|
// Check if query exists as we want to star only existing queries
|
|
|
|
exists, err := session.Table("query_history").Where("org_id = ? AND created_by = ? AND uid = ?", user.OrgId, user.UserId, UID).Get(&queryHistory)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !exists {
|
|
|
|
return ErrQueryNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
// If query exists then star it
|
|
|
|
queryHistoryStar := QueryHistoryStar{
|
|
|
|
UserID: user.UserId,
|
|
|
|
QueryUID: UID,
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = session.Insert(&queryHistoryStar)
|
|
|
|
if err != nil {
|
|
|
|
if s.SQLStore.Dialect.IsUniqueConstraintViolation(err) {
|
|
|
|
return ErrQueryAlreadyStarred
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
isStarred = true
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return QueryHistoryDTO{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
dto := QueryHistoryDTO{
|
|
|
|
UID: queryHistory.UID,
|
|
|
|
DatasourceUID: queryHistory.DatasourceUID,
|
|
|
|
CreatedBy: queryHistory.CreatedBy,
|
|
|
|
CreatedAt: queryHistory.CreatedAt,
|
|
|
|
Comment: queryHistory.Comment,
|
|
|
|
Queries: queryHistory.Queries,
|
|
|
|
Starred: isStarred,
|
|
|
|
}
|
|
|
|
|
|
|
|
return dto, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s QueryHistoryService) unstarQuery(ctx context.Context, user *models.SignedInUser, UID string) (QueryHistoryDTO, error) {
|
|
|
|
var queryHistory QueryHistory
|
|
|
|
var isStarred bool
|
|
|
|
|
|
|
|
err := s.SQLStore.WithTransactionalDbSession(ctx, func(session *sqlstore.DBSession) error {
|
|
|
|
exists, err := session.Table("query_history").Where("org_id = ? AND created_by = ? AND uid = ?", user.OrgId, user.UserId, UID).Get(&queryHistory)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !exists {
|
|
|
|
return ErrQueryNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
id, err := session.Table("query_history_star").Where("user_id = ? AND query_uid = ?", user.UserId, UID).Delete(QueryHistoryStar{})
|
|
|
|
if id == 0 {
|
|
|
|
return ErrStarredQueryNotFound
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
isStarred = false
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return QueryHistoryDTO{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
dto := QueryHistoryDTO{
|
|
|
|
UID: queryHistory.UID,
|
|
|
|
DatasourceUID: queryHistory.DatasourceUID,
|
|
|
|
CreatedBy: queryHistory.CreatedBy,
|
|
|
|
CreatedAt: queryHistory.CreatedAt,
|
|
|
|
Comment: queryHistory.Comment,
|
|
|
|
Queries: queryHistory.Queries,
|
|
|
|
Starred: isStarred,
|
2022-02-15 08:43:17 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
return dto, nil
|
|
|
|
}
|
2022-04-14 02:33:41 -05:00
|
|
|
|
|
|
|
func (s QueryHistoryService) migrateQueries(ctx context.Context, user *models.SignedInUser, cmd MigrateQueriesToQueryHistoryCommand) (int, int, error) {
|
|
|
|
queryHistories := make([]*QueryHistory, 0, len(cmd.Queries))
|
|
|
|
starredQueries := make([]*QueryHistoryStar, 0)
|
|
|
|
|
|
|
|
err := s.SQLStore.WithTransactionalDbSession(ctx, func(session *sqlstore.DBSession) error {
|
|
|
|
for _, query := range cmd.Queries {
|
|
|
|
uid := util.GenerateShortUID()
|
|
|
|
queryHistories = append(queryHistories, &QueryHistory{
|
|
|
|
OrgID: user.OrgId,
|
|
|
|
UID: uid,
|
|
|
|
Queries: query.Queries,
|
|
|
|
DatasourceUID: query.DatasourceUID,
|
|
|
|
CreatedBy: user.UserId,
|
|
|
|
CreatedAt: query.CreatedAt,
|
|
|
|
Comment: query.Comment,
|
|
|
|
})
|
|
|
|
|
|
|
|
if query.Starred {
|
|
|
|
starredQueries = append(starredQueries, &QueryHistoryStar{
|
|
|
|
UserID: user.UserId,
|
|
|
|
QueryUID: uid,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
batchSize := 50
|
|
|
|
var err error
|
|
|
|
for i := 0; i < len(queryHistories); i += batchSize {
|
|
|
|
j := i + batchSize
|
|
|
|
if j > len(queryHistories) {
|
|
|
|
j = len(queryHistories)
|
|
|
|
}
|
|
|
|
_, err = session.InsertMulti(queryHistories[i:j])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := 0; i < len(starredQueries); i += batchSize {
|
|
|
|
j := i + batchSize
|
|
|
|
if j > len(starredQueries) {
|
|
|
|
j = len(starredQueries)
|
|
|
|
}
|
|
|
|
_, err = session.InsertMulti(starredQueries[i:j])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return 0, 0, fmt.Errorf("failed to migrate query history: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return len(queryHistories), len(starredQueries), nil
|
|
|
|
}
|