Browse Source

Add a per-notifier ShouldNotify()

This way we are able to edit notification behavior per notifier.
This would be usefull to let some notifiers send notifications,
even when the state doesn't change, or with custom condition.

Signed-off-by: Thibault Chataigner <t.chataigner@criteo.com>
Thibault Chataigner 8 years ago
parent
commit
fd633a1d5d

+ 0 - 8
pkg/services/alerting/eval_context.go

@@ -75,14 +75,6 @@ func (c *EvalContext) ShouldUpdateAlertState() bool {
 	return c.Rule.State != c.PrevAlertState
 	return c.Rule.State != c.PrevAlertState
 }
 }
 
 
-func (c *EvalContext) ShouldSendNotification() bool {
-	if (c.PrevAlertState == m.AlertStatePending) && (c.Rule.State == m.AlertStateOK) {
-		return false
-	}
-
-	return true
-}
-
 func (a *EvalContext) GetDurationMs() float64 {
 func (a *EvalContext) GetDurationMs() float64 {
 	return float64(a.EndTime.Nanosecond()-a.StartTime.Nanosecond()) / float64(1000000)
 	return float64(a.EndTime.Nanosecond()-a.StartTime.Nanosecond()) / float64(1000000)
 }
 }

+ 0 - 16
pkg/services/alerting/eval_context_test.go

@@ -28,21 +28,5 @@ func TestAlertingEvalContext(t *testing.T) {
 				So(ctx.ShouldUpdateAlertState(), ShouldBeFalse)
 				So(ctx.ShouldUpdateAlertState(), ShouldBeFalse)
 			})
 			})
 		})
 		})
-
-		Convey("Should send notifications", func() {
-			Convey("pending -> ok", func() {
-				ctx.PrevAlertState = models.AlertStatePending
-				ctx.Rule.State = models.AlertStateOK
-
-				So(ctx.ShouldSendNotification(), ShouldBeFalse)
-			})
-
-			Convey("ok -> alerting", func() {
-				ctx.PrevAlertState = models.AlertStateOK
-				ctx.Rule.State = models.AlertStateAlerting
-
-				So(ctx.ShouldSendNotification(), ShouldBeTrue)
-			})
-		})
 	})
 	})
 }
 }

+ 1 - 1
pkg/services/alerting/interfaces.go

@@ -15,7 +15,7 @@ type Notifier interface {
 	Notify(evalContext *EvalContext) error
 	Notify(evalContext *EvalContext) error
 	GetType() string
 	GetType() string
 	NeedsImage() bool
 	NeedsImage() bool
-	PassesFilter(rule *Rule) bool
+	ShouldNotify(evalContext *EvalContext) bool
 
 
 	GetNotifierId() int64
 	GetNotifierId() int64
 	GetIsDefault() bool
 	GetIsDefault() bool

+ 5 - 17
pkg/services/alerting/notifier.go

@@ -24,7 +24,7 @@ type NotifierPlugin struct {
 }
 }
 
 
 type NotificationService interface {
 type NotificationService interface {
-	Send(context *EvalContext) error
+	SendIfNeeded(context *EvalContext) error
 }
 }
 
 
 func NewNotificationService() NotificationService {
 func NewNotificationService() NotificationService {
@@ -41,8 +41,8 @@ func newNotificationService() *notificationService {
 	}
 	}
 }
 }
 
 
