mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Query history: Add migration endpoint (#47551)
* Add endpoint for migration * Check for createdAt * Query history: Remove returning of dtos * Query history: Fix CreatedAt * Refactor based on suggestions * Insert into table in batches
This commit is contained in:
parent
17e44c306c
commit
aceedb3a32
@ -19,6 +19,8 @@ func (s *QueryHistoryService) registerAPIEndpoints() {
|
||||
entities.Post("/star/:uid", middleware.ReqSignedIn, routing.Wrap(s.starHandler))
|
||||
entities.Delete("/star/:uid", middleware.ReqSignedIn, routing.Wrap(s.unstarHandler))
|
||||
entities.Patch("/:uid", middleware.ReqSignedIn, routing.Wrap(s.patchCommentHandler))
|
||||
// Remove migrate endpoint in Grafana v10 as breaking change
|
||||
entities.Post("/migrate", middleware.ReqSignedIn, routing.Wrap(s.migrateHandler))
|
||||
})
|
||||
}
|
||||
|
||||
@ -117,3 +119,17 @@ func (s *QueryHistoryService) unstarHandler(c *models.ReqContext) response.Respo
|
||||
|
||||
return response.JSON(http.StatusOK, QueryHistoryResponse{Result: query})
|
||||
}
|
||||
|
||||
func (s *QueryHistoryService) migrateHandler(c *models.ReqContext) response.Response {
|
||||
cmd := MigrateQueriesToQueryHistoryCommand{}
|
||||
if err := web.Bind(c.Req, &cmd); err != nil {
|
||||
return response.Error(http.StatusBadRequest, "bad request data", err)
|
||||
}
|
||||
|
||||
totalCount, starredCount, err := s.MigrateQueriesToQueryHistory(c.Req.Context(), c.SignedInUser, cmd)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Failed to migrate query history", err)
|
||||
}
|
||||
|
||||
return response.JSON(http.StatusOK, QueryHistoryMigrationResponse{Message: "Query history successfully migrated", TotalCount: totalCount, StarredCount: starredCount})
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package queryhistory
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
@ -265,3 +266,61 @@ func (s QueryHistoryService) unstarQuery(ctx context.Context, user *models.Signe
|
||||
|
||||
return dto, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -78,3 +78,21 @@ type DeleteQueryFromQueryHistoryResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type MigrateQueriesToQueryHistoryCommand struct {
|
||||
Queries []QueryToMigrate `json:"queries"`
|
||||
}
|
||||
|
||||
type QueryToMigrate struct {
|
||||
DatasourceUID string `json:"datasourceUid"`
|
||||
Queries *simplejson.Json `json:"queries"`
|
||||
CreatedAt int64 `json:"createdAt"`
|
||||
Comment string `json:"comment"`
|
||||
Starred bool `json:"starred"`
|
||||
}
|
||||
|
||||
type QueryHistoryMigrationResponse struct {
|
||||
Message string `json:"message"`
|
||||
TotalCount int `json:"totalCount"`
|
||||
StarredCount int `json:"starredCount"`
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ type Service interface {
|
||||
PatchQueryCommentInQueryHistory(ctx context.Context, user *models.SignedInUser, UID string, cmd PatchQueryCommentInQueryHistoryCommand) (QueryHistoryDTO, error)
|
||||
StarQueryInQueryHistory(ctx context.Context, user *models.SignedInUser, UID string) (QueryHistoryDTO, error)
|
||||
UnstarQueryInQueryHistory(ctx context.Context, user *models.SignedInUser, UID string) (QueryHistoryDTO, error)
|
||||
MigrateQueriesToQueryHistory(ctx context.Context, user *models.SignedInUser, cmd MigrateQueriesToQueryHistoryCommand) (int, int, error)
|
||||
}
|
||||
|
||||
type QueryHistoryService struct {
|
||||
@ -65,3 +66,7 @@ func (s QueryHistoryService) StarQueryInQueryHistory(ctx context.Context, user *
|
||||
func (s QueryHistoryService) UnstarQueryInQueryHistory(ctx context.Context, user *models.SignedInUser, UID string) (QueryHistoryDTO, error) {
|
||||
return s.unstarQuery(ctx, user, UID)
|
||||
}
|
||||
|
||||
func (s QueryHistoryService) MigrateQueriesToQueryHistory(ctx context.Context, user *models.SignedInUser, cmd MigrateQueriesToQueryHistoryCommand) (int, int, error) {
|
||||
return s.migrateQueries(ctx, user, cmd)
|
||||
}
|
||||
|
120
pkg/services/queryhistory/queryhistory_migrate_test.go
Normal file
120
pkg/services/queryhistory/queryhistory_migrate_test.go
Normal file
@ -0,0 +1,120 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package queryhistory
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMigrateQueriesToQueryHistory(t *testing.T) {
|
||||
testScenario(t, "When users tries to migrate 1 query in query history it should succeed",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
command := MigrateQueriesToQueryHistoryCommand{
|
||||
Queries: []QueryToMigrate{
|
||||
{
|
||||
DatasourceUID: "NCzh67i",
|
||||
Queries: simplejson.NewFromAny(map[string]interface{}{
|
||||
"expr": "test",
|
||||
}),
|
||||
Comment: "",
|
||||
Starred: false,
|
||||
CreatedAt: time.Now().Unix(),
|
||||
},
|
||||
},
|
||||
}
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.migrateHandler(sc.reqContext)
|
||||
var response QueryHistoryMigrationResponse
|
||||
err := json.Unmarshal(resp.Body(), &response)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
require.Equal(t, "Query history successfully migrated", response.Message)
|
||||
require.Equal(t, 1, response.TotalCount)
|
||||
require.Equal(t, 0, response.StarredCount)
|
||||
})
|
||||
|
||||
testScenario(t, "When users tries to migrate multiple queries in query history it should succeed",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
command := MigrateQueriesToQueryHistoryCommand{
|
||||
Queries: []QueryToMigrate{
|
||||
{
|
||||
DatasourceUID: "NCzh67i",
|
||||
Queries: simplejson.NewFromAny(map[string]interface{}{
|
||||
"expr": "test1",
|
||||
}),
|
||||
Comment: "",
|
||||
Starred: false,
|
||||
CreatedAt: time.Now().Unix(),
|
||||
},
|
||||
{
|
||||
DatasourceUID: "NCzh67i",
|
||||
Queries: simplejson.NewFromAny(map[string]interface{}{
|
||||
"expr": "test2",
|
||||
}),
|
||||
Comment: "",
|
||||
Starred: false,
|
||||
CreatedAt: time.Now().Unix() - int64(100),
|
||||
},
|
||||
{
|
||||
DatasourceUID: "ABch68f",
|
||||
Queries: simplejson.NewFromAny(map[string]interface{}{
|
||||
"expr": "test3",
|
||||
}),
|
||||
Comment: "",
|
||||
Starred: false,
|
||||
CreatedAt: time.Now().Unix() - int64(1000),
|
||||
},
|
||||
},
|
||||
}
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.migrateHandler(sc.reqContext)
|
||||
var response QueryHistoryMigrationResponse
|
||||
err := json.Unmarshal(resp.Body(), &response)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
require.Equal(t, "Query history successfully migrated", response.Message)
|
||||
require.Equal(t, 3, response.TotalCount)
|
||||
require.Equal(t, 0, response.StarredCount)
|
||||
})
|
||||
|
||||
testScenario(t, "When users tries to migrate starred and not starred query in query history it should succeed",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
command := MigrateQueriesToQueryHistoryCommand{
|
||||
Queries: []QueryToMigrate{
|
||||
{
|
||||
DatasourceUID: "NCzh67i",
|
||||
Queries: simplejson.NewFromAny(map[string]interface{}{
|
||||
"expr": "test1",
|
||||
}),
|
||||
Comment: "",
|
||||
Starred: true,
|
||||
CreatedAt: time.Now().Unix(),
|
||||
},
|
||||
{
|
||||
DatasourceUID: "NCzh67i",
|
||||
Queries: simplejson.NewFromAny(map[string]interface{}{
|
||||
"expr": "test2",
|
||||
}),
|
||||
Comment: "",
|
||||
Starred: false,
|
||||
CreatedAt: time.Now().Unix() - int64(100),
|
||||
},
|
||||
},
|
||||
}
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.migrateHandler(sc.reqContext)
|
||||
var response QueryHistoryMigrationResponse
|
||||
err := json.Unmarshal(resp.Body(), &response)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
require.Equal(t, "Query history successfully migrated", response.Message)
|
||||
require.Equal(t, 2, response.TotalCount)
|
||||
require.Equal(t, 1, response.StarredCount)
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue
Block a user