mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Query history: Add search functionality (#45932)
* Query history: Add search functionality * Add more tests * Add documentation * Fix spell errors * Update docs * Update docs * Fix lint error * Update docs/sources/http_api/query_history.md Co-authored-by: Piotr Jamróz <pm.jamroz@gmail.com> * Document limit * Run tests with postgres and mysql * Use CASE insted of IIF * Use BooleanStr instead of 1 * Change LIKE to LikeStr() * Return back integration tests * Update SQL to use Bool() everywhere * Create new tests for sorting * Update docs/sources/http_api/query_history.md Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> * Update docs/sources/http_api/query_history.md Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> * Update docs/sources/http_api/query_history.md Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> * Update docs/sources/http_api/query_history.md Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> * Update docs/sources/http_api/query_history.md Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> * Add page, count and limit to results * Remove newline * Update documentation * Update docs Co-authored-by: Piotr Jamróz <pm.jamroz@gmail.com> Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com>
This commit is contained in:
parent
801d751b20
commit
6e96506c23
@ -23,7 +23,7 @@ Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
{
|
||||
"dataSourceUid": "PE1C5CBDA0504A6A3",
|
||||
"datasourceUid": "PE1C5CBDA0504A6A3",
|
||||
"queries": [
|
||||
{
|
||||
"refId": "A",
|
||||
@ -77,6 +77,67 @@ Status codes:
|
||||
- **400** - Errors (invalid JSON, missing or invalid fields)
|
||||
- **500** – Unable to add query to the database
|
||||
|
||||
## Query history search
|
||||
|
||||
`GET /api/query-history`
|
||||
|
||||
Returns a list of queries in the query history that matches the search criteria. Query history search supports pagination. Use the `limit` parameter to control the maximum number of queries returned; the default limit is 100. You can also use the `page` query parameter to fetch queries from any page other than the first one.
|
||||
|
||||
Query parameters:
|
||||
|
||||
- **datasourceUid** - Filter the query history for selected data sources. You must specify at least one data source UID. To perform an "AND" filtering with multiple data sources, specify the data source parameter using the following format: `datasourceUid=uid1&datasourceUid=uid2`.
|
||||
- **searchString** – Filter the query history based on the content.
|
||||
- **sort** - Specify the sorting order. Sorting can be `time-asc` or `time-desc`. The default is `time-desc`.
|
||||
- **onlyStarred** - Search for queries that are starred. Defaults to `false`.
|
||||
- **page** - Search supports pagination. Specify which page number to return. Use the limit parameter to specify the number of queries per page.
|
||||
- **limit** - Limits the number of returned query history items per page. The default is 100 queries per page.
|
||||
|
||||
**Example request for query history search**:
|
||||
|
||||
```http
|
||||
GET /api/query-history?datasourceUid="PE1C5CBDA0504A6A3"&datasourceUid="FG1C1CBDA0504A6EL"&searchString="ALERTS"&sort="time-asc" HTTP/1.1
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
|
||||
```
|
||||
|
||||
**Example response for query history search**:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200
|
||||
Content-Type: application/json
|
||||
{
|
||||
"result": {
|
||||
"totalCount": 150,
|
||||
"page": 1,
|
||||
"perPage": 100
|
||||
"queryHistory":[{
|
||||
"uid": "Ahg678z",
|
||||
"datasourceUid": "PE1C5CBDA0504A6A3",
|
||||
"createdBy": 1,
|
||||
"createdAt": 1643630762,
|
||||
"starred": false,
|
||||
"comment": "",
|
||||
"queries": [
|
||||
{
|
||||
"refId": "A",
|
||||
"key": "Q-87fed8e3-62ba-4eb2-8d2a-4129979bb4de-0",
|
||||
"scenarioId": "csv_content",
|
||||
"datasource": {
|
||||
"type": "testdata",
|
||||
"uid": "PE1C5CBDA0504A6A3"
|
||||
}
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
Status codes:
|
||||
|
||||
- **200** – OK
|
||||
- **500** – Unable to add query to the database
|
||||
|
||||
## Delete query from Query history by UID
|
||||
|
||||
`DELETE /api/query-history/:uid`
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
func (s *QueryHistoryService) registerAPIEndpoints() {
|
||||
s.RouteRegister.Group("/api/query-history", func(entities routing.RouteRegister) {
|
||||
entities.Post("/", middleware.ReqSignedIn, routing.Wrap(s.createHandler))
|
||||
entities.Get("/", middleware.ReqSignedIn, routing.Wrap(s.searchHandler))
|
||||
entities.Delete("/:uid", middleware.ReqSignedIn, routing.Wrap(s.deleteHandler))
|
||||
entities.Post("/star/:uid", middleware.ReqSignedIn, routing.Wrap(s.starHandler))
|
||||
entities.Delete("/star/:uid", middleware.ReqSignedIn, routing.Wrap(s.unstarHandler))
|
||||
@ -35,6 +36,24 @@ func (s *QueryHistoryService) createHandler(c *models.ReqContext) response.Respo
|
||||
return response.JSON(http.StatusOK, QueryHistoryResponse{Result: query})
|
||||
}
|
||||
|
||||
func (s *QueryHistoryService) searchHandler(c *models.ReqContext) response.Response {
|
||||
query := SearchInQueryHistoryQuery{
|
||||
DatasourceUIDs: c.QueryStrings("datasourceUid"),
|
||||
SearchString: c.Query("searchString"),
|
||||
OnlyStarred: c.QueryBoolWithDefault("onlyStarred", false),
|
||||
Sort: c.Query("sort"),
|
||||
Page: c.QueryInt("page"),
|
||||
Limit: c.QueryInt("limit"),
|
||||
}
|
||||
|
||||
result, err := s.SearchInQueryHistory(c.Req.Context(), c.SignedInUser, query)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Failed to get query history", err)
|
||||
}
|
||||
|
||||
return response.JSON(http.StatusOK, QueryHistorySearchResponse{Result: result})
|
||||
}
|
||||
|
||||
func (s *QueryHistoryService) deleteHandler(c *models.ReqContext) response.Response {
|
||||
queryUID := web.Params(c.Req)[":uid"]
|
||||
if len(queryUID) > 0 && !util.IsValidShortUID(queryUID) {
|
||||
|
@ -2,6 +2,7 @@ package queryhistory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
@ -41,6 +42,70 @@ func (s QueryHistoryService) createQuery(ctx context.Context, user *models.Signe
|
||||
return dto, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (s QueryHistoryService) deleteQuery(ctx context.Context, user *models.SignedInUser, UID string) (int64, error) {
|
||||
var queryID int64
|
||||
err := s.SQLStore.WithTransactionalDbSession(ctx, func(session *sqlstore.DBSession) error {
|
||||
|
@ -34,13 +34,22 @@ type CreateQueryInQueryHistoryCommand struct {
|
||||
Queries *simplejson.Json `json:"queries"`
|
||||
}
|
||||
|
||||
type SearchInQueryHistoryQuery struct {
|
||||
DatasourceUIDs []string `json:"datasourceUids"`
|
||||
SearchString string `json:"searchString"`
|
||||
OnlyStarred bool `json:"onlyStarred"`
|
||||
Sort string `json:"sort"`
|
||||
Page int `json:"page"`
|
||||
Limit int `json:"limit"`
|
||||
}
|
||||
|
||||
type PatchQueryCommentInQueryHistoryCommand struct {
|
||||
Comment string `json:"comment"`
|
||||
}
|
||||
|
||||
type QueryHistoryDTO struct {
|
||||
UID string `json:"uid"`
|
||||
DatasourceUID string `json:"datasourceUid"`
|
||||
UID string `json:"uid" xorm:"uid"`
|
||||
DatasourceUID string `json:"datasourceUid" xorm:"datasource_uid"`
|
||||
CreatedBy int64 `json:"createdBy"`
|
||||
CreatedAt int64 `json:"createdAt"`
|
||||
Comment string `json:"comment"`
|
||||
@ -53,6 +62,17 @@ type QueryHistoryResponse struct {
|
||||
Result QueryHistoryDTO `json:"result"`
|
||||
}
|
||||
|
||||
type QueryHistorySearchResult struct {
|
||||
TotalCount int `json:"totalCount"`
|
||||
QueryHistory []QueryHistoryDTO `json:"queryHistory"`
|
||||
Page int `json:"page"`
|
||||
PerPage int `json:"perPage"`
|
||||
}
|
||||
|
||||
type QueryHistorySearchResponse struct {
|
||||
Result QueryHistorySearchResult `json:"result"`
|
||||
}
|
||||
|
||||
// DeleteQueryFromQueryHistoryResponse is the response struct for deleting a query from query history
|
||||
type DeleteQueryFromQueryHistoryResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
|
@ -28,6 +28,7 @@ func ProvideService(cfg *setting.Cfg, sqlStore *sqlstore.SQLStore, routeRegister
|
||||
|
||||
type Service interface {
|
||||
CreateQueryInQueryHistory(ctx context.Context, user *models.SignedInUser, cmd CreateQueryInQueryHistoryCommand) (QueryHistoryDTO, error)
|
||||
SearchInQueryHistory(ctx context.Context, user *models.SignedInUser, query SearchInQueryHistoryQuery) (QueryHistorySearchResult, error)
|
||||
DeleteQueryFromQueryHistory(ctx context.Context, user *models.SignedInUser, UID string) (int64, error)
|
||||
PatchQueryCommentInQueryHistory(ctx context.Context, user *models.SignedInUser, UID string, cmd PatchQueryCommentInQueryHistoryCommand) (QueryHistoryDTO, error)
|
||||
StarQueryInQueryHistory(ctx context.Context, user *models.SignedInUser, UID string) (QueryHistoryDTO, error)
|
||||
@ -45,6 +46,10 @@ func (s QueryHistoryService) CreateQueryInQueryHistory(ctx context.Context, user
|
||||
return s.createQuery(ctx, user, cmd)
|
||||
}
|
||||
|
||||
func (s QueryHistoryService) SearchInQueryHistory(ctx context.Context, user *models.SignedInUser, query SearchInQueryHistoryQuery) (QueryHistorySearchResult, error) {
|
||||
return s.searchQueries(ctx, user, query)
|
||||
}
|
||||
|
||||
func (s QueryHistoryService) DeleteQueryFromQueryHistory(ctx context.Context, user *models.SignedInUser, UID string) (int64, error) {
|
||||
return s.deleteQuery(ctx, user, UID)
|
||||
}
|
||||
|
@ -1,3 +1,6 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package queryhistory
|
||||
|
||||
import (
|
||||
|
@ -1,3 +1,6 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package queryhistory
|
||||
|
||||
import (
|
||||
|
@ -1,3 +1,6 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package queryhistory
|
||||
|
||||
import (
|
||||
|
117
pkg/services/queryhistory/queryhistory_search_test.go
Normal file
117
pkg/services/queryhistory/queryhistory_search_test.go
Normal file
@ -0,0 +1,117 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package queryhistory
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetQueriesFromQueryHistory(t *testing.T) {
|
||||
testScenario(t, "When users tries to get query without datasourceUid, it should fail",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
resp := sc.service.searchHandler(sc.reqContext)
|
||||
require.Equal(t, 500, resp.Status())
|
||||
})
|
||||
|
||||
testScenario(t, "When users tries to get query in empty query history, it should return empty result",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.Req.Form.Add("datasourceUid", "test")
|
||||
resp := sc.service.searchHandler(sc.reqContext)
|
||||
var response QueryHistorySearchResponse
|
||||
err := json.Unmarshal(resp.Body(), &response)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
require.Equal(t, 0, response.Result.TotalCount)
|
||||
})
|
||||
|
||||
testScenarioWithQueryInQueryHistory(t, "When users tries to get query with valid datasourceUid, it should succeed",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.Req.Form.Add("datasourceUid", sc.initialResult.Result.DatasourceUID)
|
||||
resp := sc.service.searchHandler(sc.reqContext)
|
||||
var response QueryHistorySearchResponse
|
||||
err := json.Unmarshal(resp.Body(), &response)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
require.Equal(t, 1, response.Result.TotalCount)
|
||||
})
|
||||
|
||||
testScenarioWithMultipleQueriesInQueryHistory(t, "When users tries to get queries with datasourceUid, it should return correct queries",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.Req.Form.Add("datasourceUid", sc.initialResult.Result.DatasourceUID)
|
||||
resp := sc.service.searchHandler(sc.reqContext)
|
||||
var response QueryHistorySearchResponse
|
||||
err := json.Unmarshal(resp.Body(), &response)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
require.Equal(t, 2, response.Result.TotalCount)
|
||||
require.Equal(t, true, response.Result.QueryHistory[0].Starred)
|
||||
require.Equal(t, false, response.Result.QueryHistory[1].Starred)
|
||||
})
|
||||
|
||||
testScenarioWithMultipleQueriesInQueryHistory(t, "When users tries to get queries with datasourceUid and sort, it should return correct queries",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.Req.Form.Add("datasourceUid", sc.initialResult.Result.DatasourceUID)
|
||||
sc.reqContext.Req.Form.Add("sort", "time-asc")
|
||||
resp := sc.service.searchHandler(sc.reqContext)
|
||||
var response QueryHistorySearchResponse
|
||||
err := json.Unmarshal(resp.Body(), &response)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
require.Equal(t, 2, response.Result.TotalCount)
|
||||
require.Equal(t, false, response.Result.QueryHistory[0].Starred)
|
||||
require.Equal(t, true, response.Result.QueryHistory[1].Starred)
|
||||
})
|
||||
|
||||
testScenarioWithMultipleQueriesInQueryHistory(t, "When users tries to get queries with invalid datasourceUid, it should return empty result",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.Req.Form.Add("datasourceUid", "non-existent")
|
||||
resp := sc.service.searchHandler(sc.reqContext)
|
||||
var response QueryHistorySearchResponse
|
||||
err := json.Unmarshal(resp.Body(), &response)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
require.Equal(t, 0, response.Result.TotalCount)
|
||||
})
|
||||
|
||||
testScenarioWithMultipleQueriesInQueryHistory(t, "When users tries to get queries with multiple datasourceUid, it should return correct queries",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.Req.Form.Add("datasourceUid", testDsUID1)
|
||||
sc.reqContext.Req.Form.Add("datasourceUid", testDsUID2)
|
||||
resp := sc.service.searchHandler(sc.reqContext)
|
||||
var response QueryHistorySearchResponse
|
||||
err := json.Unmarshal(resp.Body(), &response)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
require.Equal(t, 3, response.Result.TotalCount)
|
||||
})
|
||||
|
||||
testScenarioWithMultipleQueriesInQueryHistory(t, "When users tries to get starred queries, it should return correct queries",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.Req.Form.Add("datasourceUid", testDsUID1)
|
||||
sc.reqContext.Req.Form.Add("onlyStarred", "true")
|
||||
resp := sc.service.searchHandler(sc.reqContext)
|
||||
var response QueryHistorySearchResponse
|
||||
err := json.Unmarshal(resp.Body(), &response)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
require.Equal(t, 1, response.Result.TotalCount)
|
||||
require.Equal(t, true, response.Result.QueryHistory[0].Starred)
|
||||
})
|
||||
|
||||
testScenarioWithMultipleQueriesInQueryHistory(t, "When users tries to get queries including search string, it should return correct queries",
|
||||
func(t *testing.T, sc scenarioContext) {
|
||||
sc.reqContext.Req.Form.Add("datasourceUid", testDsUID1)
|
||||
sc.reqContext.Req.Form.Add("searchString", "2")
|
||||
resp := sc.service.searchHandler(sc.reqContext)
|
||||
var response QueryHistorySearchResponse
|
||||
err := json.Unmarshal(resp.Body(), &response)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
require.Equal(t, 1, response.Result.TotalCount)
|
||||
require.Equal(t, true, response.Result.QueryHistory[0].Starred)
|
||||
})
|
||||
}
|
@ -1,3 +1,6 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package queryhistory
|
||||
|
||||
import (
|
||||
|
@ -1,3 +1,6 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package queryhistory
|
||||
|
||||
import (
|
||||
@ -6,6 +9,7 @@ import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -21,6 +25,8 @@ import (
|
||||
var (
|
||||
testOrgID = int64(1)
|
||||
testUserID = int64(1)
|
||||
testDsUID1 = "NCzh67i"
|
||||
testDsUID2 = "ABch1a1"
|
||||
)
|
||||
|
||||
type scenarioContext struct {
|
||||
@ -37,6 +43,7 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
|
||||
t.Run(desc, func(t *testing.T) {
|
||||
ctx := web.Context{Req: &http.Request{
|
||||
Header: http.Header{},
|
||||
Form: url.Values{},
|
||||
}}
|
||||
ctx.Req.Header.Add("Content-Type", "application/json")
|
||||
sqlStore := sqlstore.InitTestDB(t)
|
||||
@ -82,7 +89,7 @@ func testScenarioWithQueryInQueryHistory(t *testing.T, desc string, fn func(t *t
|
||||
|
||||
testScenario(t, desc, func(t *testing.T, sc scenarioContext) {
|
||||
command := CreateQueryInQueryHistoryCommand{
|
||||
DatasourceUID: "NCzh67i",
|
||||
DatasourceUID: testDsUID1,
|
||||
Queries: simplejson.NewFromAny(map[string]interface{}{
|
||||
"expr": "test",
|
||||
}),
|
||||
@ -94,6 +101,50 @@ func testScenarioWithQueryInQueryHistory(t *testing.T, desc string, fn func(t *t
|
||||
})
|
||||
}
|
||||
|
||||
func testScenarioWithMultipleQueriesInQueryHistory(t *testing.T, desc string, fn func(t *testing.T, sc scenarioContext)) {
|
||||
t.Helper()
|
||||
|
||||
testScenario(t, desc, func(t *testing.T, sc scenarioContext) {
|
||||
command1 := CreateQueryInQueryHistoryCommand{
|
||||
DatasourceUID: testDsUID1,
|
||||
Queries: simplejson.NewFromAny(map[string]interface{}{
|
||||
"expr": "test",
|
||||
}),
|
||||
}
|
||||
sc.reqContext.Req.Body = mockRequestBody(command1)
|
||||
resp1 := sc.service.createHandler(sc.reqContext)
|
||||
sc.initialResult = validateAndUnMarshalResponse(t, resp1)
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
command2 := CreateQueryInQueryHistoryCommand{
|
||||
DatasourceUID: testDsUID1,
|
||||
Queries: simplejson.NewFromAny(map[string]interface{}{
|
||||
"expr": "test2",
|
||||
}),
|
||||
}
|
||||
sc.reqContext.Req.Body = mockRequestBody(command2)
|
||||
resp2 := sc.service.createHandler(sc.reqContext)
|
||||
result2 := validateAndUnMarshalResponse(t, resp2)
|
||||
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": result2.Result.UID})
|
||||
sc.service.starHandler(sc.reqContext)
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
command3 := CreateQueryInQueryHistoryCommand{
|
||||
DatasourceUID: testDsUID2,
|
||||
Queries: simplejson.NewFromAny(map[string]interface{}{
|
||||
"expr": "test2",
|
||||
}),
|
||||
}
|
||||
sc.reqContext.Req.Body = mockRequestBody(command3)
|
||||
resp3 := sc.service.createHandler(sc.reqContext)
|
||||
result3 := validateAndUnMarshalResponse(t, resp3)
|
||||
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": result3.Result.UID})
|
||||
sc.service.starHandler(sc.reqContext)
|
||||
|
||||
fn(t, sc)
|
||||
})
|
||||
}
|
||||
|
||||
func mockRequestBody(v interface{}) io.ReadCloser {
|
||||
b, _ := json.Marshal(v)
|
||||
return io.NopCloser(bytes.NewReader(b))
|
||||
|
@ -1,7 +1,9 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package queryhistory
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
@ -20,7 +22,6 @@ func TestUnstarQueryInQueryHistory(t *testing.T) {
|
||||
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": sc.initialResult.Result.UID})
|
||||
sc.service.starHandler(sc.reqContext)
|
||||
resp := sc.service.unstarHandler(sc.reqContext)
|
||||
fmt.Println(resp)
|
||||
require.Equal(t, 200, resp.Status())
|
||||
})
|
||||
|
||||
|
49
pkg/services/queryhistory/writers.go
Normal file
49
pkg/services/queryhistory/writers.go
Normal file
@ -0,0 +1,49 @@
|
||||
package queryhistory
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
)
|
||||
|
||||
func writeStarredSQL(query SearchInQueryHistoryQuery, sqlStore *sqlstore.SQLStore, builder *sqlstore.SQLBuilder) {
|
||||
if query.OnlyStarred {
|
||||
builder.Write(sqlStore.Dialect.BooleanStr(true) + ` AS starred
|
||||
FROM query_history
|
||||
INNER JOIN query_history_star ON query_history_star.query_uid = query_history.uid
|
||||
`)
|
||||
} else {
|
||||
builder.Write(` CASE WHEN query_history_star.query_uid IS NULL THEN ` + sqlStore.Dialect.BooleanStr(false) + ` ELSE ` + sqlStore.Dialect.BooleanStr(true) + ` END AS starred
|
||||
FROM query_history
|
||||
LEFT JOIN query_history_star ON query_history_star.query_uid = query_history.uid
|
||||
`)
|
||||
}
|
||||
}
|
||||
|
||||
func writeFiltersSQL(query SearchInQueryHistoryQuery, user *models.SignedInUser, sqlStore *sqlstore.SQLStore, builder *sqlstore.SQLBuilder) {
|
||||
params := []interface{}{user.OrgId, user.UserId, "%" + query.SearchString + "%"}
|
||||
for _, uid := range query.DatasourceUIDs {
|
||||
params = append(params, uid)
|
||||
}
|
||||
var sql bytes.Buffer
|
||||
sql.WriteString(" WHERE query_history.org_id = ? AND query_history.created_by = ? AND query_history.queries " + sqlStore.Dialect.LikeStr() + " ? AND query_history.datasource_uid IN (? " + strings.Repeat(",?", len(query.DatasourceUIDs)-1) + ") ")
|
||||
builder.Write(sql.String(), params...)
|
||||
}
|
||||
|
||||
func writeSortSQL(query SearchInQueryHistoryQuery, sqlStore *sqlstore.SQLStore, builder *sqlstore.SQLBuilder) {
|
||||
if query.Sort == "time-asc" {
|
||||
builder.Write(" ORDER BY created_at ASC ")
|
||||
} else {
|
||||
builder.Write(" ORDER BY created_at DESC ")
|
||||
}
|
||||
}
|
||||
|
||||
func writeLimitSQL(query SearchInQueryHistoryQuery, sqlStore *sqlstore.SQLStore, builder *sqlstore.SQLBuilder) {
|
||||
builder.Write(" LIMIT ? ", query.Limit)
|
||||
}
|
||||
|
||||
func writeOffsetSQL(query SearchInQueryHistoryQuery, sqlStore *sqlstore.SQLStore, builder *sqlstore.SQLBuilder) {
|
||||
builder.Write(" OFFSET ? ", query.Limit*(query.Page-1))
|
||||
}
|
Loading…
Reference in New Issue
Block a user