-func (n *notificationService) Send(context *EvalContext) error {
-	notifiers, err := n.getNotifiers(context.Rule.OrgId, context.Rule.Notifications, context)
+func (n *notificationService) SendIfNeeded(context *EvalContext) error {
+	notifiers, err := n.getNeededNotifiers(context.Rule.OrgId, context.Rule.Notifications, context)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -109,7 +109,7 @@ func (n *notificationService) uploadImage(context *EvalContext) (err error) {
 	return nil
 	return nil
 }
 }
 
 
-func (n *notificationService) getNotifiers(orgId int64, notificationIds []int64, context *EvalContext) (NotifierSlice, error) {
+func (n *notificationService) getNeededNotifiers(orgId int64, notificationIds []int64, context *EvalContext) (NotifierSlice, error) {
 	query := &m.GetAlertNotificationsToSendQuery{OrgId: orgId, Ids: notificationIds}
 	query := &m.GetAlertNotificationsToSendQuery{OrgId: orgId, Ids: notificationIds}
 
 
 	if err := bus.Dispatch(query); err != nil {
 	if err := bus.Dispatch(query); err != nil {
@@ -121,7 +121,7 @@ func (n *notificationService) getNotifiers(orgId int64, notificationIds []int64,
 		if not, err := n.createNotifierFor(notification); err != nil {
 		if not, err := n.createNotifierFor(notification); err != nil {
 			return nil, err
 			return nil, err
 		} else {
 		} else {
-			if shouldUseNotification(not, context) {
+			if not.ShouldNotify(context) {
 				result = append(result, not)
 				result = append(result, not)
 			}
 			}
 		}
 		}
@@ -139,18 +139,6 @@ func (n *notificationService) createNotifierFor(model *m.AlertNotification) (Not
 	return notifierPlugin.Factory(model)
 	return notifierPlugin.Factory(model)
 }
 }
 
 
-func shouldUseNotification(notifier Notifier, context *EvalContext) bool {
-	if !context.Firing {
-		return true
-	}
-
-	if context.Error != nil {
-		return true
-	}
-
-	return notifier.PassesFilter(context.Rule)
-}
-
 type NotifierFactory func(notification *m.AlertNotification) (Notifier, error)
 type NotifierFactory func(notification *m.AlertNotification) (Notifier, error)
 
 
 var notifierFactories map[string]*NotifierPlugin = make(map[string]*NotifierPlugin)
 var notifierFactories map[string]*NotifierPlugin = make(map[string]*NotifierPlugin)

+ 0 - 89
pkg/services/alerting/notifier_test.go

@@ -1,89 +0,0 @@
-package alerting
-
-import (
-	"testing"
-
-	"fmt"
-
-	"github.com/grafana/grafana/pkg/models"
-	m "github.com/grafana/grafana/pkg/models"
-	. "github.com/smartystreets/goconvey/convey"
-)
-
-type FakeNotifier struct {
-	FakeMatchResult bool
-}
-
-func (fn *FakeNotifier) GetType() string {
-	return "FakeNotifier"
-}
-
-func (fn *FakeNotifier) NeedsImage() bool {
-	return true
-}
-
-func (n *FakeNotifier) GetNotifierId() int64 {
-	return 0
-}
-
-func (n *FakeNotifier) GetIsDefault() bool {
-	return false
-}
-
-func (fn *FakeNotifier) Notify(alertResult *EvalContext) error { return nil }
-
-func (fn *FakeNotifier) PassesFilter(rule *Rule) bool {
-	return fn.FakeMatchResult
-}
-
-func TestAlertNotificationExtraction(t *testing.T) {
-
-	Convey("Notifier tests", t, func() {
-		Convey("none firing alerts", func() {
-			ctx := &EvalContext{
-				Firing: false,
-				Rule: &Rule{
-					State: m.AlertStateAlerting,
-				},
-			}
-			notifier := &FakeNotifier{FakeMatchResult: false}
-
-			So(shouldUseNotification(notifier, ctx), ShouldBeTrue)
-		})
-
-		Convey("execution error cannot be ignored", func() {
-			ctx := &EvalContext{
-				Firing: true,
-				Error:  fmt.Errorf("I used to be a programmer just like you"),
-				Rule: &Rule{
-					State: m.AlertStateOK,
-				},
-			}
-			notifier := &FakeNotifier{FakeMatchResult: false}
-
-			So(shouldUseNotification(notifier, ctx), ShouldBeTrue)
-		})
-
-		Convey("firing alert that match", func() {
-			ctx := &EvalContext{
-				Firing: true,
-				Rule: &Rule{
-					State: models.AlertStateAlerting,
-				},
-			}
-			notifier := &FakeNotifier{FakeMatchResult: true}
-
-			So(shouldUseNotification(notifier, ctx), ShouldBeTrue)
-		})
-
-		Convey("firing alert that dont match", func() {
-			ctx := &EvalContext{
-				Firing: true,
-				Rule:   &Rule{State: m.AlertStateOK},
-			}
-			notifier := &FakeNotifier{FakeMatchResult: false}
-
-			So(shouldUseNotification(notifier, ctx), ShouldBeFalse)
-		})
-	})
-}

+ 8 - 1
pkg/services/alerting/notifiers/base.go

@@ -2,6 +2,7 @@ package notifiers
 
 
 import (
 import (
 	"github.com/grafana/grafana/pkg/components/simplejson"
 	"github.com/grafana/grafana/pkg/components/simplejson"
+	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/services/alerting"
 	"github.com/grafana/grafana/pkg/services/alerting"
 )
 )
 
 
@@ -25,7 +26,13 @@ func NewNotifierBase(id int64, isDefault bool, name, notifierType string, model
 	}
 	}
 }
 }
 
 
-func (n *NotifierBase) PassesFilter(rule *alerting.Rule) bool {
+func defaultShouldNotify(context *alerting.EvalContext) bool {
+	if context.PrevAlertState == context.Rule.State {
+		return false
+	}
+	if (context.PrevAlertState == m.AlertStatePending) && (context.Rule.State == m.AlertStateOK) {
+		return false
+	}
 	return true
 	return true
 }
 }
 
 

+ 32 - 0
pkg/services/alerting/notifiers/base_test.go

@@ -0,0 +1,32 @@
+package notifiers
+
+import (
+	"context"
+	"testing"
+
+	m "github.com/grafana/grafana/pkg/models"
+	"github.com/grafana/grafana/pkg/services/alerting"
+	. "github.com/smartystreets/goconvey/convey"
+)
+
+func TestBaseNotifier(t *testing.T) {
+	Convey("Base notifier tests", t, func() {
+		Convey("should notify", func() {
+			Convey("pending -> ok", func() {
+				context := alerting.NewEvalContext(context.TODO(), &alerting.Rule{
+					State: m.AlertStatePending,
+				})
+				context.Rule.State = m.AlertStateOK
+				So(defaultShouldNotify(context), ShouldBeFalse)
+			})
+
+			Convey("ok -> alerting", func() {
+				context := alerting.NewEvalContext(context.TODO(), &alerting.Rule{
+					State: m.AlertStateOK,
+				})
+				context.Rule.State = m.AlertStateAlerting
+				So(defaultShouldNotify(context), ShouldBeTrue)
+			})
+		})
+	})
+}

+ 4 - 0
pkg/services/alerting/notifiers/dingding.go

@@ -38,6 +38,10 @@ func NewDingDingNotifier(model *m.AlertNotification) (alerting.Notifier, error)
 	}, nil
 	}, nil
 }
 }
 
 
