SSE/Alerting: First pass at query/condition translation (#31693)
- Takes the conditions property from the settings column of an alert from alerts table and turns into an ng alerting condition with the queries and classic condition. - Has temp API rest endpoint that will take the dashboard conditions json, translate it to SEE queries + classic condition, and execute it (only enabled in dev mode). - Changes expressions to catch query responses with a non-nil error property - Adds two new states for an NG instance result (NoData, Error) and updates evaluation to match those states - Changes the AsDataFrame (for frontend) from Bool to string to represent additional states - Fix bug in condition model to accept first Operator as empty string. - In ngalert, adds GetQueryDataRequest, which was part of execute and is still called from there. But this allows me to get the Expression request from a condition to make the "pipeline" can be built. - Update AsDataFrame for evalresult to be row based so it displays a little better for now
This commit is contained in:
@@ -16,6 +16,8 @@ import (
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/expr/translate"
|
||||
"github.com/grafana/grafana/pkg/middleware"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
@@ -73,6 +75,111 @@ func (api *API) RegisterAPIEndpoints() {
|
||||
api.RouteRegister.Group("/api/alert-instances", func(alertInstances routing.RouteRegister) {
|
||||
alertInstances.Get("", middleware.ReqSignedIn, routing.Wrap(api.listAlertInstancesEndpoint))
|
||||
})
|
||||
|
||||
if api.Cfg.Env == setting.Dev {
|
||||
api.RouteRegister.Group("/api/alert-definitions", func(alertDefinitions routing.RouteRegister) {
|
||||
alertDefinitions.Post("/evalOld", middleware.ReqSignedIn, routing.Wrap(api.conditionEvalOldEndpoint))
|
||||
})
|
||||
api.RouteRegister.Group("/api/alert-definitions", func(alertDefinitions routing.RouteRegister) {
|
||||
alertDefinitions.Get("/evalOldByID/:id", middleware.ReqSignedIn, routing.Wrap(api.conditionEvalOldEndpointByID))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// conditionEvalEndpoint handles POST /api/alert-definitions/evalOld.
|
||||
func (api *API) conditionEvalOldEndpoint(c *models.ReqContext) response.Response {
|
||||
b, err := c.Req.Body().Bytes()
|
||||
if err != nil {
|
||||
response.Error(400, "failed to read body", err)
|
||||
}
|
||||
evalCond, err := translate.DashboardAlertConditions(b, c.OrgId)
|
||||
if err != nil {
|
||||
return response.Error(400, "Failed to translate alert conditions", err)
|
||||
}
|
||||
|
||||
if err := api.validateCondition(*evalCond, c.SignedInUser, c.SkipCache); err != nil {
|
||||
return response.Error(400, "invalid condition", err)
|
||||
}
|
||||
|
||||
//now := cmd.Now
|
||||
//if now.IsZero() {
|
||||
//now := timeNow()
|
||||
//}
|
||||
|
||||
evaluator := eval.Evaluator{Cfg: api.Cfg}
|
||||
evalResults, err := evaluator.ConditionEval(evalCond, timeNow(), api.DataService)
|
||||
if err != nil {
|
||||
return response.Error(400, "Failed to evaluate conditions", err)
|
||||
}
|
||||
|
||||
frame := evalResults.AsDataFrame()
|
||||
df := plugins.NewDecodedDataFrames([]*data.Frame{&frame})
|
||||
instances, err := df.Encoded()
|
||||
if err != nil {
|
||||
return response.Error(400, "Failed to encode result dataframes", err)
|
||||
}
|
||||
|
||||
return response.JSON(200, util.DynMap{
|
||||
"instances": instances,
|
||||
})
|
||||
}
|
||||
|
||||
// conditionEvalEndpoint handles POST /api/alert-definitions/evalOld.
|
||||
func (api *API) conditionEvalOldEndpointByID(c *models.ReqContext) response.Response {
|
||||
id := c.ParamsInt64("id")
|
||||
if id == 0 {
|
||||
return response.Error(400, "missing id", nil)
|
||||
}
|
||||
|
||||
getAlert := &models.GetAlertByIdQuery{
|
||||
Id: id,
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(getAlert); err != nil {
|
||||
return response.Error(400, fmt.Sprintf("could find alert with id %v", id), err)
|
||||
}
|
||||
|
||||
if getAlert.Result.OrgId != c.SignedInUser.OrgId {
|
||||
return response.Error(403, "alert does not match organization of user", nil)
|
||||
}
|
||||
|
||||
settings := getAlert.Result.Settings
|
||||
|
||||
sb, err := settings.ToDB()
|
||||
if err != nil {
|
||||
return response.Error(400, "failed to marshal alert settings", err)
|
||||
}
|
||||
|
||||
evalCond, err := translate.DashboardAlertConditions(sb, c.OrgId)
|
||||
if err != nil {
|
||||
return response.Error(400, "Failed to translate alert conditions", err)
|
||||
}
|
||||
|
||||
if err := api.validateCondition(*evalCond, c.SignedInUser, c.SkipCache); err != nil {
|
||||
return response.Error(400, "invalid condition", err)
|
||||
}
|
||||
|
||||
//now := cmd.Now
|
||||
//if now.IsZero() {
|
||||
//now := timeNow()
|
||||
//}
|
||||
|
||||
evaluator := eval.Evaluator{Cfg: api.Cfg}
|
||||
evalResults, err := evaluator.ConditionEval(evalCond, timeNow(), api.DataService)
|
||||
if err != nil {
|
||||
return response.Error(400, "Failed to evaluate conditions", err)
|
||||
}
|
||||
|
||||
frame := evalResults.AsDataFrame()
|
||||
df := plugins.NewDecodedDataFrames([]*data.Frame{&frame})
|
||||
instances, err := df.Encoded()
|
||||
if err != nil {
|
||||
return response.Error(400, "Failed to encode result dataframes", err)
|
||||
}
|
||||
|
||||
return response.JSON(200, util.DynMap{
|
||||
"instances": instances,
|
||||
})
|
||||
}
|
||||
|
||||
// conditionEvalEndpoint handles POST /api/alert-definitions/eval.
|
||||
|
||||
Reference in New Issue
Block a user