mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Extend PUT rule-group route to write the entire rule group rather than top-level fields only (#53078)
* Wire up to full alert rule struct * Extract group change detection logic to dedicated file * GroupDiff -> GroupDelta for consistency * Calculate deltas and handle backwards compatible requests * Separate changes and insert/update/delete as needed * Regenerate files * Don't touch the DB if there are no changes * Quota checking, delete unused file * Mark modified records as provisioned * Validation + a couple API layer tests * Address linter errors * Fix issue with UID assignment and rule creation * Propagate top level group fields to all rules * Tests for repeated updates and versioning * Tests for quota and provenance checks * Fix linter errors * Regenerate * Factor out some shared logic * Drop unnecessary multiple nilchecks * Use alternative strategy for rolling UIDs on inserted rules * Fix tests, add back nilcheck, refresh UIDs during test * Address feedback * Add missing nil-check
This commit is contained in:
@@ -56,7 +56,7 @@ type AlertRuleService interface {
|
||||
UpdateAlertRule(ctx context.Context, rule alerting_models.AlertRule, provenance alerting_models.Provenance) (alerting_models.AlertRule, error)
|
||||
DeleteAlertRule(ctx context.Context, orgID int64, ruleUID string, provenance alerting_models.Provenance) error
|
||||
GetRuleGroup(ctx context.Context, orgID int64, folder, group string) (definitions.AlertRuleGroup, error)
|
||||
UpdateRuleGroup(ctx context.Context, orgID int64, folderUID, rulegroup string, interval int64) error
|
||||
ReplaceRuleGroup(ctx context.Context, orgID int64, group definitions.AlertRuleGroup, userID int64, provenance alerting_models.Provenance) error
|
||||
}
|
||||
|
||||
func (srv *ProvisioningSrv) RouteGetPolicyTree(c *models.ReqContext) response.Response {
|
||||
@@ -312,8 +312,13 @@ func (srv *ProvisioningSrv) RouteGetAlertRuleGroup(c *models.ReqContext, folder
|
||||
return response.JSON(http.StatusOK, g)
|
||||
}
|
||||
|
||||
func (srv *ProvisioningSrv) RoutePutAlertRuleGroup(c *models.ReqContext, ag definitions.AlertRuleGroupMetadata, folderUID string, group string) response.Response {
|
||||
err := srv.alertRules.UpdateRuleGroup(c.Req.Context(), c.OrgId, folderUID, group, ag.Interval)
|
||||
func (srv *ProvisioningSrv) RoutePutAlertRuleGroup(c *models.ReqContext, ag definitions.AlertRuleGroup, folderUID string, group string) response.Response {
|
||||
ag.FolderUID = folderUID
|
||||
ag.Title = group
|
||||
err := srv.alertRules.ReplaceRuleGroup(c.Req.Context(), c.OrgId, ag, c.UserId, alerting_models.ProvenanceAPI)
|
||||
if errors.Is(err, alerting_models.ErrAlertRuleFailedValidation) {
|
||||
return ErrResp(http.StatusBadRequest, err, "")
|
||||
}
|
||||
if err != nil {
|
||||
if errors.Is(err, store.ErrOptimisticLock) {
|
||||
return ErrResp(http.StatusConflict, err, "")
|
||||
|
||||
@@ -299,6 +299,37 @@ func TestProvisioningApi(t *testing.T) {
|
||||
|
||||
require.Equal(t, 404, response.Status())
|
||||
})
|
||||
|
||||
t.Run("are invalid at group level", func(t *testing.T) {
|
||||
t.Run("PUT returns 400", func(t *testing.T) {
|
||||
sut := createProvisioningSrvSut(t)
|
||||
rc := createTestRequestCtx()
|
||||
insertRule(t, sut, createTestAlertRule("rule", 1))
|
||||
group := createInvalidAlertRuleGroup()
|
||||
group.Interval = 0
|
||||
|
||||
response := sut.RoutePutAlertRuleGroup(&rc, group, "folder-uid", group.Title)
|
||||
|
||||
require.Equal(t, 400, response.Status())
|
||||
require.NotEmpty(t, response.Body())
|
||||
require.Contains(t, string(response.Body()), "invalid alert rule")
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("are invalid at rule level", func(t *testing.T) {
|
||||
t.Run("PUT returns 400", func(t *testing.T) {
|
||||
sut := createProvisioningSrvSut(t)
|
||||
rc := createTestRequestCtx()
|
||||
insertRule(t, sut, createTestAlertRule("rule", 1))
|
||||
group := createInvalidAlertRuleGroup()
|
||||
|
||||
response := sut.RoutePutAlertRuleGroup(&rc, group, "folder-uid", group.Title)
|
||||
|
||||
require.Equal(t, 400, response.Status())
|
||||
require.NotEmpty(t, response.Body())
|
||||
require.Contains(t, string(response.Body()), "invalid alert rule")
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -477,6 +508,14 @@ func createInvalidAlertRule() definitions.ProvisionedAlertRule {
|
||||
return definitions.ProvisionedAlertRule{}
|
||||
}
|
||||
|
||||
func createInvalidAlertRuleGroup() definitions.AlertRuleGroup {
|
||||
return definitions.AlertRuleGroup{
|
||||
Title: "invalid",
|
||||
Interval: 10,
|
||||
Rules: []models.AlertRule{{}},
|
||||
}
|
||||
}
|
||||
|
||||
func createTestAlertRule(title string, orgID int64) definitions.ProvisionedAlertRule {
|
||||
return definitions.ProvisionedAlertRule{
|
||||
OrgID: orgID,
|
||||
|
||||
@@ -135,7 +135,7 @@ func (f *ProvisioningApiHandler) RoutePutAlertRuleGroup(ctx *models.ReqContext)
|
||||
folderUIDParam := web.Params(ctx.Req)[":FolderUID"]
|
||||
groupParam := web.Params(ctx.Req)[":Group"]
|
||||
// Parse Request Body
|
||||
conf := apimodels.AlertRuleGroupMetadata{}
|
||||
conf := apimodels.AlertRuleGroup{}
|
||||
if err := web.Bind(ctx.Req, &conf); err != nil {
|
||||
return response.Error(http.StatusBadRequest, "bad request data", err)
|
||||
}
|
||||
|
||||
@@ -100,6 +100,6 @@ func (f *ProvisioningApiHandler) handleRouteGetAlertRuleGroup(ctx *models.ReqCon
|
||||
return f.svc.RouteGetAlertRuleGroup(ctx, folder, group)
|
||||
}
|
||||
|
||||
func (f *ProvisioningApiHandler) handleRoutePutAlertRuleGroup(ctx *models.ReqContext, ag apimodels.AlertRuleGroupMetadata, folder, group string) response.Response {
|
||||
func (f *ProvisioningApiHandler) handleRoutePutAlertRuleGroup(ctx *models.ReqContext, ag apimodels.AlertRuleGroup, folder, group string) response.Response {
|
||||
return f.svc.RoutePutAlertRuleGroup(ctx, ag, folder, group)
|
||||
}
|
||||
|
||||
@@ -3513,7 +3513,6 @@
|
||||
"$ref": "#/definitions/Duration"
|
||||
},
|
||||
"gettableAlert": {
|
||||
"description": "GettableAlert gettable alert",
|
||||
"properties": {
|
||||
"annotations": {
|
||||
"$ref": "#/definitions/labelSet"
|
||||
@@ -3569,7 +3568,6 @@
|
||||
"type": "object"
|
||||
},
|
||||
"gettableAlerts": {
|
||||
"description": "GettableAlerts gettable alerts",
|
||||
"items": {
|
||||
"$ref": "#/definitions/gettableAlert"
|
||||
},
|
||||
@@ -3735,7 +3733,6 @@
|
||||
"type": "array"
|
||||
},
|
||||
"postableSilence": {
|
||||
"description": "PostableSilence postable silence",
|
||||
"properties": {
|
||||
"comment": {
|
||||
"description": "comment",
|
||||
@@ -4183,15 +4180,15 @@
|
||||
"in": "body",
|
||||
"name": "Body",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/AlertRuleGroupMetadata"
|
||||
"$ref": "#/definitions/AlertRuleGroup"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "AlertRuleGroupMetadata",
|
||||
"description": "AlertRuleGroup",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/AlertRuleGroupMetadata"
|
||||
"$ref": "#/definitions/AlertRuleGroup"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
|
||||
@@ -151,7 +151,7 @@ func NewAlertRule(rule models.AlertRule, provenance models.Provenance) Provision
|
||||
// - application/json
|
||||
//
|
||||
// Responses:
|
||||
// 200: AlertRuleGroupMetadata
|
||||
// 200: AlertRuleGroup
|
||||
// 400: ValidationError
|
||||
|
||||
// swagger:parameters RouteGetAlertRuleGroup RoutePutAlertRuleGroup
|
||||
@@ -169,7 +169,7 @@ type RuleGroupPathParam struct {
|
||||
// swagger:parameters RoutePutAlertRuleGroup
|
||||
type AlertRuleGroupPayload struct {
|
||||
// in:body
|
||||
Body AlertRuleGroupMetadata
|
||||
Body AlertRuleGroup
|
||||
}
|
||||
|
||||
// swagger:model
|
||||
|
||||
@@ -3381,6 +3381,7 @@
|
||||
"type": "object"
|
||||
},
|
||||
"alertGroup": {
|
||||
"description": "AlertGroup alert group",
|
||||
"properties": {
|
||||
"alerts": {
|
||||
"description": "alerts",
|
||||
@@ -3568,7 +3569,6 @@
|
||||
"type": "object"
|
||||
},
|
||||
"gettableAlerts": {
|
||||
"description": "GettableAlerts gettable alerts",
|
||||
"items": {
|
||||
"$ref": "#/definitions/gettableAlert"
|
||||
},
|
||||
@@ -3624,7 +3624,6 @@
|
||||
"type": "object"
|
||||
},
|
||||
"gettableSilences": {
|
||||
"description": "GettableSilences gettable silences",
|
||||
"items": {
|
||||
"$ref": "#/definitions/gettableSilence"
|
||||
},
|
||||
@@ -3735,7 +3734,6 @@
|
||||
"type": "array"
|
||||
},
|
||||
"postableSilence": {
|
||||
"description": "PostableSilence postable silence",
|
||||
"properties": {
|
||||
"comment": {
|
||||
"description": "comment",
|
||||
@@ -5923,15 +5921,15 @@
|
||||
"in": "body",
|
||||
"name": "Body",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/AlertRuleGroupMetadata"
|
||||
"$ref": "#/definitions/AlertRuleGroup"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "AlertRuleGroupMetadata",
|
||||
"description": "AlertRuleGroup",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/AlertRuleGroupMetadata"
|
||||
"$ref": "#/definitions/AlertRuleGroup"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
|
||||
@@ -2075,15 +2075,15 @@
|
||||
"name": "Body",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/AlertRuleGroupMetadata"
|
||||
"$ref": "#/definitions/AlertRuleGroup"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "AlertRuleGroupMetadata",
|
||||
"description": "AlertRuleGroup",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/AlertRuleGroupMetadata"
|
||||
"$ref": "#/definitions/AlertRuleGroup"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
@@ -5886,6 +5886,7 @@
|
||||
}
|
||||
},
|
||||
"alertGroup": {
|
||||
"description": "AlertGroup alert group",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"alerts",
|
||||
@@ -6076,7 +6077,6 @@
|
||||
"$ref": "#/definitions/gettableAlert"
|
||||
},
|
||||
"gettableAlerts": {
|
||||
"description": "GettableAlerts gettable alerts",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/gettableAlert"
|
||||
@@ -6134,7 +6134,6 @@
|
||||
"$ref": "#/definitions/gettableSilence"
|
||||
},
|
||||
"gettableSilences": {
|
||||
"description": "GettableSilences gettable silences",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/gettableSilence"
|
||||
@@ -6246,7 +6245,6 @@
|
||||
}
|
||||
},
|
||||
"postableSilence": {
|
||||
"description": "PostableSilence postable silence",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"comment",
|
||||
|
||||
Reference in New Issue
Block a user