+func (this *DingDingNotifier) ShouldNotify(context *alerting.EvalContext) bool {
+	return defaultShouldNotify(context)
+}
+
 type DingDingNotifier struct {
 type DingDingNotifier struct {
 	NotifierBase
 	NotifierBase
 	Url string
 	Url string

+ 1 - 1
pkg/services/alerting/notifiers/dingding_test.go

@@ -9,7 +9,7 @@ import (
 )
 )
 
 
 func TestDingDingNotifier(t *testing.T) {
 func TestDingDingNotifier(t *testing.T) {
-	Convey("Line notifier tests", t, func() {
+	Convey("Dingding notifier tests", t, func() {
 		Convey("empty settings should return error", func() {
 		Convey("empty settings should return error", func() {
 			json := `{ }`
 			json := `{ }`
 
 

+ 4 - 0
pkg/services/alerting/notifiers/email.go

@@ -58,6 +58,10 @@ func NewEmailNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
 	}, nil
 	}, nil
 }
 }
 
 
+func (this *EmailNotifier) ShouldNotify(context *alerting.EvalContext) bool {
+	return defaultShouldNotify(context)
+}
+
 func (this *EmailNotifier) Notify(evalContext *alerting.EvalContext) error {
 func (this *EmailNotifier) Notify(evalContext *alerting.EvalContext) error {
 	this.log.Info("Sending alert notification to", "addresses", this.Addresses)
 	this.log.Info("Sending alert notification to", "addresses", this.Addresses)
 
 

+ 4 - 0
pkg/services/alerting/notifiers/hipchat.go

@@ -75,6 +75,10 @@ type HipChatNotifier struct {
 	log    log.Logger
 	log    log.Logger
 }
 }
 
 
+func (this *HipChatNotifier) ShouldNotify(context *alerting.EvalContext) bool {
+	return defaultShouldNotify(context)
+}
+
 func (this *HipChatNotifier) Notify(evalContext *alerting.EvalContext) error {
 func (this *HipChatNotifier) Notify(evalContext *alerting.EvalContext) error {
 	this.log.Info("Executing hipchat notification", "ruleId", evalContext.Rule.Id, "notification", this.Name)
 	this.log.Info("Executing hipchat notification", "ruleId", evalContext.Rule.Id, "notification", this.Name)
 
 

+ 4 - 0
pkg/services/alerting/notifiers/kafka.go

@@ -57,6 +57,10 @@ type KafkaNotifier struct {
 	log      log.Logger
 	log      log.Logger
 }
 }
 
 
+func (this *KafkaNotifier) ShouldNotify(context *alerting.EvalContext) bool {
+	return defaultShouldNotify(context)
+}
+
 func (this *KafkaNotifier) Notify(evalContext *alerting.EvalContext) error {
 func (this *KafkaNotifier) Notify(evalContext *alerting.EvalContext) error {
 
 
 	state := evalContext.Rule.State
 	state := evalContext.Rule.State

+ 4 - 0
pkg/services/alerting/notifiers/line.go

@@ -51,6 +51,10 @@ type LineNotifier struct {
 	log   log.Logger
 	log   log.Logger
 }
 }
 
 
+func (this *LineNotifier) ShouldNotify(context *alerting.EvalContext) bool {
+	return defaultShouldNotify(context)
+}
+
 func (this *LineNotifier) Notify(evalContext *alerting.EvalContext) error {
 func (this *LineNotifier) Notify(evalContext *alerting.EvalContext) error {
 	this.log.Info("Executing line notification", "ruleId", evalContext.Rule.Id, "notification", this.Name)
 	this.log.Info("Executing line notification", "ruleId", evalContext.Rule.Id, "notification", this.Name)
 
 

+ 4 - 0
pkg/services/alerting/notifiers/opsgenie.go

@@ -62,6 +62,10 @@ type OpsGenieNotifier struct {
 	log       log.Logger
 	log       log.Logger
 }
 }
 
 
+func (this *OpsGenieNotifier) ShouldNotify(context *alerting.EvalContext) bool {
+	return defaultShouldNotify(context)
+}
+
 func (this *OpsGenieNotifier) Notify(evalContext *alerting.EvalContext) error {
 func (this *OpsGenieNotifier) Notify(evalContext *alerting.EvalContext) error {
 
 
 	var err error
 	var err error

+ 4 - 0
pkg/services/alerting/notifiers/pagerduty.go

@@ -63,6 +63,10 @@ type PagerdutyNotifier struct {
 	log         log.Logger
 	log         log.Logger
 }
 }
 
 
+func (this *PagerdutyNotifier) ShouldNotify(context *alerting.EvalContext) bool {
+	return defaultShouldNotify(context)
+}
+
 func (this *PagerdutyNotifier) Notify(evalContext *alerting.EvalContext) error {
 func (this *PagerdutyNotifier) Notify(evalContext *alerting.EvalContext) error {
 
 
 	if evalContext.Rule.State == m.AlertStateOK && !this.AutoResolve {
 	if evalContext.Rule.State == m.AlertStateOK && !this.AutoResolve {

+ 4 - 0
pkg/services/alerting/notifiers/pushover.go

@@ -123,6 +123,10 @@ type PushoverNotifier struct {
 	log      log.Logger
 	log      log.Logger
 }
 }
 
 
+func (this *PushoverNotifier) ShouldNotify(context *alerting.EvalContext) bool {
+	return defaultShouldNotify(context)
+}
+
 func (this *PushoverNotifier) Notify(evalContext *alerting.EvalContext) error {
 func (this *PushoverNotifier) Notify(evalContext *alerting.EvalContext) error {
 	ruleUrl, err := evalContext.GetRuleUrl()
 	ruleUrl, err := evalContext.GetRuleUrl()
 	if err != nil {
 	if err != nil {

+ 4 - 0
pkg/services/alerting/notifiers/sensu.go

@@ -71,6 +71,10 @@ type SensuNotifier struct {
 	log      log.Logger
 	log      log.Logger
 }
 }
 
 
+func (this *SensuNotifier) ShouldNotify(context *alerting.EvalContext) bool {
+	return defaultShouldNotify(context)
+}
+
 func (this *SensuNotifier) Notify(evalContext *alerting.EvalContext) error {
 func (this *SensuNotifier) Notify(evalContext *alerting.EvalContext) error {
 	this.log.Info("Sending sensu result")
 	this.log.Info("Sending sensu result")
 
 

+ 4 - 0
pkg/services/alerting/notifiers/slack.go

@@ -98,6 +98,10 @@ type SlackNotifier struct {
 	log       log.Logger
 	log       log.Logger
 }
 }
 
 
+func (this *SlackNotifier) ShouldNotify(context *alerting.EvalContext) bool {
+	return defaultShouldNotify(context)
+}
+
 func (this *SlackNotifier) Notify(evalContext *alerting.EvalContext) error {
 func (this *SlackNotifier) Notify(evalContext *alerting.EvalContext) error {
 	this.log.Info("Executing slack notification", "ruleId", evalContext.Rule.Id, "notification", this.Name)
 	this.log.Info("Executing slack notification", "ruleId", evalContext.Rule.Id, "notification", this.Name)
 
 

+ 0 - 1
pkg/services/alerting/notifiers/slack_test.go

@@ -78,7 +78,6 @@ func TestSlackNotifier(t *testing.T) {
 				So(slackNotifier.Mention, ShouldEqual, "@carl")
 				So(slackNotifier.Mention, ShouldEqual, "@carl")
 				So(slackNotifier.Token, ShouldEqual, "xoxb-XXXXXXXX-XXXXXXXX-XXXXXXXXXX")
 				So(slackNotifier.Token, ShouldEqual, "xoxb-XXXXXXXX-XXXXXXXX-XXXXXXXXXX")
 			})
 			})
-
 		})
 		})
 	})
 	})
 }
 }

+ 4 - 0
pkg/services/alerting/notifiers/teams.go

@@ -47,6 +47,10 @@ type TeamsNotifier struct {
 	log       log.Logger
 	log       log.Logger
 }
 }
 
 
+func (this *TeamsNotifier) ShouldNotify(context *alerting.EvalContext) bool {
+	return defaultShouldNotify(context)
+}
+
 func (this *TeamsNotifier) Notify(evalContext *alerting.EvalContext) error {
 func (this *TeamsNotifier) Notify(evalContext *alerting.EvalContext) error {
 	this.log.Info("Executing teams notification", "ruleId", evalContext.Rule.Id, "notification", this.Name)
 	this.log.Info("Executing teams notification", "ruleId", evalContext.Rule.Id, "notification", this.Name)
 
 

+ 4 - 0
pkg/services/alerting/notifiers/telegram.go

@@ -76,6 +76,10 @@ func NewTelegramNotifier(model *m.AlertNotification) (alerting.Notifier, error)
 	}, nil
 	}, nil
 }
 }
 
 
+func (this *TelegramNotifier) ShouldNotify(context *alerting.EvalContext) bool {
+	return defaultShouldNotify(context)
+}
+
 func (this *TelegramNotifier) Notify(evalContext *alerting.EvalContext) error {
 func (this *TelegramNotifier) Notify(evalContext *alerting.EvalContext) error {
 	this.log.Info("Sending alert notification to", "bot_token", this.BotToken)
 	this.log.Info("Sending alert notification to", "bot_token", this.BotToken)
 	this.log.Info("Sending alert notification to", "chat_id", this.ChatID)
 	this.log.Info("Sending alert notification to", "chat_id", this.ChatID)

+ 4 - 0
pkg/services/alerting/notifiers/threema.go

@@ -114,6 +114,10 @@ func NewThreemaNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
 	}, nil
 	}, nil
 }
 }
 
 
+func (this *ThreemaNotifier) ShouldNotify(context *alerting.EvalContext) bool {
+	return defaultShouldNotify(context)
+}
+
 func (notifier *ThreemaNotifier) Notify(evalContext *alerting.EvalContext) error {
 func (notifier *ThreemaNotifier) Notify(evalContext *alerting.EvalContext) error {
 	notifier.log.Info("Sending alert notification from", "threema_id", notifier.GatewayID)
 	notifier.log.Info("Sending alert notification from", "threema_id", notifier.GatewayID)
 	notifier.log.Info("Sending alert notification to", "threema_id", notifier.RecipientID)
 	notifier.log.Info("Sending alert notification to", "threema_id", notifier.RecipientID)

+ 4 - 0
pkg/services/alerting/notifiers/victorops.go

@@ -68,6 +68,10 @@ type VictoropsNotifier struct {
 	log         log.Logger
 	log         log.Logger
 }
 }
 
 
+func (this *VictoropsNotifier) ShouldNotify(context *alerting.EvalContext) bool {
+	return defaultShouldNotify(context)
+}
+
 // Notify sends notification to Victorops via POST to URL endpoint
 // Notify sends notification to Victorops via POST to URL endpoint
 func (this *VictoropsNotifier) Notify(evalContext *alerting.EvalContext) error {
 func (this *VictoropsNotifier) Notify(evalContext *alerting.EvalContext) error {
 	this.log.Info("Executing victorops notification", "ruleId", evalContext.Rule.Id, "notification", this.Name)
 	this.log.Info("Executing victorops notification", "ruleId", evalContext.Rule.Id, "notification", this.Name)

+ 4 - 0
pkg/services/alerting/notifiers/webhook.go

@@ -65,6 +65,10 @@ type WebhookNotifier struct {
 	log        log.Logger
 	log        log.Logger
 }
 }
 
 
+func (this *WebhookNotifier) ShouldNotify(context *alerting.EvalContext) bool {
+	return defaultShouldNotify(context)
+}
+
 func (this *WebhookNotifier) Notify(evalContext *alerting.EvalContext) error {
 func (this *WebhookNotifier) Notify(evalContext *alerting.EvalContext) error {
 	this.log.Info("Sending webhook")
 	this.log.Info("Sending webhook")
 
 

+ 3 - 3
pkg/services/alerting/notifiers/webhook_test.go

@@ -18,7 +18,7 @@ func TestWebhookNotifier(t *testing.T) {
 				settingsJSON, _ := simplejson.NewJson([]byte(json))
 				settingsJSON, _ := simplejson.NewJson([]byte(json))
 				model := &m.AlertNotification{
 				model := &m.AlertNotification{
 					Name:     "ops",
 					Name:     "ops",
-					Type:     "email",
+					Type:     "webhook",
 					Settings: settingsJSON,
 					Settings: settingsJSON,
 				}
 				}
 
 
@@ -35,7 +35,7 @@ func TestWebhookNotifier(t *testing.T) {
 				settingsJSON, _ := simplejson.NewJson([]byte(json))
 				settingsJSON, _ := simplejson.NewJson([]byte(json))
 				model := &m.AlertNotification{
 				model := &m.AlertNotification{
 					Name:     "ops",
 					Name:     "ops",
-					Type:     "email",
+					Type:     "webhook",
 					Settings: settingsJSON,
 					Settings: settingsJSON,
 				}
 				}
 
 
@@ -44,7 +44,7 @@ func TestWebhookNotifier(t *testing.T) {
 
 
 				So(err, ShouldBeNil)
 				So(err, ShouldBeNil)
 				So(webhookNotifier.Name, ShouldEqual, "ops")
 				So(webhookNotifier.Name, ShouldEqual, "ops")
-				So(webhookNotifier.Type, ShouldEqual, "email")
+				So(webhookNotifier.Type, ShouldEqual, "webhook")
 				So(webhookNotifier.Url, ShouldEqual, "http://google.com")
 				So(webhookNotifier.Url, ShouldEqual, "http://google.com")
 			})
 			})
 		})
 		})

+ 2 - 4
pkg/services/alerting/result_handler.go

@@ -85,11 +85,9 @@ func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error {
 		if err := annotationRepo.Save(&item); err != nil {
 		if err := annotationRepo.Save(&item); err != nil {
 			handler.log.Error("Failed to save annotation for new alert state", "error", err)
 			handler.log.Error("Failed to save annotation for new alert state", "error", err)
 		}
 		}
-
-		if evalContext.ShouldSendNotification() {
-			handler.notifier.Send(evalContext)
-		}
 	}
 	}
 
 
+	handler.notifier.SendIfNeeded(evalContext)
+
 	return nil
 	return nil
 }
 }