mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Query history: Create API to delete query from query history (#44653)
* Query history: Add delete and refactor * Update docs/sources/http_api/query_history.md Co-authored-by: Piotr Jamróz <pm.jamroz@gmail.com> Co-authored-by: Piotr Jamróz <pm.jamroz@gmail.com>
This commit is contained in:
@@ -7,12 +7,14 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/middleware"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
)
|
||||
|
||||
func (s *QueryHistoryService) registerAPIEndpoints() {
|
||||
s.RouteRegister.Group("/api/query-history", func(entities routing.RouteRegister) {
|
||||
entities.Post("/", middleware.ReqSignedIn, routing.Wrap(s.createHandler))
|
||||
entities.Delete("/:uid", middleware.ReqSignedIn, routing.Wrap(s.deleteHandler))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -22,10 +24,31 @@ func (s *QueryHistoryService) createHandler(c *models.ReqContext) response.Respo
|
||||
return response.Error(http.StatusBadRequest, "bad request data", err)
|
||||
}
|
||||
|
||||
err := s.CreateQueryInQueryHistory(c.Req.Context(), c.SignedInUser, cmd)
|
||||
query, err := s.CreateQueryInQueryHistory(c.Req.Context(), c.SignedInUser, cmd)
|
||||
if err != nil {
|
||||
return response.Error(500, "Failed to create query history", err)
|
||||
return response.Error(http.StatusInternalServerError, "Failed to create query history", err)
|
||||
}
|
||||
|
||||
return response.Success("Query successfully added to query history")
|
||||
return response.JSON(http.StatusOK, QueryHistoryResponse{Result: query})
|
||||
}
|
||||
|
||||
func (s *QueryHistoryService) deleteHandler(c *models.ReqContext) response.Response {
|
||||
queryUID := web.Params(c.Req)[":uid"]
|
||||
if len(queryUID) == 0 {
|
||||
return response.Error(http.StatusNotFound, "Query in query history not found", nil)
|
||||
}
|
||||
|
||||
if !util.IsValidShortUID(queryUID) {
|
||||
return response.Error(http.StatusNotFound, "Query in query history not found", nil)
|
||||
}
|
||||
|
||||
id, err := s.DeleteQueryFromQueryHistory(c.Req.Context(), c.SignedInUser, queryUID)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Failed to delete query from query history", err)
|
||||
}
|
||||
|
||||
return response.JSON(http.StatusOK, DeleteQueryFromQueryHistoryResponse{
|
||||
Message: "Query deleted",
|
||||
ID: id,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -9,12 +9,12 @@ import (
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
func (s QueryHistoryService) createQuery(ctx context.Context, user *models.SignedInUser, cmd CreateQueryInQueryHistoryCommand) error {
|
||||
func (s QueryHistoryService) createQuery(ctx context.Context, user *models.SignedInUser, cmd CreateQueryInQueryHistoryCommand) (QueryHistoryDTO, error) {
|
||||
queryHistory := QueryHistory{
|
||||
OrgId: user.OrgId,
|
||||
Uid: util.GenerateShortUID(),
|
||||
OrgID: user.OrgId,
|
||||
UID: util.GenerateShortUID(),
|
||||
Queries: cmd.Queries,
|
||||
DatasourceUid: cmd.DatasourceUid,
|
||||
DatasourceUID: cmd.DatasourceUID,
|
||||
CreatedBy: user.UserId,
|
||||
CreatedAt: time.Now().Unix(),
|
||||
Comment: "",
|
||||
@@ -25,8 +25,32 @@ func (s QueryHistoryService) createQuery(ctx context.Context, user *models.Signe
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
return QueryHistoryDTO{}, err
|
||||
}
|
||||
|
||||
return nil
|
||||
dto := QueryHistoryDTO{
|
||||
UID: queryHistory.UID,
|
||||
DatasourceUID: queryHistory.DatasourceUID,
|
||||
CreatedBy: queryHistory.CreatedBy,
|
||||
CreatedAt: queryHistory.CreatedAt,
|
||||
Comment: queryHistory.Comment,
|
||||
Queries: queryHistory.Queries,
|
||||
Starred: false,
|
||||
}
|
||||
|
||||
return dto, nil
|
||||
}
|
||||
|
||||
func (s QueryHistoryService) deleteQuery(ctx context.Context, user *models.SignedInUser, UID string) (int64, error) {
|
||||
var queryID int64
|
||||
err := s.SQLStore.WithDbSession(ctx, func(session *sqlstore.DBSession) error {
|
||||
id, err := session.Where("org_id = ? AND created_by = ? AND uid = ?", user.OrgId, user.UserId, UID).Delete(QueryHistory{})
|
||||
if id == 0 {
|
||||
return ErrQueryNotFound
|
||||
}
|
||||
queryID = id
|
||||
return err
|
||||
})
|
||||
|
||||
return queryID, err
|
||||
}
|
||||
|
||||
@@ -1,21 +1,48 @@
|
||||
package queryhistory
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrQueryNotFound = errors.New("query in query history not found")
|
||||
)
|
||||
|
||||
type QueryHistory struct {
|
||||
Id int64 `json:"id"`
|
||||
Uid string `json:"uid"`
|
||||
DatasourceUid string `json:"datasourceUid"`
|
||||
OrgId int64 `json:"orgId"`
|
||||
ID int64 `xorm:"pk autoincr 'id'"`
|
||||
UID string `xorm:"uid"`
|
||||
DatasourceUID string `xorm:"datasource_uid"`
|
||||
OrgID int64 `xorm:"org_id"`
|
||||
CreatedBy int64
|
||||
CreatedAt int64
|
||||
Comment string
|
||||
Queries *simplejson.Json
|
||||
}
|
||||
|
||||
type CreateQueryInQueryHistoryCommand struct {
|
||||
DatasourceUID string `json:"datasourceUid"`
|
||||
Queries *simplejson.Json `json:"queries"`
|
||||
}
|
||||
|
||||
type QueryHistoryDTO struct {
|
||||
UID string `json:"uid"`
|
||||
DatasourceUID string `json:"datasourceUid"`
|
||||
CreatedBy int64 `json:"createdBy"`
|
||||
CreatedAt int64 `json:"createdAt"`
|
||||
Comment string `json:"comment"`
|
||||
Queries *simplejson.Json `json:"queries"`
|
||||
Starred bool `json:"starred"`
|
||||
}
|
||||
|
||||
type CreateQueryInQueryHistoryCommand struct {
|
||||
DatasourceUid string `json:"datasourceUid"`
|
||||
Queries *simplejson.Json `json:"queries"`
|
||||
// QueryHistoryResponse is a response struct for QueryHistoryDTO
|
||||
type QueryHistoryResponse struct {
|
||||
Result QueryHistoryDTO `json:"result"`
|
||||
}
|
||||
|
||||
// DeleteQueryFromQueryHistoryResponse is the response struct for deleting a query from query history
|
||||
type DeleteQueryFromQueryHistoryResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
@@ -27,7 +27,8 @@ func ProvideService(cfg *setting.Cfg, sqlStore *sqlstore.SQLStore, routeRegister
|
||||
}
|
||||
|
||||
type Service interface {
|
||||
CreateQueryInQueryHistory(ctx context.Context, user *models.SignedInUser, cmd CreateQueryInQueryHistoryCommand) error
|
||||
CreateQueryInQueryHistory(ctx context.Context, user *models.SignedInUser, cmd CreateQueryInQueryHistoryCommand) (QueryHistoryDTO, error)
|
||||
DeleteQueryFromQueryHistory(ctx context.Context, user *models.SignedInUser, UID string) (int64, error)
|
||||
}
|
||||
|
||||
type QueryHistoryService struct {
|
||||
@@ -37,6 +38,10 @@ type QueryHistoryService struct {
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func (s QueryHistoryService) CreateQueryInQueryHistory(ctx context.Context, user *models.SignedInUser, cmd CreateQueryInQueryHistoryCommand) error {
|
||||
func (s QueryHistoryService) CreateQueryInQueryHistory(ctx context.Context, user *models.SignedInUser, cmd CreateQueryInQueryHistoryCommand) (QueryHistoryDTO, error) {
|
||||
return s.createQuery(ctx, user, cmd)
|
||||
}
|
||||
|
||||
func (s QueryHistoryService) DeleteQueryFromQueryHistory(ctx context.Context, user *models.SignedInUser, UID string) (int64, error) {
|
||||
return s.deleteQuery(ctx, user, UID)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ func TestCreateQueryInQueryHistory(t *testing.T) {
|
||||
testScenario(t, "When users tries to create query in query history it should succeed",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
command := CreateQueryInQueryHistoryCommand{
|
||||
DatasourceUid: "NCzh67i",
|
||||
DatasourceUID: "NCzh67i",
|
||||
Queries: simplejson.NewFromAny(map[string]interface{}{
|
||||
"expr": "test",
|
||||
}),
|
||||
|
||||
23
pkg/services/queryhistory/queryhistory_delete_test.go
Normal file
23
pkg/services/queryhistory/queryhistory_delete_test.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package queryhistory
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDeleteQueryFromQueryHistory(t *testing.T) {
|
||||
testScenarioWithQueryInQueryHistory(t, "When users tries to delete query in query history that does not exist, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
resp := sc.service.deleteHandler(sc.reqContext)
|
||||
require.Equal(t, 404, resp.Status())
|
||||
})
|
||||
|
||||
testScenarioWithQueryInQueryHistory(t, "When users tries to delete query in query history that exists, it should succeed",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
resp := sc.service.deleteHandler(sc.reqContext)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
})
|
||||
}
|
||||
@@ -9,6 +9,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
@@ -22,10 +24,11 @@ var (
|
||||
)
|
||||
|
||||
type scenarioContext struct {
|
||||
ctx *web.Context
|
||||
service *QueryHistoryService
|
||||
reqContext *models.ReqContext
|
||||
sqlStore *sqlstore.SQLStore
|
||||
ctx *web.Context
|
||||
service *QueryHistoryService
|
||||
reqContext *models.ReqContext
|
||||
sqlStore *sqlstore.SQLStore
|
||||
initialResult QueryHistoryResponse
|
||||
}
|
||||
|
||||
func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioContext)) {
|
||||
@@ -71,7 +74,36 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
|
||||
})
|
||||
}
|
||||
|
||||
func testScenarioWithQueryInQueryHistory(t *testing.T, desc string, fn func(t *testing.T, sc scenarioContext)) {
|
||||
t.Helper()
|
||||
|
||||
testScenario(t, desc, func(t *testing.T, sc scenarioContext) {
|
||||
command := CreateQueryInQueryHistoryCommand{
|
||||
DatasourceUID: "NCzh67i",
|
||||
Queries: simplejson.NewFromAny(map[string]interface{}{
|
||||
"expr": "test",
|
||||
}),
|
||||
}
|
||||
sc.reqContext.Req.Body = mockRequestBody(command)
|
||||
resp := sc.service.createHandler(sc.reqContext)
|
||||
sc.initialResult = validateAndUnMarshalResponse(t, resp)
|
||||
fn(t, sc)
|
||||
})
|
||||
}
|
||||
|
||||
func mockRequestBody(v interface{}) io.ReadCloser {
|
||||
b, _ := json.Marshal(v)
|
||||
return io.NopCloser(bytes.NewReader(b))
|
||||
}
|
||||
|
||||
func validateAndUnMarshalResponse(t *testing.T, resp response.Response) QueryHistoryResponse {
|
||||
t.Helper()
|
||||
|
||||
require.Equal(t, 200, resp.Status())
|
||||
|
||||
var result = QueryHistoryResponse{}
|
||||
err := json.Unmarshal(resp.Body(), &result)
|
||||
require.NoError(t, err)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user