Query history: Create API to add query to query history (#44479)

* Create config to enable/disable query history

* Create add to query history functionality

* Add documentation

* Add test

* Refactor

* Add test

* Fix built errors and linting errors

* Refactor

* Remove old tests

* Refactor, adjust based on feedback, add new test

* Update default value
This commit is contained in:
Ivana Huckova
2022-01-28 17:55:09 +01:00
committed by GitHub
parent ca24b95b49
commit 4e37a53a1c
15 changed files with 339 additions and 2 deletions

View File

@@ -0,0 +1,31 @@
package queryhistory
import (
"net/http"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/models"
"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))
})
}
func (s *QueryHistoryService) createHandler(c *models.ReqContext) response.Response {
cmd := CreateQueryInQueryHistoryCommand{}
if err := web.Bind(c.Req, &cmd); err != nil {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
err := s.CreateQueryInQueryHistory(c.Req.Context(), c.SignedInUser, cmd)
if err != nil {
return response.Error(500, "Failed to create query history", err)
}
return response.Success("Query successfully added to query history")
}

View File

@@ -0,0 +1,32 @@
package queryhistory
import (
"context"
"time"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/util"
)
func (s QueryHistoryService) createQuery(ctx context.Context, user *models.SignedInUser, cmd CreateQueryInQueryHistoryCommand) error {
queryHistory := QueryHistory{
OrgId: user.OrgId,
Uid: util.GenerateShortUID(),
Queries: cmd.Queries,
DatasourceUid: cmd.DatasourceUid,
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 {
return err
}
return nil
}

View File

@@ -0,0 +1,21 @@
package queryhistory
import (
"github.com/grafana/grafana/pkg/components/simplejson"
)
type QueryHistory struct {
Id int64 `json:"id"`
Uid string `json:"uid"`
DatasourceUid string `json:"datasourceUid"`
OrgId int64 `json:"orgId"`
CreatedBy int64 `json:"createdBy"`
CreatedAt int64 `json:"createdAt"`
Comment string `json:"comment"`
Queries *simplejson.Json `json:"queries"`
}
type CreateQueryInQueryHistoryCommand struct {
DatasourceUid string `json:"datasourceUid"`
Queries *simplejson.Json `json:"queries"`
}

View File

@@ -0,0 +1,42 @@
package queryhistory
import (
"context"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
)
func ProvideService(cfg *setting.Cfg, sqlStore *sqlstore.SQLStore, routeRegister routing.RouteRegister) *QueryHistoryService {
s := &QueryHistoryService{
SQLStore: sqlStore,
Cfg: cfg,
RouteRegister: routeRegister,
log: log.New("query-history"),
}
// Register routes only when query history is enabled
if s.Cfg.QueryHistoryEnabled {
s.registerAPIEndpoints()
}
return s
}
type Service interface {
CreateQueryInQueryHistory(ctx context.Context, user *models.SignedInUser, cmd CreateQueryInQueryHistoryCommand) error
}
type QueryHistoryService struct {
SQLStore *sqlstore.SQLStore
Cfg *setting.Cfg
RouteRegister routing.RouteRegister
log log.Logger
}
func (s QueryHistoryService) CreateQueryInQueryHistory(ctx context.Context, user *models.SignedInUser, cmd CreateQueryInQueryHistoryCommand) error {
return s.createQuery(ctx, user, cmd)
}

View File

@@ -0,0 +1,23 @@
package queryhistory
import (
"testing"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/stretchr/testify/require"
)
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",
Queries: simplejson.NewFromAny(map[string]interface{}{
"expr": "test",
}),
}
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
})
}

View File

@@ -0,0 +1,77 @@
package queryhistory
import (
"bytes"
"context"
"encoding/json"
"io"
"net/http"
"testing"
"time"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/web"
"github.com/stretchr/testify/require"
)
var (
testOrgID = int64(1)
testUserID = int64(1)
)
type scenarioContext struct {
ctx *web.Context
service *QueryHistoryService
reqContext *models.ReqContext
sqlStore *sqlstore.SQLStore
}
func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioContext)) {
t.Helper()
t.Run(desc, func(t *testing.T) {
ctx := web.Context{Req: &http.Request{}}
sqlStore := sqlstore.InitTestDB(t)
service := QueryHistoryService{
Cfg: setting.NewCfg(),
SQLStore: sqlStore,
}
service.Cfg.QueryHistoryEnabled = true
user := models.SignedInUser{
UserId: testUserID,
Name: "Signed In User",
Login: "signed_in_user",
Email: "signed.in.user@test.com",
OrgId: testOrgID,
OrgRole: models.ROLE_VIEWER,
LastSeenAt: time.Now(),
}
_, err := sqlStore.CreateUser(context.Background(), models.CreateUserCommand{
Email: "signed.in.user@test.com",
Name: "Signed In User",
Login: "signed_in_user",
})
require.NoError(t, err)
sc := scenarioContext{
ctx: &ctx,
service: &service,
sqlStore: sqlStore,
reqContext: &models.ReqContext{
Context: &ctx,
SignedInUser: &user,
},
}
fn(t, sc)
})
}
func mockRequestBody(v interface{}) io.ReadCloser {
b, _ := json.Marshal(v)
return io.NopCloser(bytes.NewReader(b))
}