2022-01-29 00:55:09 +08:00
package queryhistory
import (
"net/http"
2024-06-06 04:31:06 +08:00
"github.com/grafana/grafana-plugin-sdk-go/backend/gtime"
2022-01-29 00:55:09 +08:00
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/grafana/grafana/pkg/middleware"
2024-08-01 02:10:52 +08:00
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
2023-01-27 15:50:36 +08:00
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
2024-07-20 03:44:58 +08:00
"github.com/grafana/grafana/pkg/services/org"
2022-02-04 23:14:36 +08:00
"github.com/grafana/grafana/pkg/util"
2022-01-29 00:55:09 +08:00
"github.com/grafana/grafana/pkg/web"
)
func ( s * QueryHistoryService ) registerAPIEndpoints ( ) {
s . RouteRegister . Group ( "/api/query-history" , func ( entities routing . RouteRegister ) {
2024-08-01 02:10:52 +08:00
entities . Post ( "/" , middleware . ReqSignedIn , routing . Wrap ( s . permissionsMiddleware ( s . createHandler , "Failed to create query history" ) ) )
entities . Get ( "/" , middleware . ReqSignedIn , routing . Wrap ( s . permissionsMiddleware ( s . searchHandler , "Failed to get query history" ) ) )
entities . Delete ( "/:uid" , middleware . ReqSignedIn , routing . Wrap ( s . permissionsMiddleware ( s . deleteHandler , "Failed to delete query history" ) ) )
entities . Post ( "/star/:uid" , middleware . ReqSignedIn , routing . Wrap ( s . permissionsMiddleware ( s . starHandler , "Failed to star query history" ) ) )
entities . Delete ( "/star/:uid" , middleware . ReqSignedIn , routing . Wrap ( s . permissionsMiddleware ( s . unstarHandler , "Failed to unstar query history" ) ) )
entities . Patch ( "/:uid" , middleware . ReqSignedIn , routing . Wrap ( s . permissionsMiddleware ( s . patchCommentHandler , "Failed to update comment of query in query history" ) ) )
2022-01-29 00:55:09 +08:00
} )
}
2024-08-01 02:10:52 +08:00
type CallbackHandler func ( c * contextmodel . ReqContext ) response . Response
func ( s * QueryHistoryService ) permissionsMiddleware ( handler CallbackHandler , errorMessage string ) CallbackHandler {
return func ( c * contextmodel . ReqContext ) response . Response {
hasAccess := ac . HasAccess ( s . accessControl , c )
2025-03-25 21:55:59 +08:00
if c . GetOrgRole ( ) == org . RoleViewer && ! hasAccess ( ac . EvalPermission ( ac . ActionDatasourcesExplore ) ) {
2024-08-01 02:10:52 +08:00
return response . Error ( http . StatusUnauthorized , errorMessage , nil )
}
return handler ( c )
}
}
2022-07-27 21:54:37 +08:00
// swagger:route POST /query-history query_history createQuery
//
// Add query to query history.
//
// Adds new query to query history.
//
// Responses:
// 200: getQueryHistoryResponse
// 400: badRequestError
// 401: unauthorisedError
// 500: internalServerError
2023-01-27 15:50:36 +08:00
func ( s * QueryHistoryService ) createHandler ( c * contextmodel . ReqContext ) response . Response {
2022-01-29 00:55:09 +08:00
cmd := CreateQueryInQueryHistoryCommand { }
if err := web . Bind ( c . Req , & cmd ) ; err != nil {
return response . Error ( http . StatusBadRequest , "bad request data" , err )
}
2022-02-04 23:14:36 +08:00
query , err := s . CreateQueryInQueryHistory ( c . Req . Context ( ) , c . SignedInUser , cmd )
2022-01-29 00:55:09 +08:00
if err != nil {
2022-02-04 23:14:36 +08:00
return response . Error ( http . StatusInternalServerError , "Failed to create query history" , err )
2022-01-29 00:55:09 +08:00
}
2022-02-04 23:14:36 +08:00
return response . JSON ( http . StatusOK , QueryHistoryResponse { Result : query } )
}
2022-07-27 21:54:37 +08:00
// swagger:route GET /query-history query_history searchQueries
//
// Query history search.
//
// 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.
//
// Responses:
// 200: getQueryHistorySearchResponse
// 401: unauthorisedError
// 500: internalServerError
2023-01-27 15:50:36 +08:00
func ( s * QueryHistoryService ) searchHandler ( c * contextmodel . ReqContext ) response . Response {
2024-06-06 04:31:06 +08:00
timeRange := gtime . NewTimeRange ( c . Query ( "from" ) , c . Query ( "to" ) )
2022-04-29 15:55:33 +08:00
2022-03-07 19:28:04 +08:00
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" ) ,
2022-04-29 15:55:33 +08:00
From : timeRange . GetFromAsSecondsEpoch ( ) ,
To : timeRange . GetToAsSecondsEpoch ( ) ,
2022-03-07 19:28:04 +08:00
}
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 } )
}
2022-07-27 21:54:37 +08:00
// swagger:route DELETE /query-history/{query_history_uid} query_history deleteQuery
//
// Delete query in query history.
//
// Deletes an existing query in query history as specified by the UID. This operation cannot be reverted.
//
// Responses:
// 200: getQueryHistoryDeleteQueryResponse
// 401: unauthorisedError
// 500: internalServerError
2023-01-27 15:50:36 +08:00
func ( s * QueryHistoryService ) deleteHandler ( c * contextmodel . ReqContext ) response . Response {
2022-02-04 23:14:36 +08:00
queryUID := web . Params ( c . Req ) [ ":uid" ]
2022-02-24 00:03:04 +08:00
if len ( queryUID ) > 0 && ! util . IsValidShortUID ( queryUID ) {
2022-02-04 23:14:36 +08:00
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 )
}
2022-06-13 15:47:40 +08:00
return response . JSON ( http . StatusOK , QueryHistoryDeleteQueryResponse {
2022-02-04 23:14:36 +08:00
Message : "Query deleted" ,
ID : id ,
} )
2022-01-29 00:55:09 +08:00
}
2022-02-15 22:43:17 +08:00
2022-07-27 21:54:37 +08:00
// swagger:route PATCH /query-history/{query_history_uid} query_history patchQueryComment
//
// Update comment for query in query history.
//
// Updates comment for query in query history as specified by the UID.
//
// Responses:
// 200: getQueryHistoryResponse
// 400: badRequestError
// 401: unauthorisedError
// 500: internalServerError
2023-01-27 15:50:36 +08:00
func ( s * QueryHistoryService ) patchCommentHandler ( c * contextmodel . ReqContext ) response . Response {
2022-02-15 22:43:17 +08:00
queryUID := web . Params ( c . Req ) [ ":uid" ]
2022-02-24 00:03:04 +08:00
if len ( queryUID ) > 0 && ! util . IsValidShortUID ( queryUID ) {
2022-02-15 22:43:17 +08:00
return response . Error ( http . StatusNotFound , "Query in query history not found" , nil )
}
cmd := PatchQueryCommentInQueryHistoryCommand { }
if err := web . Bind ( c . Req , & cmd ) ; err != nil {
return response . Error ( http . StatusBadRequest , "bad request data" , err )
}
query , err := s . PatchQueryCommentInQueryHistory ( c . Req . Context ( ) , c . SignedInUser , queryUID , cmd )
if err != nil {
return response . Error ( http . StatusInternalServerError , "Failed to update comment of query in query history" , err )
}
return response . JSON ( http . StatusOK , QueryHistoryResponse { Result : query } )
}
2022-02-24 00:03:04 +08:00
2022-07-27 21:54:37 +08:00
// swagger:route POST /query-history/star/{query_history_uid} query_history starQuery
//
// Add star to query in query history.
//
// Adds star to query in query history as specified by the UID.
//
// Responses:
// 200: getQueryHistoryResponse
// 401: unauthorisedError
// 500: internalServerError
2023-01-27 15:50:36 +08:00
func ( s * QueryHistoryService ) starHandler ( c * contextmodel . ReqContext ) response . Response {
2022-02-24 00:03:04 +08:00
queryUID := web . Params ( c . Req ) [ ":uid" ]
if len ( queryUID ) > 0 && ! util . IsValidShortUID ( queryUID ) {
return response . Error ( http . StatusNotFound , "Query in query history not found" , nil )
}
2025-10-08 04:28:34 +08:00
if s . k8sClients != nil {
if err := s . k8sClients . AddStar ( c , queryUID ) ; err != nil {
return response . Error ( http . StatusInternalServerError , "Failed to star query in query history" , err )
}
return response . JSON ( http . StatusOK , QueryHistoryResponse {
Result : QueryHistoryDTO {
UID : queryUID ,
Starred : true ,
} } )
}
2022-02-24 00:03:04 +08:00
query , err := s . StarQueryInQueryHistory ( c . Req . Context ( ) , c . SignedInUser , queryUID )
if err != nil {
return response . Error ( http . StatusInternalServerError , "Failed to star query in query history" , err )
}
return response . JSON ( http . StatusOK , QueryHistoryResponse { Result : query } )
}
2022-07-27 21:54:37 +08:00
// swagger:route DELETE /query-history/star/{query_history_uid} query_history unstarQuery
//
// Remove star to query in query history.
//
// Removes star from query in query history as specified by the UID.
//
// Responses:
// 200: getQueryHistoryResponse
// 401: unauthorisedError
// 500: internalServerError
2023-01-27 15:50:36 +08:00
func ( s * QueryHistoryService ) unstarHandler ( c * contextmodel . ReqContext ) response . Response {
2022-02-24 00:03:04 +08:00
queryUID := web . Params ( c . Req ) [ ":uid" ]
if len ( queryUID ) > 0 && ! util . IsValidShortUID ( queryUID ) {
return response . Error ( http . StatusNotFound , "Query in query history not found" , nil )
}
2025-10-08 04:28:34 +08:00
if s . k8sClients != nil {
if err := s . k8sClients . RemoveStar ( c , queryUID ) ; err != nil {
return response . Error ( http . StatusInternalServerError , "Failed to star query in query history" , err )
}
return response . JSON ( http . StatusOK , QueryHistoryResponse {
Result : QueryHistoryDTO {
UID : queryUID ,
Starred : true ,
} } )
}
2022-02-24 00:03:04 +08:00
query , err := s . UnstarQueryInQueryHistory ( c . Req . Context ( ) , c . SignedInUser , queryUID )
if err != nil {
return response . Error ( http . StatusInternalServerError , "Failed to unstar query in query history" , err )
}
return response . JSON ( http . StatusOK , QueryHistoryResponse { Result : query } )
}
2022-04-14 15:33:41 +08:00
2022-07-27 21:54:37 +08:00
// swagger:parameters starQuery patchQueryComment deleteQuery unstarQuery
type QueryHistoryByUID struct {
// in:path
// required:true
UID string ` json:"query_history_uid" `
}
// swagger:parameters searchQueries
type SearchQueriesParams struct {
// List of data source UIDs to search for
// in:query
// required: false
// type: array
// collectionFormat: multi
DatasourceUid [ ] string ` json:"datasourceUid" `
// Text inside query or comments that is searched for
// in:query
// required: false
SearchString string ` json:"searchString" `
// Flag indicating if only starred queries should be returned
// in:query
// required: false
OnlyStarred bool ` json:"onlyStarred" `
// Sort method
// in:query
// required: false
// default: time-desc
// Enum: time-desc,time-asc
Sort string ` json:"sort" `
// Use this parameter to access hits beyond limit. Numbering starts at 1. limit param acts as page size.
// in:query
// required: false
Page int ` json:"page" `
// Limit the number of returned results
// in:query
// required: false
Limit int ` json:"limit" `
// From range for the query history search
// in:query
// required: false
From int64 ` json:"from" `
// To range for the query history search
// in:query
// required: false
To int64 ` json:"to" `
}
// swagger:parameters createQuery
type CreateQueryParams struct {
// in:body
// required:true
Body CreateQueryInQueryHistoryCommand ` json:"body" `
}
// swagger:parameters patchQueryComment
type PatchQueryCommentParams struct {
// in:body
// required:true
Body PatchQueryCommentInQueryHistoryCommand ` json:"body" `
}
//swagger:response getQueryHistorySearchResponse
type GetQueryHistorySearchResponse struct {
// in: body
Body QueryHistorySearchResponse ` json:"body" `
}
// swagger:response getQueryHistoryResponse
type GetQueryHistoryResponse struct {
// in: body
Body QueryHistoryResponse ` json:"body" `
}
// swagger:response getQueryHistoryDeleteQueryResponse
type GetQueryHistoryDeleteQueryResponse struct {
// in: body
Body QueryHistoryDeleteQueryResponse ` json:"body" `
}