Chore: Sql store split for legacy alerting (#52901)

Moves ~20 sqlstore methods for legacy alerting out of sqlstore (sqlstore.Store interface) and into alerting.
pull/53163/head^2
Kyle Brandt 3 years ago committed by GitHub
parent 46b7ca12e1
commit 643d2bc890
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      pkg/api/alerting.go
  2. 157
      pkg/api/alerting_test.go
  3. 43
      pkg/api/common_test.go
  4. 2
      pkg/cmd/grafana-cli/runner/wire.go
  5. 3
      pkg/server/wire.go
  6. 4
      pkg/services/alerting/alerting_usage.go
  7. 29
      pkg/services/alerting/alerting_usage_test.go
  8. 2
      pkg/services/alerting/conditions/query.go
  9. 7
      pkg/services/alerting/conditions/query_interval_test.go
  10. 7
      pkg/services/alerting/conditions/query_test.go
  11. 30
      pkg/services/alerting/engine.go
  12. 5
      pkg/services/alerting/engine_integration_test.go
  13. 34
      pkg/services/alerting/engine_test.go
  14. 35
      pkg/services/alerting/eval_context.go
  15. 6
      pkg/services/alerting/eval_context_test.go
  16. 28
      pkg/services/alerting/eval_handler_test.go
  17. 4
      pkg/services/alerting/extractor.go
  18. 2
      pkg/services/alerting/extractor_test.go
  19. 6
      pkg/services/alerting/notifier_test.go
  20. 2
      pkg/services/alerting/notifiers/alertmanager_test.go
  21. 2
      pkg/services/alerting/notifiers/base_test.go
  22. 2
      pkg/services/alerting/notifiers/dingding_test.go
  23. 6
      pkg/services/alerting/notifiers/opsgenie_test.go
  24. 12
      pkg/services/alerting/notifiers/pagerduty_test.go
  25. 4
      pkg/services/alerting/notifiers/pushover_test.go
  26. 8
      pkg/services/alerting/notifiers/telegram_test.go
  27. 4
      pkg/services/alerting/notifiers/victorops_test.go
  28. 3
      pkg/services/alerting/rule_test.go
  29. 8
      pkg/services/alerting/service.go
  30. 11
      pkg/services/alerting/service_test.go
  31. 105
      pkg/services/alerting/store.go
  32. 78
      pkg/services/alerting/store_notification.go
  33. 109
      pkg/services/alerting/store_notification_test.go
  34. 135
      pkg/services/alerting/store_test.go
  35. 2
      pkg/services/alerting/test_notification.go
  36. 4
      pkg/services/alerting/test_rule.go
  37. 20
      pkg/services/provisioning/notifiers/config_reader_test.go
  38. 1
      pkg/services/sqlstore/db/db.go
  39. 23
      pkg/services/sqlstore/store.go

@ -28,7 +28,7 @@ func (hs *HTTPServer) ValidateOrgAlert(c *models.ReqContext) {
}
query := models.GetAlertByIdQuery{Id: id}
if err := hs.SQLStore.GetAlertById(c.Req.Context(), &query); err != nil {
if err := hs.AlertEngine.AlertStore.GetAlertById(c.Req.Context(), &query); err != nil {
c.JsonApiErr(404, "Alert not found", nil)
return
}
@ -60,7 +60,7 @@ func (hs *HTTPServer) GetAlertStatesForDashboard(c *models.ReqContext) response.
DashboardId: c.QueryInt64("dashboardId"),
}
if err := hs.SQLStore.GetAlertStatesForDashboard(c.Req.Context(), &query); err != nil {
if err := hs.AlertEngine.AlertStore.GetAlertStatesForDashboard(c.Req.Context(), &query); err != nil {
return response.Error(500, "Failed to fetch alert states", err)
}
@ -141,7 +141,7 @@ func (hs *HTTPServer) GetAlerts(c *models.ReqContext) response.Response {
query.State = states
}
if err := hs.SQLStore.HandleAlertsQuery(c.Req.Context(), &query); err != nil {
if err := hs.AlertEngine.AlertStore.HandleAlertsQuery(c.Req.Context(), &query); err != nil {
return response.Error(500, "List alerts failed", err)
}
@ -223,7 +223,7 @@ func (hs *HTTPServer) GetAlert(c *models.ReqContext) response.Response {
}
query := models.GetAlertByIdQuery{Id: id}
if err := hs.SQLStore.GetAlertById(c.Req.Context(), &query); err != nil {
if err := hs.AlertEngine.AlertStore.GetAlertById(c.Req.Context(), &query); err != nil {
return response.Error(500, "List alerts failed", err)
}
@ -689,7 +689,7 @@ func (hs *HTTPServer) PauseAlert(legacyAlertingEnabled *bool) func(c *models.Req
result["alertId"] = alertID
query := models.GetAlertByIdQuery{Id: alertID}
if err := hs.SQLStore.GetAlertById(c.Req.Context(), &query); err != nil {
if err := hs.AlertEngine.AlertStore.GetAlertById(c.Req.Context(), &query); err != nil {
return response.Error(500, "Get Alert failed", err)
}
@ -719,7 +719,7 @@ func (hs *HTTPServer) PauseAlert(legacyAlertingEnabled *bool) func(c *models.Req
Paused: dto.Paused,
}
if err := hs.SQLStore.PauseAlert(c.Req.Context(), &cmd); err != nil {
if err := hs.AlertEngine.AlertStore.PauseAlert(c.Req.Context(), &cmd); err != nil {
return response.Error(500, "", err)
}
@ -764,7 +764,7 @@ func (hs *HTTPServer) PauseAllAlerts(legacyAlertingEnabled *bool) func(c *models
Paused: dto.Paused,
}
if err := hs.SQLStore.PauseAllAlerts(c.Req.Context(), &updateCmd); err != nil {
if err := hs.AlertEngine.AlertStore.PauseAllAlerts(c.Req.Context(), &updateCmd); err != nil {
return response.Error(500, "Failed to pause alerts", err)
}

@ -1,157 +0,0 @@
package api
import (
"context"
"fmt"
"testing"
"github.com/grafana/grafana/pkg/api/routing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/search"
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
)
var (
viewerRole = models.ROLE_VIEWER
editorRole = models.ROLE_EDITOR
)
type setUpConf struct {
aclMockResp []*models.DashboardACLInfoDTO
}
type mockSearchService struct{ ExpectedResult models.HitList }
func (mss *mockSearchService) SearchHandler(_ context.Context, q *search.Query) error {
q.Result = mss.ExpectedResult
return nil
}
func (mss *mockSearchService) SortOptions() []models.SortOption { return nil }
func setUp(confs ...setUpConf) *HTTPServer {
singleAlert := &models.Alert{Id: 1, DashboardId: 1, Name: "singlealert"}
store := mockstore.NewSQLStoreMock()
hs := &HTTPServer{SQLStore: store, SearchService: &mockSearchService{}}
store.ExpectedAlert = singleAlert
aclMockResp := []*models.DashboardACLInfoDTO{}
for _, c := range confs {
if c.aclMockResp != nil {
aclMockResp = c.aclMockResp
}
}
store.ExpectedTeamsByUser = []*models.TeamDTO{}
dashSvc := &dashboards.FakeDashboardService{}
dashSvc.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*models.GetDashboardACLInfoListQuery")).Run(func(args mock.Arguments) {
q := args.Get(1).(*models.GetDashboardACLInfoListQuery)
q.Result = aclMockResp
}).Return(nil)
guardian.InitLegacyGuardian(store, dashSvc)
return hs
}
func TestAlertingAPIEndpoint(t *testing.T) {
t.Run("When user is editor and not in the ACL", func(t *testing.T) {
hs := setUp()
cmd := dtos.PauseAlertCommand{
AlertId: 1,
Paused: true,
}
postAlertScenario(t, hs, "When calling POST on", "/api/alerts/1/pause", "/api/alerts/:alertId/pause",
models.ROLE_EDITOR, cmd, func(sc *scenarioContext) {
setUp()
callPauseAlert(sc)
assert.Equal(t, 403, sc.resp.Code)
})
})
t.Run("When user is editor and dashboard has default ACL", func(t *testing.T) {
hs := setUp()
cmd := dtos.PauseAlertCommand{
AlertId: 1,
Paused: true,
}
postAlertScenario(t, hs, "When calling POST on", "/api/alerts/1/pause", "/api/alerts/:alertId/pause",
models.ROLE_EDITOR, cmd, func(sc *scenarioContext) {
setUp(setUpConf{
aclMockResp: []*models.DashboardACLInfoDTO{
{Role: &viewerRole, Permission: models.PERMISSION_VIEW},
{Role: &editorRole, Permission: models.PERMISSION_EDIT},
},
})
callPauseAlert(sc)
assert.Equal(t, 200, sc.resp.Code)
})
})
t.Run("When calling GET", func(t *testing.T) {
hs := setUp()
loggedInUserScenarioWithRole(t, "When calling GET on", "GET",
"/api/alerts?dashboardId=1&dashboardId=2&folderId=3&dashboardTag=abc&dashboardQuery=dbQuery&limit=5&query=alertQuery",
"/api/alerts", models.ROLE_EDITOR, func(sc *scenarioContext) {
hs.SearchService.(*mockSearchService).ExpectedResult = models.HitList{
&models.Hit{ID: 1},
&models.Hit{ID: 2},
}
sc.handlerFunc = hs.GetAlerts
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
getAlertsQuery := hs.SQLStore.(*mockstore.SQLStoreMock).LastGetAlertsQuery
require.NotNil(t, getAlertsQuery)
assert.Equal(t, int64(1), getAlertsQuery.DashboardIDs[0])
assert.Equal(t, int64(2), getAlertsQuery.DashboardIDs[1])
assert.Equal(t, int64(5), getAlertsQuery.Limit)
assert.Equal(t, "alertQuery", getAlertsQuery.Query)
}, hs.SQLStore)
})
t.Run("When calling GET on alert-notifications", func(t *testing.T) {
hs := setUp()
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/alert-notifications/1",
"/alert-notifications/:notificationId", models.ROLE_ADMIN, func(sc *scenarioContext) {
sc.handlerFunc = hs.GetAlertNotificationByID
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
assert.Equal(t, 404, sc.resp.Code)
}, hs.SQLStore)
})
}
func callPauseAlert(sc *scenarioContext) {
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
}
func postAlertScenario(t *testing.T, hs *HTTPServer, desc string, url string, routePattern string, role models.RoleType,
cmd dtos.PauseAlertCommand, fn scenarioFunc) {
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
sc := setupScenarioContext(t, url)
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
c.Req.Body = mockRequestBody(cmd)
c.Req.Header.Add("Content-Type", "application/json")
sc.context = c
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID
sc.context.OrgRole = role
legacyAlertingEnabled := new(bool)
*legacyAlertingEnabled = true
return hs.PauseAlert(legacyAlertingEnabled)(c)
})
sc.m.Post(routePattern, sc.defaultHandler)
fn(sc)
})
}

@ -11,6 +11,7 @@ import (
"path/filepath"
"testing"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/api/response"
@ -33,6 +34,7 @@ import (
dashboardservice "github.com/grafana/grafana/pkg/services/dashboards/service"
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/ldap"
"github.com/grafana/grafana/pkg/services/licensing"
"github.com/grafana/grafana/pkg/services/login/loginservice"
@ -40,9 +42,11 @@ import (
"github.com/grafana/grafana/pkg/services/preference/preftest"
"github.com/grafana/grafana/pkg/services/quota/quotaimpl"
"github.com/grafana/grafana/pkg/services/rendering"
"github.com/grafana/grafana/pkg/services/search"
"github.com/grafana/grafana/pkg/services/searchusers"
"github.com/grafana/grafana/pkg/services/searchusers/filters"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/web"
@ -468,3 +472,42 @@ func SetupAPITestServer(t *testing.T, opts ...APITestServerOption) *webtest.Serv
s := webtest.NewServer(t, hs.RouteRegister)
return s
}
var (
viewerRole = models.ROLE_VIEWER
editorRole = models.ROLE_EDITOR
)
type setUpConf struct {
aclMockResp []*models.DashboardACLInfoDTO
}
type mockSearchService struct{ ExpectedResult models.HitList }
func (mss *mockSearchService) SearchHandler(_ context.Context, q *search.Query) error {
q.Result = mss.ExpectedResult
return nil
}
func (mss *mockSearchService) SortOptions() []models.SortOption { return nil }
func setUp(confs ...setUpConf) *HTTPServer {
singleAlert := &models.Alert{Id: 1, DashboardId: 1, Name: "singlealert"}
store := mockstore.NewSQLStoreMock()
hs := &HTTPServer{SQLStore: store, SearchService: &mockSearchService{}}
store.ExpectedAlert = singleAlert
aclMockResp := []*models.DashboardACLInfoDTO{}
for _, c := range confs {
if c.aclMockResp != nil {
aclMockResp = c.aclMockResp
}
}
store.ExpectedTeamsByUser = []*models.TeamDTO{}
dashSvc := &dashboards.FakeDashboardService{}
dashSvc.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*models.GetDashboardACLInfoListQuery")).Run(func(args mock.Arguments) {
q := args.Get(1).(*models.GetDashboardACLInfoListQuery)
q.Result = aclMockResp
}).Return(nil)
guardian.InitLegacyGuardian(store, dashSvc)
return hs
}

@ -156,6 +156,7 @@ var wireSet = wire.NewSet(
hooks.ProvideService,
legacydataservice.ProvideService,
wire.Bind(new(legacydata.RequestHandler), new(*legacydataservice.Service)),
alerting.ProvideAlertStore,
alerting.ProvideAlertEngine,
wire.Bind(new(alerting.UsageStatsQuerier), new(*alerting.AlertEngine)),
api.ProvideHTTPServer,
@ -307,7 +308,6 @@ var wireSet = wire.NewSet(
wire.Bind(new(secretsMigrations.SecretMigrationService), new(*secretsMigrations.SecretMigrationServiceImpl)),
userauthimpl.ProvideService,
ngmetrics.ProvideServiceForTest,
wire.Bind(new(alerting.AlertStore), new(*sqlstore.SQLStore)),
wire.Bind(new(sqlstore.TeamStore), new(*sqlstore.SQLStore)),
notifications.MockNotificationService,
wire.Bind(new(notifications.TempUserStore), new(*mockstore.SQLStoreMock)),

@ -139,6 +139,7 @@ import (
var wireBasicSet = wire.NewSet(
legacydataservice.ProvideService,
wire.Bind(new(legacydata.RequestHandler), new(*legacydataservice.Service)),
alerting.ProvideAlertStore,
alerting.ProvideAlertEngine,
wire.Bind(new(alerting.UsageStatsQuerier), new(*alerting.AlertEngine)),
setting.NewCfgFromArgs,
@ -316,7 +317,6 @@ var wireBasicSet = wire.NewSet(
var wireSet = wire.NewSet(
wireBasicSet,
sqlstore.ProvideService,
wire.Bind(new(alerting.AlertStore), new(*sqlstore.SQLStore)),
wire.Bind(new(sqlstore.TeamStore), new(*sqlstore.SQLStore)),
ngmetrics.ProvideService,
wire.Bind(new(notifications.TempUserStore), new(*sqlstore.SQLStore)),
@ -333,7 +333,6 @@ var wireTestSet = wire.NewSet(
ProvideTestEnv,
sqlstore.ProvideServiceForTests,
ngmetrics.ProvideServiceForTest,
wire.Bind(new(alerting.AlertStore), new(*sqlstore.SQLStore)),
wire.Bind(new(sqlstore.TeamStore), new(*sqlstore.SQLStore)),
notifications.MockNotificationService,

@ -29,7 +29,7 @@ type UsageStatsQuerier interface {
// configured in Grafana.
func (e *AlertEngine) QueryUsageStats(ctx context.Context) (*UsageStats, error) {
cmd := &models.GetAllAlertsQuery{}
err := e.sqlStore.GetAllAlertQueryHandler(ctx, cmd)
err := e.AlertStore.GetAllAlertQueryHandler(ctx, cmd)
if err != nil {
return nil, err
}
@ -64,7 +64,7 @@ func (e *AlertEngine) mapRulesToUsageStats(ctx context.Context, rules []*models.
result := map[string]int{}
for k, v := range typeCount {
query := &datasources.GetDataSourceQuery{Id: k}
err := e.sqlStore.GetDataSource(ctx, query)
err := e.datasourceService.GetDataSource(ctx, query)
if err != nil {
return map[string]int{}, nil
}

@ -12,12 +12,22 @@ import (
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/datasources"
fd "github.com/grafana/grafana/pkg/services/datasources/fakes"
)
func TestAlertingUsageStats(t *testing.T) {
store := &AlertStoreMock{}
dsMock := &fd.FakeDataSourceService{
DataSources: []*datasources.DataSource{
{Id: 1, Type: datasources.DS_INFLUXDB},
{Id: 2, Type: datasources.DS_GRAPHITE},
{Id: 3, Type: datasources.DS_PROMETHEUS},
{Id: 4, Type: datasources.DS_PROMETHEUS},
},
}
ae := &AlertEngine{
sqlStore: store,
AlertStore: store,
datasourceService: dsMock,
}
store.getAllAlerts = func(ctx context.Context, query *models.GetAllAlertsQuery) error {
@ -41,23 +51,6 @@ func TestAlertingUsageStats(t *testing.T) {
return nil
}
store.getDataSource = func(ctx context.Context, query *datasources.GetDataSourceQuery) error {
ds := map[int64]*datasources.DataSource{
1: {Type: "influxdb"},
2: {Type: "graphite"},
3: {Type: "prometheus"},
4: {Type: "prometheus"},
}
r, exist := ds[query.Id]
if !exist {
return datasources.ErrDataSourceNotFound
}
query.Result = r
return nil
}
result, err := ae.QueryUsageStats(context.Background())
require.NoError(t, err, "getAlertingUsage should not return error")

@ -147,7 +147,7 @@ func (c *QueryCondition) executeQuery(context *alerting.EvalContext, timeRange l
OrgId: context.Rule.OrgID,
}
if err := context.Store.GetDataSource(context.Ctx, getDsInfo); err != nil {
if err := context.GetDataSource(context.Ctx, getDsInfo); err != nil {
return nil, fmt.Errorf("could not find datasource: %w", err)
}

@ -7,6 +7,7 @@ import (
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/services/datasources"
fd "github.com/grafana/grafana/pkg/services/datasources/fakes"
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
"github.com/grafana/grafana/pkg/services/validations"
"github.com/grafana/grafana/pkg/tsdb/intervalv2"
@ -138,7 +139,6 @@ func (rh fakeIntervalTestReqHandler) HandleRequest(ctx context.Context, dsInfo *
func applyScenario(t *testing.T, timeRange string, dataSourceJsonData *simplejson.Json, queryModel string, verifier func(query legacydata.DataSubQuery)) {
t.Run("desc", func(t *testing.T) {
store := mockstore.NewSQLStoreMock()
store.ExpectedDatasource = &datasources.DataSource{Id: 1, Type: "graphite", JsonData: dataSourceJsonData}
ctx := &queryIntervalTestContext{}
ctx.result = &alerting.EvalContext{
@ -146,6 +146,11 @@ func applyScenario(t *testing.T, timeRange string, dataSourceJsonData *simplejso
Rule: &alerting.Rule{},
RequestValidator: &validations.OSSPluginRequestValidator{},
Store: store,
DatasourceService: &fd.FakeDataSourceService{
DataSources: []*datasources.DataSource{
{Id: 1, Type: datasources.DS_GRAPHITE, JsonData: dataSourceJsonData},
},
},
}
jsonModel, err := simplejson.NewJson([]byte(`{

@ -7,6 +7,7 @@ import (
"time"
"github.com/grafana/grafana/pkg/services/datasources"
fd "github.com/grafana/grafana/pkg/services/datasources/fakes"
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
"github.com/grafana/grafana/pkg/services/validations"
"github.com/grafana/grafana/pkg/tsdb/legacydata"
@ -37,7 +38,6 @@ func TestQueryCondition(t *testing.T) {
setup := func() *queryConditionTestContext {
ctx := &queryConditionTestContext{}
store := mockstore.NewSQLStoreMock()
store.ExpectedDatasource = &datasources.DataSource{Id: 1, Type: "graphite"}
ctx.reducer = `{"type":"avg"}`
ctx.evaluator = `{"type":"gt","params":[100]}`
@ -46,6 +46,11 @@ func TestQueryCondition(t *testing.T) {
Rule: &alerting.Rule{},
RequestValidator: &validations.OSSPluginRequestValidator{},
Store: store,
DatasourceService: &fd.FakeDataSourceService{
DataSources: []*datasources.DataSource{
{Id: 1, Type: datasources.DS_GRAPHITE},
},
},
}
return ctx
}

@ -11,6 +11,7 @@ import (
"go.opentelemetry.io/otel/attribute"
"golang.org/x/sync/errgroup"
"github.com/grafana/grafana/pkg/infra/localcache"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/infra/usagestats"
@ -25,19 +26,6 @@ import (
"github.com/grafana/grafana/pkg/tsdb/legacydata"
)
// AlertStore is a subset of SQLStore API to satisfy the needs of the alerting service.
// A subset is needed to make it easier to mock during the tests.
type AlertStore interface {
GetAllAlertQueryHandler(context.Context, *models.GetAllAlertsQuery) error
GetDataSource(context.Context, *datasources.GetDataSourceQuery) error
SetAlertNotificationStateToCompleteCommand(context.Context, *models.SetAlertNotificationStateToCompleteCommand) error
SetAlertNotificationStateToPendingCommand(context.Context, *models.SetAlertNotificationStateToPendingCommand) error
GetAlertNotificationUidWithId(context.Context, *models.GetAlertNotificationUidQuery) error
GetAlertNotificationsWithUidToSend(context.Context, *models.GetAlertNotificationsWithUidToSendQuery) error
GetOrCreateAlertNotificationState(context.Context, *models.GetOrCreateNotificationStateQuery) error
SetAlertState(context.Context, *models.SetAlertStateCommand) error
}
// AlertEngine is the background process that
// schedules alert evaluations and makes sure notifications
// are sent.
@ -56,9 +44,10 @@ type AlertEngine struct {
resultHandler resultHandler
usageStatsService usagestats.Service
tracer tracing.Tracer
sqlStore AlertStore
AlertStore AlertStore
dashAlertExtractor DashAlertExtractor
dashboardService dashboards.DashboardService
datasourceService datasources.DataSourceService
}
// IsDisabled returns true if the alerting service is disabled for this instance.
@ -69,8 +58,8 @@ func (e *AlertEngine) IsDisabled() bool {
// ProvideAlertEngine returns a new AlertEngine.
func ProvideAlertEngine(renderer rendering.Service, requestValidator models.PluginRequestValidator,
dataService legacydata.RequestHandler, usageStatsService usagestats.Service, encryptionService encryption.Internal,
notificationService *notifications.NotificationService, tracer tracing.Tracer, sqlStore AlertStore, cfg *setting.Cfg,
dashAlertExtractor DashAlertExtractor, dashboardService dashboards.DashboardService) *AlertEngine {
notificationService *notifications.NotificationService, tracer tracing.Tracer, store AlertStore, cfg *setting.Cfg,
dashAlertExtractor DashAlertExtractor, dashboardService dashboards.DashboardService, cacheService *localcache.CacheService, dsService datasources.DataSourceService) *AlertEngine {
e := &AlertEngine{
Cfg: cfg,
RenderService: renderer,
@ -78,16 +67,17 @@ func ProvideAlertEngine(renderer rendering.Service, requestValidator models.Plug
DataService: dataService,
usageStatsService: usageStatsService,
tracer: tracer,
sqlStore: sqlStore,
AlertStore: store,
dashAlertExtractor: dashAlertExtractor,
dashboardService: dashboardService,
datasourceService: dsService,
}
e.execQueue = make(chan *Job, 1000)
e.scheduler = newScheduler()
e.evalHandler = NewEvalHandler(e.DataService)
e.ruleReader = newRuleReader(sqlStore)
e.ruleReader = newRuleReader(store)
e.log = log.New("alerting.engine")
e.resultHandler = newResultHandler(e.RenderService, sqlStore, notificationService, encryptionService.GetDecryptedValue)
e.resultHandler = newResultHandler(e.RenderService, store, notificationService, encryptionService.GetDecryptedValue)
e.registerUsageMetrics()
@ -203,7 +193,7 @@ func (e *AlertEngine) processJob(attemptID int, attemptChan chan int, cancelChan
alertCtx, cancelFn := context.WithTimeout(context.Background(), setting.AlertingEvaluationTimeout)
cancelChan <- cancelFn
alertCtx, span := e.tracer.Start(alertCtx, "alert execution")
evalContext := NewEvalContext(alertCtx, job.Rule, e.RequestValidator, e.sqlStore, e.dashboardService)
evalContext := NewEvalContext(alertCtx, job.Rule, e.RequestValidator, e.AlertStore, e.dashboardService, e.datasourceService)
evalContext.Ctx = alertCtx
go func() {

@ -9,8 +9,10 @@ import (
"testing"
"time"
"github.com/grafana/grafana/pkg/infra/localcache"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/infra/usagestats"
datasources "github.com/grafana/grafana/pkg/services/datasources/fakes"
encryptionprovider "github.com/grafana/grafana/pkg/services/encryption/provider"
encryptionservice "github.com/grafana/grafana/pkg/services/encryption/service"
"github.com/grafana/grafana/pkg/setting"
@ -32,7 +34,8 @@ func TestIntegrationEngineTimeouts(t *testing.T) {
require.NoError(t, err)
tracer := tracing.InitializeTracerForTest()
engine := ProvideAlertEngine(nil, nil, nil, usMock, encService, nil, tracer, nil, cfg, nil, nil)
dsMock := &datasources.FakeDataSourceService{}
engine := ProvideAlertEngine(nil, nil, nil, usMock, encService, nil, tracer, nil, setting.NewCfg(), nil, nil, localcache.New(time.Minute, time.Minute), dsMock)
setting.AlertingNotificationTimeout = 30 * time.Second
setting.AlertingMaxAttempts = 3
engine.resultHandler = &FakeResultHandler{}

@ -8,10 +8,12 @@ import (
"time"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/localcache"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/infra/usagestats"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/datasources"
fd "github.com/grafana/grafana/pkg/services/datasources/fakes"
encryptionprovider "github.com/grafana/grafana/pkg/services/encryption/provider"
encryptionservice "github.com/grafana/grafana/pkg/services/encryption/service"
"github.com/grafana/grafana/pkg/setting"
@ -46,15 +48,11 @@ func (handler *FakeResultHandler) handle(evalContext *EvalContext) error {
// A mock implementation of the AlertStore interface, allowing to override certain methods individually
type AlertStoreMock struct {
getAllAlerts func(context.Context, *models.GetAllAlertsQuery) error
getDataSource func(context.Context, *datasources.GetDataSourceQuery) error
getAlertNotificationsWithUidToSend func(ctx context.Context, query *models.GetAlertNotificationsWithUidToSendQuery) error
getOrCreateNotificationState func(ctx context.Context, query *models.GetOrCreateNotificationStateQuery) error
}
func (a *AlertStoreMock) GetDataSource(c context.Context, cmd *datasources.GetDataSourceQuery) error {
if a.getDataSource != nil {
return a.getDataSource(c, cmd)
}
func (a *AlertStoreMock) GetAlertById(c context.Context, cmd *models.GetAlertByIdQuery) error {
return nil
}
@ -99,6 +97,22 @@ func (a *AlertStoreMock) SetAlertState(_ context.Context, _ *models.SetAlertStat
return nil
}
func (a *AlertStoreMock) GetAlertStatesForDashboard(_ context.Context, _ *models.GetAlertStatesForDashboardQuery) error {
return nil
}
func (a *AlertStoreMock) HandleAlertsQuery(context.Context, *models.GetAlertsQuery) error {
return nil
}
func (a *AlertStoreMock) PauseAlert(context.Context, *models.PauseAlertCommand) error {
return nil
}
func (a *AlertStoreMock) PauseAllAlerts(context.Context, *models.PauseAllAlertCommand) error {
return nil
}
func TestEngineProcessJob(t *testing.T) {
usMock := &usagestats.UsageStatsMock{T: t}
@ -111,7 +125,10 @@ func TestEngineProcessJob(t *testing.T) {
tracer := tracing.InitializeTracerForTest()
store := &AlertStoreMock{}
engine := ProvideAlertEngine(nil, nil, nil, usMock, encService, nil, tracer, store, cfg, nil, nil)
dsMock := &fd.FakeDataSourceService{
DataSources: []*datasources.DataSource{{Id: 1, Type: datasources.DS_PROMETHEUS}},
}
engine := ProvideAlertEngine(nil, nil, nil, usMock, encService, nil, tracer, store, setting.NewCfg(), nil, nil, localcache.New(time.Minute, time.Minute), dsMock)
setting.AlertingEvaluationTimeout = 30 * time.Second
setting.AlertingNotificationTimeout = 30 * time.Second
setting.AlertingMaxAttempts = 3
@ -128,11 +145,6 @@ func TestEngineProcessJob(t *testing.T) {
return nil
}
store.getDataSource = func(ctx context.Context, q *datasources.GetDataSourceQuery) error {
q.Result = &datasources.DataSource{Id: 1, Type: datasources.DS_PROMETHEUS}
return nil
}
report, err := usMock.GetUsageReport(context.Background())
require.Nil(t, err)

@ -9,6 +9,7 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/setting"
)
@ -38,25 +39,27 @@ type EvalContext struct {
Ctx context.Context
Store AlertStore
dashboardService dashboards.DashboardService
Store AlertStore
dashboardService dashboards.DashboardService
DatasourceService datasources.DataSourceService
}
// NewEvalContext is the EvalContext constructor.
func NewEvalContext(alertCtx context.Context, rule *Rule, requestValidator models.PluginRequestValidator,
sqlStore AlertStore, dashboardService dashboards.DashboardService) *EvalContext {
alertStore AlertStore, dashboardService dashboards.DashboardService, dsService datasources.DataSourceService) *EvalContext {
return &EvalContext{
Ctx: alertCtx,
StartTime: time.Now(),
Rule: rule,
Logs: make([]*ResultLogEntry, 0),
EvalMatches: make([]*EvalMatch, 0),
AllMatches: make([]*EvalMatch, 0),
Log: log.New("alerting.evalContext"),
PrevAlertState: rule.State,
RequestValidator: requestValidator,
Store: sqlStore,
dashboardService: dashboardService,
Ctx: alertCtx,
StartTime: time.Now(),
Rule: rule,
Logs: make([]*ResultLogEntry, 0),
EvalMatches: make([]*EvalMatch, 0),
AllMatches: make([]*EvalMatch, 0),
Log: log.New("alerting.evalContext"),
PrevAlertState: rule.State,
RequestValidator: requestValidator,
Store: alertStore,
dashboardService: dashboardService,
DatasourceService: dsService,
}
}
@ -220,6 +223,10 @@ func (c *EvalContext) evaluateNotificationTemplateFields() error {
return nil
}
func (c *EvalContext) GetDataSource(ctx context.Context, q *datasources.GetDataSourceQuery) error {
return c.DatasourceService.GetDataSource(ctx, q)
}
// getTemplateMatches returns the values we should use to parse the templates
func (c *EvalContext) getTemplateMatches() []*EvalMatch {
// EvalMatches represent series violating the rule threshold,

@ -14,7 +14,7 @@ import (
)
func TestStateIsUpdatedWhenNeeded(t *testing.T) {
ctx := NewEvalContext(context.Background(), &Rule{Conditions: []Condition{&conditionStub{firing: true}}}, &validations.OSSPluginRequestValidator{}, nil, nil)
ctx := NewEvalContext(context.Background(), &Rule{Conditions: []Condition{&conditionStub{firing: true}}}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
t.Run("ok -> alerting", func(t *testing.T) {
ctx.PrevAlertState = models.AlertStateOK
@ -199,7 +199,7 @@ func TestGetStateFromEvalContext(t *testing.T) {
}
for _, tc := range tcs {
evalContext := NewEvalContext(context.Background(), &Rule{Conditions: []Condition{&conditionStub{firing: true}}}, &validations.OSSPluginRequestValidator{}, nil, nil)
evalContext := NewEvalContext(context.Background(), &Rule{Conditions: []Condition{&conditionStub{firing: true}}}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
tc.applyFn(evalContext)
newState := evalContext.GetNewState()
@ -391,7 +391,7 @@ func TestEvaluateNotificationTemplateFields(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(tt *testing.T) {
evalContext := NewEvalContext(context.Background(), &Rule{Name: "Rule name: ${value1}", Message: "Rule message: ${value2}",
Conditions: []Condition{&conditionStub{firing: true}}}, &validations.OSSPluginRequestValidator{}, nil, nil)
Conditions: []Condition{&conditionStub{firing: true}}}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
evalContext.EvalMatches = test.evalMatches
evalContext.AllMatches = test.allMatches

@ -29,7 +29,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
Conditions: []Condition{&conditionStub{
firing: true,
}},
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
handler.Eval(context)
require.Equal(t, true, context.Firing)
@ -39,7 +39,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
t.Run("Show return triggered with single passing condition2", func(t *testing.T) {
context := NewEvalContext(context.Background(), &Rule{
Conditions: []Condition{&conditionStub{firing: true, operator: "and"}},
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
handler.Eval(context)
require.Equal(t, true, context.Firing)
@ -52,7 +52,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
&conditionStub{firing: true, operator: "and", matches: []*EvalMatch{{}, {}}},
&conditionStub{firing: false, operator: "and"},
},
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
handler.Eval(context)
require.Equal(t, false, context.Firing)
@ -65,7 +65,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
&conditionStub{firing: true, operator: "and"},
&conditionStub{firing: false, operator: "or"},
},
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
handler.Eval(context)
require.Equal(t, true, context.Firing)
@ -78,7 +78,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
&conditionStub{firing: true, operator: "and"},
&conditionStub{firing: false, operator: "and"},
},
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
handler.Eval(context)
require.Equal(t, false, context.Firing)
@ -92,7 +92,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
&conditionStub{firing: true, operator: "and"},
&conditionStub{firing: false, operator: "or"},
},
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
handler.Eval(context)
require.Equal(t, true, context.Firing)
@ -106,7 +106,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
&conditionStub{firing: false, operator: "and"},
&conditionStub{firing: false, operator: "or"},
},
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
handler.Eval(context)
require.Equal(t, false, context.Firing)
@ -120,7 +120,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
&conditionStub{firing: false, operator: "and"},
&conditionStub{firing: true, operator: "and"},
},
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
handler.Eval(context)
require.Equal(t, false, context.Firing)
@ -134,7 +134,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
&conditionStub{firing: false, operator: "or"},
&conditionStub{firing: true, operator: "or"},
},
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
handler.Eval(context)
require.Equal(t, true, context.Firing)
@ -148,7 +148,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
&conditionStub{firing: false, operator: "or"},
&conditionStub{firing: false, operator: "or"},
},
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
handler.Eval(context)
require.Equal(t, false, context.Firing)
@ -163,7 +163,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
&conditionStub{operator: "or", noData: false},
&conditionStub{operator: "or", noData: false},
},
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
handler.Eval(context)
require.False(t, context.NoDataFound)
@ -174,7 +174,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
Conditions: []Condition{
&conditionStub{operator: "and", noData: true},
},
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
handler.Eval(context)
require.Equal(t, false, context.Firing)
@ -187,7 +187,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
&conditionStub{operator: "and", noData: true},
&conditionStub{operator: "and", noData: false},
},
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
handler.Eval(context)
require.True(t, context.NoDataFound)
@ -199,7 +199,7 @@ func TestAlertingEvaluationHandler(t *testing.T) {
&conditionStub{operator: "or", noData: true},
&conditionStub{operator: "or", noData: false},
},
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
handler.Eval(context)
require.True(t, context.NoDataFound)

@ -26,11 +26,11 @@ type DashAlertExtractorService struct {
log log.Logger
}
func ProvideDashAlertExtractorService(datasourcePermissionsService permissions.DatasourcePermissionsService, datasourceService datasources.DataSourceService, alertStore AlertStore) *DashAlertExtractorService {
func ProvideDashAlertExtractorService(datasourcePermissionsService permissions.DatasourcePermissionsService, datasourceService datasources.DataSourceService, store AlertStore) *DashAlertExtractorService {
return &DashAlertExtractorService{
datasourcePermissionsService: datasourcePermissionsService,
datasourceService: datasourceService,
alertStore: alertStore,
alertStore: store,
log: log.New("alerting.extractor"),
}
}

@ -200,7 +200,7 @@ func TestAlertRuleExtraction(t *testing.T) {
})
t.Run("Alert notifications are in DB", func(t *testing.T) {
sqlStore := sqlstore.InitTestDB(t)
sqlStore := sqlStore{db: sqlstore.InitTestDB(t)}
firstNotification := models.CreateAlertNotificationCommand{Uid: "notifier1", OrgId: 1, Name: "1"}
err = sqlStore.CreateAlertNotificationCommand(context.Background(), &firstNotification)

@ -20,18 +20,18 @@ import (
func TestNotificationService(t *testing.T) {
testRule := &Rule{Name: "Test", Message: "Something is bad"}
store := &AlertStoreMock{}
evalCtx := NewEvalContext(context.Background(), testRule, &validations.OSSPluginRequestValidator{}, store, nil)
evalCtx := NewEvalContext(context.Background(), testRule, &validations.OSSPluginRequestValidator{}, store, nil, nil)
testRuleTemplated := &Rule{Name: "Test latency ${quantile}", Message: "Something is bad on instance ${instance}"}
evalCtxWithMatch := NewEvalContext(context.Background(), testRuleTemplated, &validations.OSSPluginRequestValidator{}, store, nil)
evalCtxWithMatch := NewEvalContext(context.Background(), testRuleTemplated, &validations.OSSPluginRequestValidator{}, store, nil, nil)
evalCtxWithMatch.EvalMatches = []*EvalMatch{{
Tags: map[string]string{
"instance": "localhost:3000",
"quantile": "0.99",
},
}}
evalCtxWithoutMatch := NewEvalContext(context.Background(), testRuleTemplated, &validations.OSSPluginRequestValidator{}, store, nil)
evalCtxWithoutMatch := NewEvalContext(context.Background(), testRuleTemplated, &validations.OSSPluginRequestValidator{}, store, nil, nil)
notificationServiceScenario(t, "Given alert rule with upload image enabled should render and upload image and send notification",
evalCtx, true, func(sc *scenarioContext) {

@ -68,7 +68,7 @@ func TestWhenAlertManagerShouldNotify(t *testing.T) {
am := &AlertmanagerNotifier{log: log.New("test.logger")}
evalContext := alerting.NewEvalContext(context.Background(), &alerting.Rule{
State: tc.prevState,
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
evalContext.Rule.State = tc.newState

@ -170,7 +170,7 @@ func TestShouldSendAlertNotification(t *testing.T) {
for _, tc := range tcs {
evalContext := alerting.NewEvalContext(context.Background(), &alerting.Rule{
State: tc.prevState,
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
if tc.state == nil {
tc.state = &models.AlertNotificationState{}

@ -52,7 +52,7 @@ func TestDingDingNotifier(t *testing.T) {
&alerting.Rule{
State: models.AlertStateAlerting,
Message: `{host="localhost"}`,
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
_, err = notifier.genBody(evalContext, "")
require.Nil(t, err)
})

@ -105,7 +105,7 @@ func TestOpsGenieNotifier(t *testing.T) {
Message: "someMessage",
State: models.AlertStateAlerting,
AlertRuleTags: tagPairs,
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
evalContext.IsTestRun = true
tags := make([]string, 0)
@ -154,7 +154,7 @@ func TestOpsGenieNotifier(t *testing.T) {
Message: "someMessage",
State: models.AlertStateAlerting,
AlertRuleTags: tagPairs,
}, nil, nil, nil)
}, nil, nil, nil, nil)
evalContext.IsTestRun = true
tags := make([]string, 0)
@ -203,7 +203,7 @@ func TestOpsGenieNotifier(t *testing.T) {
Message: "someMessage",
State: models.AlertStateAlerting,
AlertRuleTags: tagPairs,
}, nil, nil, nil)
}, nil, nil, nil, nil)
evalContext.IsTestRun = true
tags := make([]string, 0)

@ -142,7 +142,7 @@ func TestPagerdutyNotifier(t *testing.T) {
Name: "someRule",
Message: "someMessage",
State: models.AlertStateAlerting,
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
evalContext.IsTestRun = true
payloadJSON, err := pagerdutyNotifier.buildEventPayload(evalContext)
@ -198,7 +198,7 @@ func TestPagerdutyNotifier(t *testing.T) {
ID: 0,
Name: "someRule",
State: models.AlertStateAlerting,
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
evalContext.IsTestRun = true
payloadJSON, err := pagerdutyNotifier.buildEventPayload(evalContext)
@ -256,7 +256,7 @@ func TestPagerdutyNotifier(t *testing.T) {
Name: "someRule",
Message: "someMessage",
State: models.AlertStateAlerting,
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
evalContext.IsTestRun = true
evalContext.EvalMatches = []*alerting.EvalMatch{
{
@ -335,7 +335,7 @@ func TestPagerdutyNotifier(t *testing.T) {
{Key: "severity", Value: "warning"},
{Key: "dedup_key", Value: "key-" + strings.Repeat("x", 260)},
},
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
evalContext.ImagePublicURL = "http://somewhere.com/omg_dont_panic.png"
evalContext.IsTestRun = true
@ -414,7 +414,7 @@ func TestPagerdutyNotifier(t *testing.T) {
{Key: "component", Value: "aComponent"},
{Key: "severity", Value: "info"},
},
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
evalContext.ImagePublicURL = "http://somewhere.com/omg_dont_panic.png"
evalContext.IsTestRun = true
@ -493,7 +493,7 @@ func TestPagerdutyNotifier(t *testing.T) {
{Key: "component", Value: "aComponent"},
{Key: "severity", Value: "llama"},
},
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
evalContext.ImagePublicURL = "http://somewhere.com/omg_dont_panic.png"
evalContext.IsTestRun = true

@ -76,7 +76,7 @@ func TestGenPushoverBody(t *testing.T) {
evalContext := alerting.NewEvalContext(context.Background(),
&alerting.Rule{
State: models.AlertStateAlerting,
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
_, pushoverBody, err := notifier.genPushoverBody(evalContext, "", "")
require.Nil(t, err)
@ -87,7 +87,7 @@ func TestGenPushoverBody(t *testing.T) {
evalContext := alerting.NewEvalContext(context.Background(),
&alerting.Rule{
State: models.AlertStateOK,
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
_, pushoverBody, err := notifier.genPushoverBody(evalContext, "", "")
require.Nil(t, err)

@ -61,7 +61,7 @@ func TestTelegramNotifier(t *testing.T) {
Name: "This is an alarm",
Message: "Some kind of message.",
State: models.AlertStateOK,
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
caption := generateImageCaption(evalContext, "http://grafa.url/abcdef", "")
require.LessOrEqual(t, len(caption), 1024)
@ -77,7 +77,7 @@ func TestTelegramNotifier(t *testing.T) {
Name: "This is an alarm",
Message: "Some kind of message.",
State: models.AlertStateOK,
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
caption := generateImageCaption(evalContext,
"http://grafa.url/abcdefaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
@ -95,7 +95,7 @@ func TestTelegramNotifier(t *testing.T) {
Name: "This is an alarm",
Message: "Some kind of message that is too long for appending to our pretty little message, this line is actually exactly 197 chars long and I will get there in the end I promise I will. Yes siree that's it. But suddenly Telegram increased the length so now we need some lorem ipsum to fix this test. Here we go: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus consectetur molestie cursus. Donec suscipit egestas nisi. Proin ut efficitur ex. Mauris mi augue, volutpat a nisi vel, euismod dictum arcu. Sed quis tempor eros, sed malesuada dolor. Ut orci augue, viverra sit amet blandit quis, faucibus sit amet ex. Duis condimentum efficitur lectus, id dignissim quam tempor id. Morbi sollicitudin rhoncus diam, id tincidunt lectus scelerisque vitae. Etiam imperdiet semper sem, vel eleifend ligula mollis eget. Etiam ultrices fringilla lacus, sit amet pharetra ex blandit quis. Suspendisse in egestas neque, et posuere lectus. Vestibulum eu ex dui. Sed molestie nulla a lobortis scelerisque. Nulla ipsum ex, iaculis vitae vehicula sit amet, fermentum eu eros.",
State: models.AlertStateOK,
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
caption := generateImageCaption(evalContext,
"http://grafa.url/foo",
@ -112,7 +112,7 @@ func TestTelegramNotifier(t *testing.T) {
Name: "This is an alarm",
Message: "Some kind of message that is too long for appending to our pretty little message, this line is actually exactly 197 chars long and I will get there in the end I promise I will. Yes siree that's it. But suddenly Telegram increased the length so now we need some lorem ipsum to fix this test. Here we go: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus consectetur molestie cursus. Donec suscipit egestas nisi. Proin ut efficitur ex. Mauris mi augue, volutpat a nisi vel, euismod dictum arcu. Sed quis tempor eros, sed malesuada dolor. Ut orci augue, viverra sit amet blandit quis, faucibus sit amet ex. Duis condimentum efficitur lectus, id dignissim quam tempor id. Morbi sollicitudin rhoncus diam, id tincidunt lectus scelerisque vitae. Etiam imperdiet semper sem, vel eleifend ligula mollis eget. Etiam ultrices fringilla lacus, sit amet pharetra ex blandit quis. Suspendisse in egestas neque, et posuere lectus. Vestibulum eu ex dui. Sed molestie nulla a lobortis sceleri",
State: models.AlertStateOK,
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
caption := generateImageCaption(evalContext,
"http://grafa.url/foo",

@ -93,7 +93,7 @@ func TestVictoropsNotifier(t *testing.T) {
{Key: "keyOnly"},
{Key: "severity", Value: "warning"},
},
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
evalContext.IsTestRun = true
payload, err := victoropsNotifier.buildEventPayload(evalContext)
@ -141,7 +141,7 @@ func TestVictoropsNotifier(t *testing.T) {
{Key: "keyOnly"},
{Key: "severity", Value: "warning"},
},
}, &validations.OSSPluginRequestValidator{}, nil, nil)
}, &validations.OSSPluginRequestValidator{}, nil, nil, nil)
evalContext.IsTestRun = true
payload, err := victoropsNotifier.buildEventPayload(evalContext)

@ -7,6 +7,7 @@ import (
"time"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/localcache"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/tsdb/legacydata"
@ -84,7 +85,7 @@ func TestAlertRuleForParsing(t *testing.T) {
}
func TestAlertRuleModel(t *testing.T) {
sqlStore := sqlstore.InitTestDB(t)
sqlStore := &sqlStore{db: sqlstore.InitTestDB(t), cache: localcache.New(time.Minute, time.Minute)}
RegisterCondition("test", func(model *simplejson.Json, index int) (Condition, error) {
return &FakeCondition{}, nil
})

@ -7,21 +7,21 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/encryption"
"github.com/grafana/grafana/pkg/services/notifications"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/db"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
)
type AlertNotificationService struct {
SQLStore *sqlstore.SQLStore
SQLStore AlertNotificationStore
EncryptionService encryption.Internal
NotificationService *notifications.NotificationService
}
func ProvideService(store *sqlstore.SQLStore, encryptionService encryption.Internal,
func ProvideService(store db.DB, encryptionService encryption.Internal,
notificationService *notifications.NotificationService) *AlertNotificationService {
s := &AlertNotificationService{
SQLStore: store,
SQLStore: &sqlStore{db: store},
EncryptionService: encryptionService,
NotificationService: notificationService,
}

@ -4,8 +4,11 @@ import (
"context"
"strings"
"testing"
"time"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/localcache"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/usagestats"
"github.com/grafana/grafana/pkg/models"
encryptionprovider "github.com/grafana/grafana/pkg/services/encryption/provider"
@ -17,7 +20,11 @@ import (
)
func TestService(t *testing.T) {
sqlStore := sqlstore.InitTestDB(t)
sqlStore := &sqlStore{
db: sqlstore.InitTestDB(t),
log: &log.ConcreteLogger{},
cache: localcache.New(time.Minute, time.Minute),
}
nType := "test"
registerTestNotifier(nType)
@ -30,7 +37,7 @@ func TestService(t *testing.T) {
encService, err := encryptionservice.ProvideEncryptionService(encProvider, usMock, settings)
require.NoError(t, err)
s := ProvideService(sqlStore, encService, nil)
s := ProvideService(sqlStore.db, encService, nil)
origSecret := setting.SecretKey
setting.SecretKey = "alert_notification_service_test"

@ -1,20 +1,53 @@
package sqlstore
package alerting
import (
"bytes"
"context"
"fmt"
"strings"
"time"
"github.com/grafana/grafana/pkg/infra/localcache"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/db"
)
// timeNow makes it possible to test usage of time
var timeNow = time.Now
// AlertStore is a subset of SQLStore API to satisfy the needs of the alerting service.
// A subset is needed to make it easier to mock during the tests.
type AlertStore interface {
GetAlertById(context.Context, *models.GetAlertByIdQuery) error
GetAllAlertQueryHandler(context.Context, *models.GetAllAlertsQuery) error
GetAlertStatesForDashboard(context.Context, *models.GetAlertStatesForDashboardQuery) error
HandleAlertsQuery(context.Context, *models.GetAlertsQuery) error
SetAlertNotificationStateToCompleteCommand(context.Context, *models.SetAlertNotificationStateToCompleteCommand) error
SetAlertNotificationStateToPendingCommand(context.Context, *models.SetAlertNotificationStateToPendingCommand) error
GetAlertNotificationUidWithId(context.Context, *models.GetAlertNotificationUidQuery) error
GetAlertNotificationsWithUidToSend(context.Context, *models.GetAlertNotificationsWithUidToSendQuery) error
GetOrCreateAlertNotificationState(context.Context, *models.GetOrCreateNotificationStateQuery) error
SetAlertState(context.Context, *models.SetAlertStateCommand) error
PauseAlert(context.Context, *models.PauseAlertCommand) error
PauseAllAlerts(context.Context, *models.PauseAllAlertCommand) error
}
type sqlStore struct {
db db.DB
cache *localcache.CacheService
log *log.ConcreteLogger
}
func ProvideAlertStore(
db db.DB,
cacheService *localcache.CacheService) AlertStore {
return &sqlStore{
db: db,
cache: cacheService,
log: log.New("alerting.store"),
}
}
func (ss *SQLStore) GetAlertById(ctx context.Context, query *models.GetAlertByIdQuery) error {
return ss.WithDbSession(ctx, func(sess *DBSession) error {
func (ss *sqlStore) GetAlertById(ctx context.Context, query *models.GetAlertByIdQuery) error {
return ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
alert := models.Alert{}
has, err := sess.ID(query.Id).Get(&alert)
if !has {
@ -29,8 +62,8 @@ func (ss *SQLStore) GetAlertById(ctx context.Context, query *models.GetAlertById
})
}
func (ss *SQLStore) GetAllAlertQueryHandler(ctx context.Context, query *models.GetAllAlertsQuery) error {
return ss.WithDbSession(ctx, func(sess *DBSession) error {
func (ss *sqlStore) GetAllAlertQueryHandler(ctx context.Context, query *models.GetAllAlertsQuery) error {
return ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
var alerts []*models.Alert
err := sess.SQL("select * from alert").Find(&alerts)
if err != nil {
@ -42,8 +75,8 @@ func (ss *SQLStore) GetAllAlertQueryHandler(ctx context.Context, query *models.G
})
}
func deleteAlertByIdInternal(alertId int64, reason string, sess *DBSession) error {
sqlog.Debug("Deleting alert", "id", alertId, "reason", reason)
func deleteAlertByIdInternal(alertId int64, reason string, sess *sqlstore.DBSession, log *log.ConcreteLogger) error {
log.Debug("Deleting alert", "id", alertId, "reason", reason)
if _, err := sess.Exec("DELETE FROM alert WHERE id = ?", alertId); err != nil {
return err
@ -64,9 +97,9 @@ func deleteAlertByIdInternal(alertId int64, reason string, sess *DBSession) erro
return nil
}
func (ss *SQLStore) HandleAlertsQuery(ctx context.Context, query *models.GetAlertsQuery) error {
return ss.WithDbSession(ctx, func(sess *DBSession) error {
builder := SQLBuilder{}
func (ss *sqlStore) HandleAlertsQuery(ctx context.Context, query *models.GetAlertsQuery) error {
return ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
builder := sqlstore.SQLBuilder{}
builder.Write(`SELECT
alert.id,
@ -86,11 +119,11 @@ func (ss *SQLStore) HandleAlertsQuery(ctx context.Context, query *models.GetAler
builder.Write(`WHERE alert.org_id = ?`, query.OrgId)
if len(strings.TrimSpace(query.Query)) > 0 {
builder.Write(" AND alert.name "+dialect.LikeStr()+" ?", "%"+query.Query+"%")
builder.Write(" AND alert.name "+ss.db.GetDialect().LikeStr()+" ?", "%"+query.Query+"%")
}
if len(query.DashboardIDs) > 0 {
builder.sql.WriteString(` AND alert.dashboard_id IN (?` + strings.Repeat(",?", len(query.DashboardIDs)-1) + `) `)
builder.Write(` AND alert.dashboard_id IN (?` + strings.Repeat(",?", len(query.DashboardIDs)-1) + `) `)
for _, dbID := range query.DashboardIDs {
builder.AddParams(dbID)
@ -125,11 +158,11 @@ func (ss *SQLStore) HandleAlertsQuery(ctx context.Context, query *models.GetAler
builder.Write(" ORDER BY name ASC")
if query.Limit != 0 {
builder.Write(dialect.Limit(query.Limit))
builder.Write(ss.db.GetDialect().Limit(query.Limit))
}
alerts := make([]*models.AlertListItemDTO, 0)
if err := sess.SQL(builder.GetSQLString(), builder.params...).Find(&alerts); err != nil {
if err := sess.SQL(builder.GetSQLString(), builder.GetParams()...).Find(&alerts); err != nil {
return err
}
@ -144,18 +177,18 @@ func (ss *SQLStore) HandleAlertsQuery(ctx context.Context, query *models.GetAler
})
}
func (ss *SQLStore) SaveAlerts(ctx context.Context, dashID int64, alerts []*models.Alert) error {
return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
func (ss *sqlStore) SaveAlerts(ctx context.Context, dashID int64, alerts []*models.Alert) error {
return ss.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
existingAlerts, err := GetAlertsByDashboardId2(dashID, sess)
if err != nil {
return err
}
if err := updateAlerts(existingAlerts, alerts, sess); err != nil {
if err := updateAlerts(existingAlerts, alerts, sess, ss.log); err != nil {
return err
}
if err := deleteMissingAlerts(existingAlerts, alerts, sess); err != nil {
if err := deleteMissingAlerts(existingAlerts, alerts, sess, ss.log); err != nil {
return err
}
@ -163,7 +196,7 @@ func (ss *SQLStore) SaveAlerts(ctx context.Context, dashID int64, alerts []*mode
})
}
func updateAlerts(existingAlerts []*models.Alert, alerts []*models.Alert, sess *DBSession) error {
func updateAlerts(existingAlerts []*models.Alert, alerts []*models.Alert, sess *sqlstore.DBSession, log *log.ConcreteLogger) error {
for _, alert := range alerts {
update := false
var alertToUpdate *models.Alert
@ -188,7 +221,7 @@ func updateAlerts(existingAlerts []*models.Alert, alerts []*models.Alert, sess *
return err
}
sqlog.Debug("Alert updated", "name", alert.Name, "id", alert.Id)
log.Debug("Alert updated", "name", alert.Name, "id", alert.Id)
}
} else {
alert.Updated = timeNow()
@ -201,14 +234,14 @@ func updateAlerts(existingAlerts []*models.Alert, alerts []*models.Alert, sess *
return err
}
sqlog.Debug("Alert inserted", "name", alert.Name, "id", alert.Id)
log.Debug("Alert inserted", "name", alert.Name, "id", alert.Id)
}
tags := alert.GetTagsFromSettings()
if _, err := sess.Exec("DELETE FROM alert_rule_tag WHERE alert_id = ?", alert.Id); err != nil {
return err
}
if tags != nil {
tags, err := EnsureTagsExist(sess, tags)
tags, err := sqlstore.EnsureTagsExist(sess, tags)
if err != nil {
return err
}
@ -223,7 +256,7 @@ func updateAlerts(existingAlerts []*models.Alert, alerts []*models.Alert, sess *
return nil
}
func deleteMissingAlerts(alerts []*models.Alert, existingAlerts []*models.Alert, sess *DBSession) error {
func deleteMissingAlerts(alerts []*models.Alert, existingAlerts []*models.Alert, sess *sqlstore.DBSession, log *log.ConcreteLogger) error {
for _, missingAlert := range alerts {
missing := true
@ -235,7 +268,7 @@ func deleteMissingAlerts(alerts []*models.Alert, existingAlerts []*models.Alert,
}
if missing {
if err := deleteAlertByIdInternal(missingAlert.Id, "Removed from dashboard", sess); err != nil {
if err := deleteAlertByIdInternal(missingAlert.Id, "Removed from dashboard", sess, log); err != nil {
// No use trying to delete more, since we're in a transaction and it will be
// rolled back on error.
return err
@ -246,7 +279,7 @@ func deleteMissingAlerts(alerts []*models.Alert, existingAlerts []*models.Alert,
return nil
}
func GetAlertsByDashboardId2(dashboardId int64, sess *DBSession) ([]*models.Alert, error) {
func GetAlertsByDashboardId2(dashboardId int64, sess *sqlstore.DBSession) ([]*models.Alert, error) {
alerts := make([]*models.Alert, 0)
err := sess.Where("dashboard_id = ?", dashboardId).Find(&alerts)
@ -257,8 +290,8 @@ func GetAlertsByDashboardId2(dashboardId int64, sess *DBSession) ([]*models.Aler
return alerts, nil
}
func (ss *SQLStore) SetAlertState(ctx context.Context, cmd *models.SetAlertStateCommand) error {
return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
func (ss *sqlStore) SetAlertState(ctx context.Context, cmd *models.SetAlertStateCommand) error {
return ss.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
alert := models.Alert{}
if has, err := sess.ID(cmd.AlertId).Get(&alert); err != nil {
@ -296,8 +329,8 @@ func (ss *SQLStore) SetAlertState(ctx context.Context, cmd *models.SetAlertState
})
}
func (ss *SQLStore) PauseAlert(ctx context.Context, cmd *models.PauseAlertCommand) error {
return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
func (ss *sqlStore) PauseAlert(ctx context.Context, cmd *models.PauseAlertCommand) error {
return ss.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
if len(cmd.AlertIds) == 0 {
return fmt.Errorf("command contains no alertids")
}
@ -330,8 +363,8 @@ func (ss *SQLStore) PauseAlert(ctx context.Context, cmd *models.PauseAlertComman
})
}
func (ss *SQLStore) PauseAllAlerts(ctx context.Context, cmd *models.PauseAllAlertCommand) error {
return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
func (ss *sqlStore) PauseAllAlerts(ctx context.Context, cmd *models.PauseAllAlertCommand) error {
return ss.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
var newState string
if cmd.Paused {
newState = string(models.AlertStatePaused)
@ -348,8 +381,8 @@ func (ss *SQLStore) PauseAllAlerts(ctx context.Context, cmd *models.PauseAllAler
})
}
func (ss *SQLStore) GetAlertStatesForDashboard(ctx context.Context, query *models.GetAlertStatesForDashboardQuery) error {
return ss.WithDbSession(ctx, func(sess *DBSession) error {
func (ss *sqlStore) GetAlertStatesForDashboard(ctx context.Context, query *models.GetAlertStatesForDashboardQuery) error {
return ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
var rawSQL = `SELECT
id,
dashboard_id,

@ -1,4 +1,4 @@
package sqlstore
package alerting
import (
"bytes"
@ -9,6 +9,7 @@ import (
"time"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/util"
)
@ -28,8 +29,11 @@ type AlertNotificationStore interface {
GetOrCreateAlertNotificationState(ctx context.Context, cmd *models.GetOrCreateNotificationStateQuery) error
}
func (ss *SQLStore) DeleteAlertNotification(ctx context.Context, cmd *models.DeleteAlertNotificationCommand) error {
return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
// timeNow makes it possible to test usage of time
var timeNow = time.Now
func (ss *sqlStore) DeleteAlertNotification(ctx context.Context, cmd *models.DeleteAlertNotificationCommand) error {
return ss.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
sql := "DELETE FROM alert_notification WHERE alert_notification.org_id = ? AND alert_notification.id = ?"
res, err := sess.Exec(sql, cmd.OrgId, cmd.Id)
if err != nil {
@ -52,9 +56,9 @@ func (ss *SQLStore) DeleteAlertNotification(ctx context.Context, cmd *models.Del
})
}
func (ss *SQLStore) DeleteAlertNotificationWithUid(ctx context.Context, cmd *models.DeleteAlertNotificationWithUidCommand) error {
func (ss *sqlStore) DeleteAlertNotificationWithUid(ctx context.Context, cmd *models.DeleteAlertNotificationWithUidCommand) error {
existingNotification := &models.GetAlertNotificationsWithUidQuery{OrgId: cmd.OrgId, Uid: cmd.Uid}
if err := getAlertNotificationWithUidInternal(ctx, existingNotification, ss.newSession(ctx)); err != nil {
if err := getAlertNotificationWithUidInternal(ctx, existingNotification, ss.db.NewSession(ctx)); err != nil {
return err
}
@ -74,24 +78,24 @@ func (ss *SQLStore) DeleteAlertNotificationWithUid(ctx context.Context, cmd *mod
return nil
}
func (ss *SQLStore) GetAlertNotifications(ctx context.Context, query *models.GetAlertNotificationsQuery) error {
return getAlertNotificationInternal(ctx, query, ss.newSession(ctx))
func (ss *sqlStore) GetAlertNotifications(ctx context.Context, query *models.GetAlertNotificationsQuery) error {
return getAlertNotificationInternal(ctx, query, ss.db.NewSession(ctx))
}
func (ss *SQLStore) GetAlertNotificationUidWithId(ctx context.Context, query *models.GetAlertNotificationUidQuery) error {
func (ss *sqlStore) GetAlertNotificationUidWithId(ctx context.Context, query *models.GetAlertNotificationUidQuery) error {
cacheKey := newAlertNotificationUidCacheKey(query.OrgId, query.Id)
if cached, found := ss.CacheService.Get(cacheKey); found {
if cached, found := ss.cache.Get(cacheKey); found {
query.Result = cached.(string)
return nil
}
err := getAlertNotificationUidInternal(ctx, query, ss.newSession(ctx))
err := getAlertNotificationUidInternal(ctx, query, ss.db.NewSession(ctx))
if err != nil {
return err
}
ss.CacheService.Set(cacheKey, query.Result, -1) // Infinite, never changes
ss.cache.Set(cacheKey, query.Result, -1) // Infinite, never changes
return nil
}
@ -100,12 +104,12 @@ func newAlertNotificationUidCacheKey(orgID, notificationId int64) string {
return fmt.Sprintf("notification-uid-by-org-%d-and-id-%d", orgID, notificationId)
}
func (ss *SQLStore) GetAlertNotificationsWithUid(ctx context.Context, query *models.GetAlertNotificationsWithUidQuery) error {
return getAlertNotificationWithUidInternal(ctx, query, ss.newSession(ctx))
func (ss *sqlStore) GetAlertNotificationsWithUid(ctx context.Context, query *models.GetAlertNotificationsWithUidQuery) error {
return getAlertNotificationWithUidInternal(ctx, query, ss.db.NewSession(ctx))
}
func (ss *SQLStore) GetAllAlertNotifications(ctx context.Context, query *models.GetAllAlertNotificationsQuery) error {
return ss.WithDbSession(ctx, func(sess *DBSession) error {
func (ss *sqlStore) GetAllAlertNotifications(ctx context.Context, query *models.GetAllAlertNotificationsQuery) error {
return ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
results := make([]*models.AlertNotification, 0)
if err := sess.Where("org_id = ?", query.OrgId).Asc("name").Find(&results); err != nil {
return err
@ -116,8 +120,8 @@ func (ss *SQLStore) GetAllAlertNotifications(ctx context.Context, query *models.
})
}
func (ss *SQLStore) GetAlertNotificationsWithUidToSend(ctx context.Context, query *models.GetAlertNotificationsWithUidToSendQuery) error {
return ss.WithDbSession(ctx, func(sess *DBSession) error {
func (ss *sqlStore) GetAlertNotificationsWithUidToSend(ctx context.Context, query *models.GetAlertNotificationsWithUidToSendQuery) error {
return ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
var sql bytes.Buffer
params := make([]interface{}, 0)
@ -142,7 +146,7 @@ func (ss *SQLStore) GetAlertNotificationsWithUidToSend(ctx context.Context, quer
params = append(params, query.OrgId)
sql.WriteString(` AND ((alert_notification.is_default = ?)`)
params = append(params, dialect.BooleanStr(true))
params = append(params, ss.db.GetDialect().BooleanStr(true))
if len(query.Uids) > 0 {
sql.WriteString(` OR alert_notification.uid IN (?` + strings.Repeat(",?", len(query.Uids)-1) + ")")
@ -162,7 +166,7 @@ func (ss *SQLStore) GetAlertNotificationsWithUidToSend(ctx context.Context, quer
})
}
func getAlertNotificationUidInternal(ctx context.Context, query *models.GetAlertNotificationUidQuery, sess *DBSession) error {
func getAlertNotificationUidInternal(ctx context.Context, query *models.GetAlertNotificationUidQuery, sess *sqlstore.DBSession) error {
var sql bytes.Buffer
params := make([]interface{}, 0)
@ -191,7 +195,7 @@ func getAlertNotificationUidInternal(ctx context.Context, query *models.GetAlert
return nil
}
func getAlertNotificationInternal(ctx context.Context, query *models.GetAlertNotificationsQuery, sess *DBSession) error {
func getAlertNotificationInternal(ctx context.Context, query *models.GetAlertNotificationsQuery, sess *sqlstore.DBSession) error {
var sql bytes.Buffer
params := make([]interface{}, 0)
@ -241,7 +245,7 @@ func getAlertNotificationInternal(ctx context.Context, query *models.GetAlertNot
return nil
}
func getAlertNotificationWithUidInternal(ctx context.Context, query *models.GetAlertNotificationsWithUidQuery, sess *DBSession) error {
func getAlertNotificationWithUidInternal(ctx context.Context, query *models.GetAlertNotificationsWithUidQuery, sess *sqlstore.DBSession) error {
var sql bytes.Buffer
params := make([]interface{}, 0)
@ -279,8 +283,8 @@ func getAlertNotificationWithUidInternal(ctx context.Context, query *models.GetA
return nil
}
func (ss *SQLStore) CreateAlertNotificationCommand(ctx context.Context, cmd *models.CreateAlertNotificationCommand) error {
return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
func (ss *sqlStore) CreateAlertNotificationCommand(ctx context.Context, cmd *models.CreateAlertNotificationCommand) error {
return ss.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
if cmd.Uid == "" {
uid, uidGenerationErr := generateNewAlertNotificationUid(ctx, sess, cmd.OrgId)
if uidGenerationErr != nil {
@ -353,7 +357,7 @@ func (ss *SQLStore) CreateAlertNotificationCommand(ctx context.Context, cmd *mod
})
}
func generateNewAlertNotificationUid(ctx context.Context, sess *DBSession, orgId int64) (string, error) {
func generateNewAlertNotificationUid(ctx context.Context, sess *sqlstore.DBSession, orgId int64) (string, error) {
for i := 0; i < 3; i++ {
uid := util.GenerateShortUID()
exists, err := sess.Where("org_id=? AND uid=?", orgId, uid).Get(&models.AlertNotification{})
@ -369,8 +373,8 @@ func generateNewAlertNotificationUid(ctx context.Context, sess *DBSession, orgId
return "", models.ErrAlertNotificationFailedGenerateUniqueUid
}
func (ss *SQLStore) UpdateAlertNotification(ctx context.Context, cmd *models.UpdateAlertNotificationCommand) error {
return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) (err error) {
func (ss *sqlStore) UpdateAlertNotification(ctx context.Context, cmd *models.UpdateAlertNotificationCommand) error {
return ss.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) (err error) {
current := models.AlertNotification{}
if _, err = sess.ID(cmd.Id).Get(&current); err != nil {
@ -437,10 +441,10 @@ func (ss *SQLStore) UpdateAlertNotification(ctx context.Context, cmd *models.Upd
})
}
func (ss *SQLStore) UpdateAlertNotificationWithUid(ctx context.Context, cmd *models.UpdateAlertNotificationWithUidCommand) error {
func (ss *sqlStore) UpdateAlertNotificationWithUid(ctx context.Context, cmd *models.UpdateAlertNotificationWithUidCommand) error {
getAlertNotificationWithUidQuery := &models.GetAlertNotificationsWithUidQuery{OrgId: cmd.OrgId, Uid: cmd.Uid}
if err := getAlertNotificationWithUidInternal(ctx, getAlertNotificationWithUidQuery, ss.newSession(ctx)); err != nil {
if err := getAlertNotificationWithUidInternal(ctx, getAlertNotificationWithUidQuery, ss.db.NewSession(ctx)); err != nil {
return err
}
@ -478,8 +482,8 @@ func (ss *SQLStore) UpdateAlertNotificationWithUid(ctx context.Context, cmd *mod
return nil
}
func (ss *SQLStore) SetAlertNotificationStateToCompleteCommand(ctx context.Context, cmd *models.SetAlertNotificationStateToCompleteCommand) error {
return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
func (ss *sqlStore) SetAlertNotificationStateToCompleteCommand(ctx context.Context, cmd *models.SetAlertNotificationStateToCompleteCommand) error {
return ss.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
version := cmd.Version
var current models.AlertNotificationState
if _, err := sess.ID(cmd.Id).Get(&current); err != nil {
@ -500,15 +504,15 @@ func (ss *SQLStore) SetAlertNotificationStateToCompleteCommand(ctx context.Conte
}
if current.Version != version {
sqlog.Error("notification state out of sync. the notification is marked as complete but has been modified between set as pending and completion.", "notifierId", current.NotifierId)
ss.log.Error("notification state out of sync. the notification is marked as complete but has been modified between set as pending and completion.", "notifierId", current.NotifierId)
}
return nil
})
}
func (ss *SQLStore) SetAlertNotificationStateToPendingCommand(ctx context.Context, cmd *models.SetAlertNotificationStateToPendingCommand) error {
return ss.WithDbSession(ctx, func(sess *DBSession) error {
func (ss *sqlStore) SetAlertNotificationStateToPendingCommand(ctx context.Context, cmd *models.SetAlertNotificationStateToPendingCommand) error {
return ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
newVersion := cmd.Version + 1
sql := `UPDATE alert_notification_state SET
state = ?,
@ -543,8 +547,8 @@ func (ss *SQLStore) SetAlertNotificationStateToPendingCommand(ctx context.Contex
})
}
func (ss *SQLStore) GetOrCreateAlertNotificationState(ctx context.Context, cmd *models.GetOrCreateNotificationStateQuery) error {
return ss.WithTransactionalDbSession(ctx, func(sess *DBSession) error {
func (ss *sqlStore) GetOrCreateAlertNotificationState(ctx context.Context, cmd *models.GetOrCreateNotificationStateQuery) error {
return ss.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
nj := &models.AlertNotificationState{}
exist, err := getAlertNotificationState(ctx, sess, cmd, nj)
@ -568,7 +572,7 @@ func (ss *SQLStore) GetOrCreateAlertNotificationState(ctx context.Context, cmd *
}
if _, err := sess.Insert(notificationState); err != nil {
if dialect.IsUniqueConstraintViolation(err) {
if ss.db.GetDialect().IsUniqueConstraintViolation(err) {
exist, err = getAlertNotificationState(ctx, sess, cmd, nj)
if err != nil {
@ -591,7 +595,7 @@ func (ss *SQLStore) GetOrCreateAlertNotificationState(ctx context.Context, cmd *
})
}
func getAlertNotificationState(ctx context.Context, sess *DBSession, cmd *models.GetOrCreateNotificationStateQuery, nj *models.AlertNotificationState) (bool, error) {
func getAlertNotificationState(ctx context.Context, sess *sqlstore.DBSession, cmd *models.GetOrCreateNotificationStateQuery, nj *models.AlertNotificationState) (bool, error) {
return sess.
Where("alert_notification_state.org_id = ?", cmd.OrgId).
Where("alert_notification_state.alert_id = ?", cmd.AlertId).

@ -1,4 +1,4 @@
package sqlstore
package alerting
import (
"context"
@ -8,7 +8,10 @@ import (
"time"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/localcache"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/stretchr/testify/require"
)
@ -17,8 +20,13 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test")
}
var sqlStore *SQLStore
setup := func() { sqlStore = InitTestDB(t) }
var store *sqlStore
setup := func() {
store = &sqlStore{
db: sqlstore.InitTestDB(t),
log: log.New(),
cache: localcache.New(time.Minute, time.Minute)}
}
t.Run("Alert notification state", func(t *testing.T) {
setup()
@ -33,7 +41,7 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
t.Run("Get no existing state should create a new state", func(t *testing.T) {
query := &models.GetOrCreateNotificationStateQuery{AlertId: alertID, OrgId: orgID, NotifierId: notifierID}
err := sqlStore.GetOrCreateAlertNotificationState(context.Background(), query)
err := store.GetOrCreateAlertNotificationState(context.Background(), query)
require.Nil(t, err)
require.NotNil(t, query.Result)
require.Equal(t, models.AlertNotificationStateUnknown, query.Result.State)
@ -42,7 +50,7 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
t.Run("Get existing state should not create a new state", func(t *testing.T) {
query2 := &models.GetOrCreateNotificationStateQuery{AlertId: alertID, OrgId: orgID, NotifierId: notifierID}
err := sqlStore.GetOrCreateAlertNotificationState(context.Background(), query2)
err := store.GetOrCreateAlertNotificationState(context.Background(), query2)
require.Nil(t, err)
require.NotNil(t, query2.Result)
require.Equal(t, query.Result.Id, query2.Result.Id)
@ -58,12 +66,12 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
AlertRuleStateUpdatedVersion: s.AlertRuleStateUpdatedVersion,
}
err := sqlStore.SetAlertNotificationStateToPendingCommand(context.Background(), &cmd)
err := store.SetAlertNotificationStateToPendingCommand(context.Background(), &cmd)
require.Nil(t, err)
require.Equal(t, int64(1), cmd.ResultVersion)
query2 := &models.GetOrCreateNotificationStateQuery{AlertId: alertID, OrgId: orgID, NotifierId: notifierID}
err = sqlStore.GetOrCreateAlertNotificationState(context.Background(), query2)
err = store.GetOrCreateAlertNotificationState(context.Background(), query2)
require.Nil(t, err)
require.Equal(t, int64(1), query2.Result.Version)
require.Equal(t, models.AlertNotificationStatePending, query2.Result.State)
@ -75,11 +83,11 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
Id: s.Id,
Version: cmd.ResultVersion,
}
err := sqlStore.SetAlertNotificationStateToCompleteCommand(context.Background(), &setStateCmd)
err := store.SetAlertNotificationStateToCompleteCommand(context.Background(), &setStateCmd)
require.Nil(t, err)
query3 := &models.GetOrCreateNotificationStateQuery{AlertId: alertID, OrgId: orgID, NotifierId: notifierID}
err = sqlStore.GetOrCreateAlertNotificationState(context.Background(), query3)
err = store.GetOrCreateAlertNotificationState(context.Background(), query3)
require.Nil(t, err)
require.Equal(t, int64(2), query3.Result.Version)
require.Equal(t, models.AlertNotificationStateCompleted, query3.Result.State)
@ -93,11 +101,11 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
Id: s.Id,
Version: unknownVersion,
}
err := sqlStore.SetAlertNotificationStateToCompleteCommand(context.Background(), &cmd)
err := store.SetAlertNotificationStateToCompleteCommand(context.Background(), &cmd)
require.Nil(t, err)
query3 := &models.GetOrCreateNotificationStateQuery{AlertId: alertID, OrgId: orgID, NotifierId: notifierID}
err = sqlStore.GetOrCreateAlertNotificationState(context.Background(), query3)
err = store.GetOrCreateAlertNotificationState(context.Background(), query3)
require.Nil(t, err)
require.Equal(t, unknownVersion+1, query3.Result.Version)
require.Equal(t, models.AlertNotificationStateCompleted, query3.Result.State)
@ -113,7 +121,7 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
Version: s.Version,
AlertRuleStateUpdatedVersion: s.AlertRuleStateUpdatedVersion,
}
err := sqlStore.SetAlertNotificationStateToPendingCommand(context.Background(), &cmd)
err := store.SetAlertNotificationStateToPendingCommand(context.Background(), &cmd)
require.Equal(t, models.ErrAlertNotificationStateVersionConflict, err)
})
@ -124,7 +132,7 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
Version: s.Version,
AlertRuleStateUpdatedVersion: 1000,
}
err := sqlStore.SetAlertNotificationStateToPendingCommand(context.Background(), &cmd)
err := store.SetAlertNotificationStateToPendingCommand(context.Background(), &cmd)
require.Nil(t, err)
require.Equal(t, int64(1), cmd.ResultVersion)
@ -138,7 +146,7 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
Version: s.Version,
AlertRuleStateUpdatedVersion: s.AlertRuleStateUpdatedVersion,
}
err := sqlStore.SetAlertNotificationStateToPendingCommand(context.Background(), &cmd)
err := store.SetAlertNotificationStateToPendingCommand(context.Background(), &cmd)
require.Error(t, err)
})
})
@ -151,7 +159,7 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
Name: "email",
}
err := sqlStore.GetAlertNotifications(context.Background(), cmd)
err := store.GetAlertNotifications(context.Background(), cmd)
require.Nil(t, err)
require.Nil(t, cmd.Result)
})
@ -167,13 +175,13 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
}
t.Run("and missing frequency", func(t *testing.T) {
err := sqlStore.CreateAlertNotificationCommand(context.Background(), cmd)
err := store.CreateAlertNotificationCommand(context.Background(), cmd)
require.Equal(t, models.ErrNotificationFrequencyNotFound, err)
})
t.Run("invalid frequency", func(t *testing.T) {
cmd.Frequency = "invalid duration"
err := sqlStore.CreateAlertNotificationCommand(context.Background(), cmd)
err := store.CreateAlertNotificationCommand(context.Background(), cmd)
require.True(t, regexp.MustCompile(`^time: invalid duration "?invalid duration"?$`).MatchString(
err.Error()))
})
@ -189,7 +197,7 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
Settings: simplejson.New(),
}
err := sqlStore.CreateAlertNotificationCommand(context.Background(), cmd)
err := store.CreateAlertNotificationCommand(context.Background(), cmd)
require.Nil(t, err)
updateCmd := &models.UpdateAlertNotificationCommand{
@ -198,14 +206,14 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
}
t.Run("and missing frequency", func(t *testing.T) {
err := sqlStore.UpdateAlertNotification(context.Background(), updateCmd)
err := store.UpdateAlertNotification(context.Background(), updateCmd)
require.Equal(t, models.ErrNotificationFrequencyNotFound, err)
})
t.Run("invalid frequency", func(t *testing.T) {
updateCmd.Frequency = "invalid duration"
err := sqlStore.UpdateAlertNotification(context.Background(), updateCmd)
err := store.UpdateAlertNotification(context.Background(), updateCmd)
require.Error(t, err)
require.True(t, regexp.MustCompile(`^time: invalid duration "?invalid duration"?$`).MatchString(
err.Error()))
@ -223,7 +231,7 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
Settings: simplejson.New(),
}
err := sqlStore.CreateAlertNotificationCommand(context.Background(), cmd)
err := store.CreateAlertNotificationCommand(context.Background(), cmd)
require.Nil(t, err)
require.NotEqual(t, 0, cmd.Result.Id)
require.NotEqual(t, 0, cmd.Result.OrgId)
@ -233,7 +241,7 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
require.NotEmpty(t, cmd.Result.Uid)
t.Run("Cannot save Alert Notification with the same name", func(t *testing.T) {
err = sqlStore.CreateAlertNotificationCommand(context.Background(), cmd)
err = store.CreateAlertNotificationCommand(context.Background(), cmd)
require.Error(t, err)
})
t.Run("Cannot save Alert Notification with the same name and another uid", func(t *testing.T) {
@ -246,7 +254,7 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
Settings: cmd.Settings,
Uid: "notifier1",
}
err = sqlStore.CreateAlertNotificationCommand(context.Background(), anotherUidCmd)
err = store.CreateAlertNotificationCommand(context.Background(), anotherUidCmd)
require.Error(t, err)
})
t.Run("Can save Alert Notification with another name and another uid", func(t *testing.T) {
@ -259,7 +267,7 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
Settings: cmd.Settings,
Uid: "notifier2",
}
err = sqlStore.CreateAlertNotificationCommand(context.Background(), anotherUidCmd)
err = store.CreateAlertNotificationCommand(context.Background(), anotherUidCmd)
require.Nil(t, err)
})
@ -274,7 +282,7 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
Settings: simplejson.New(),
Id: cmd.Result.Id,
}
err := sqlStore.UpdateAlertNotification(context.Background(), newCmd)
err := store.UpdateAlertNotification(context.Background(), newCmd)
require.Nil(t, err)
require.Equal(t, "NewName", newCmd.Result.Name)
require.Equal(t, 60*time.Second, newCmd.Result.Frequency)
@ -290,7 +298,7 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
Settings: simplejson.New(),
Id: cmd.Result.Id,
}
err := sqlStore.UpdateAlertNotification(context.Background(), newCmd)
err := store.UpdateAlertNotification(context.Background(), newCmd)
require.Nil(t, err)
require.False(t, newCmd.Result.SendReminder)
})
@ -305,11 +313,11 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
otherOrg := models.CreateAlertNotificationCommand{Name: "default", Type: "email", OrgId: 2, SendReminder: true, Frequency: "10s", Settings: simplejson.New()}
require.Nil(t, sqlStore.CreateAlertNotificationCommand(context.Background(), &cmd1))
require.Nil(t, sqlStore.CreateAlertNotificationCommand(context.Background(), &cmd2))
require.Nil(t, sqlStore.CreateAlertNotificationCommand(context.Background(), &cmd3))
require.Nil(t, sqlStore.CreateAlertNotificationCommand(context.Background(), &cmd4))
require.Nil(t, sqlStore.CreateAlertNotificationCommand(context.Background(), &otherOrg))
require.Nil(t, store.CreateAlertNotificationCommand(context.Background(), &cmd1))
require.Nil(t, store.CreateAlertNotificationCommand(context.Background(), &cmd2))
require.Nil(t, store.CreateAlertNotificationCommand(context.Background(), &cmd3))
require.Nil(t, store.CreateAlertNotificationCommand(context.Background(), &cmd4))
require.Nil(t, store.CreateAlertNotificationCommand(context.Background(), &otherOrg))
t.Run("search", func(t *testing.T) {
query := &models.GetAlertNotificationsWithUidToSendQuery{
@ -317,7 +325,7 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
OrgId: 1,
}
err := sqlStore.GetAlertNotificationsWithUidToSend(context.Background(), query)
err := store.GetAlertNotificationsWithUidToSend(context.Background(), query)
require.Nil(t, err)
require.Equal(t, 3, len(query.Result))
})
@ -327,7 +335,7 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
OrgId: 1,
}
err := sqlStore.GetAllAlertNotifications(context.Background(), query)
err := store.GetAllAlertNotifications(context.Background(), query)
require.Nil(t, err)
require.Equal(t, 4, len(query.Result))
require.Equal(t, cmd4.Name, query.Result[0].Name)
@ -339,10 +347,9 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
t.Run("Notification Uid by Id Caching", func(t *testing.T) {
setup()
ss := InitTestDB(t)
notification := &models.CreateAlertNotificationCommand{Uid: "aNotificationUid", OrgId: 1, Name: "aNotificationUid"}
err := sqlStore.CreateAlertNotificationCommand(context.Background(), notification)
err := store.CreateAlertNotificationCommand(context.Background(), notification)
require.Nil(t, err)
byUidQuery := &models.GetAlertNotificationsWithUidQuery{
@ -350,7 +357,7 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
OrgId: notification.OrgId,
}
notificationByUidErr := sqlStore.GetAlertNotificationsWithUid(context.Background(), byUidQuery)
notificationByUidErr := store.GetAlertNotificationsWithUid(context.Background(), byUidQuery)
require.Nil(t, notificationByUidErr)
t.Run("Can cache notification Uid", func(t *testing.T) {
@ -361,14 +368,14 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
cacheKey := newAlertNotificationUidCacheKey(byIdQuery.OrgId, byIdQuery.Id)
resultBeforeCaching, foundBeforeCaching := ss.CacheService.Get(cacheKey)
resultBeforeCaching, foundBeforeCaching := store.cache.Get(cacheKey)
require.False(t, foundBeforeCaching)
require.Nil(t, resultBeforeCaching)
notificationByIdErr := ss.GetAlertNotificationUidWithId(context.Background(), byIdQuery)
notificationByIdErr := store.GetAlertNotificationUidWithId(context.Background(), byIdQuery)
require.Nil(t, notificationByIdErr)
resultAfterCaching, foundAfterCaching := ss.CacheService.Get(cacheKey)
resultAfterCaching, foundAfterCaching := store.cache.Get(cacheKey)
require.True(t, foundAfterCaching)
require.Equal(t, notification.Uid, resultAfterCaching)
})
@ -379,9 +386,9 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
OrgId: 100,
}
cacheKey := newAlertNotificationUidCacheKey(query.OrgId, query.Id)
ss.CacheService.Set(cacheKey, "a-cached-uid", -1)
store.cache.Set(cacheKey, "a-cached-uid", -1)
err := ss.GetAlertNotificationUidWithId(context.Background(), query)
err := store.GetAlertNotificationUidWithId(context.Background(), query)
require.Nil(t, err)
require.Equal(t, "a-cached-uid", query.Result)
})
@ -392,13 +399,13 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
OrgId: 100,
}
err := ss.GetAlertNotificationUidWithId(context.Background(), query)
err := store.GetAlertNotificationUidWithId(context.Background(), query)
require.Equal(t, "", query.Result)
require.Error(t, err)
require.True(t, errors.Is(err, models.ErrAlertNotificationFailedTranslateUniqueID))
cacheKey := newAlertNotificationUidCacheKey(query.OrgId, query.Id)
result, found := ss.CacheService.Get(cacheKey)
result, found := store.cache.Get(cacheKey)
require.False(t, found)
require.Nil(t, result)
})
@ -416,7 +423,7 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
Settings: simplejson.New(),
Id: 1,
}
err := sqlStore.UpdateAlertNotification(context.Background(), updateCmd)
err := store.UpdateAlertNotification(context.Background(), updateCmd)
require.Equal(t, models.ErrAlertNotificationNotFound, err)
t.Run("using UID", func(t *testing.T) {
@ -431,7 +438,7 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
Uid: "uid",
NewUid: "newUid",
}
err := sqlStore.UpdateAlertNotificationWithUid(context.Background(), updateWithUidCmd)
err := store.UpdateAlertNotificationWithUid(context.Background(), updateWithUidCmd)
require.Equal(t, models.ErrAlertNotificationNotFound, err)
})
})
@ -446,18 +453,18 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
Settings: simplejson.New(),
}
err := sqlStore.CreateAlertNotificationCommand(context.Background(), cmd)
err := store.CreateAlertNotificationCommand(context.Background(), cmd)
require.Nil(t, err)
deleteCmd := &models.DeleteAlertNotificationCommand{
Id: cmd.Result.Id,
OrgId: 1,
}
err = sqlStore.DeleteAlertNotification(context.Background(), deleteCmd)
err = store.DeleteAlertNotification(context.Background(), deleteCmd)
require.Nil(t, err)
t.Run("using UID", func(t *testing.T) {
err := sqlStore.CreateAlertNotificationCommand(context.Background(), cmd)
err := store.CreateAlertNotificationCommand(context.Background(), cmd)
require.Nil(t, err)
deleteWithUidCmd := &models.DeleteAlertNotificationWithUidCommand{
@ -465,7 +472,7 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
OrgId: 1,
}
err = sqlStore.DeleteAlertNotificationWithUid(context.Background(), deleteWithUidCmd)
err = store.DeleteAlertNotificationWithUid(context.Background(), deleteWithUidCmd)
require.Nil(t, err)
require.Equal(t, cmd.Result.Id, deleteWithUidCmd.DeletedAlertNotificationId)
})
@ -477,7 +484,7 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
Id: 1,
OrgId: 1,
}
err := sqlStore.DeleteAlertNotification(context.Background(), deleteCmd)
err := store.DeleteAlertNotification(context.Background(), deleteCmd)
require.Equal(t, models.ErrAlertNotificationNotFound, err)
t.Run("using UID", func(t *testing.T) {
@ -485,7 +492,7 @@ func TestIntegrationAlertNotificationSQLAccess(t *testing.T) {
Uid: "uid",
OrgId: 1,
}
err = sqlStore.DeleteAlertNotificationWithUid(context.Background(), deleteWithUidCmd)
err = store.DeleteAlertNotificationWithUid(context.Background(), deleteWithUidCmd)
require.Equal(t, models.ErrAlertNotificationNotFound, err)
})
})

@ -1,4 +1,4 @@
package sqlstore
package alerting
import (
"context"
@ -6,7 +6,13 @@ import (
"time"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/dashboards"
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/db"
"github.com/grafana/grafana/pkg/util"
"github.com/stretchr/testify/require"
)
@ -32,14 +38,17 @@ func TestIntegrationAlertingDataAccess(t *testing.T) {
mockTimeNow()
defer resetTimeNow()
var sqlStore *SQLStore
var store *sqlStore
var testDash *models.Dashboard
var items []*models.Alert
setup := func(t *testing.T) {
sqlStore = InitTestDB(t)
store = &sqlStore{
db: sqlstore.InitTestDB(t),
log: log.New(),
}
testDash = insertTestDashboard(t, sqlStore, "dashboard with alerts", 1, 0, false, "alert")
testDash = insertTestDashboard(t, store.db, "dashboard with alerts", 1, 0, false, "alert")
evalData, err := simplejson.NewJson([]byte(`{"test": "test"}`))
require.Nil(t, err)
items = []*models.Alert{
@ -55,7 +64,7 @@ func TestIntegrationAlertingDataAccess(t *testing.T) {
},
}
err = sqlStore.SaveAlerts(context.Background(), testDash.Id, items)
err = store.SaveAlerts(context.Background(), testDash.Id, items)
require.Nil(t, err)
}
@ -64,7 +73,7 @@ func TestIntegrationAlertingDataAccess(t *testing.T) {
// Get alert so we can use its ID in tests
alertQuery := models.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, PanelId: 1, OrgId: 1, User: &models.SignedInUser{OrgRole: models.ROLE_ADMIN}}
err2 := sqlStore.HandleAlertsQuery(context.Background(), &alertQuery)
err2 := store.HandleAlertsQuery(context.Background(), &alertQuery)
require.Nil(t, err2)
insertedAlert := alertQuery.Result[0]
@ -75,15 +84,15 @@ func TestIntegrationAlertingDataAccess(t *testing.T) {
State: models.AlertStateOK,
}
err := sqlStore.SetAlertState(context.Background(), cmd)
err := store.SetAlertState(context.Background(), cmd)
require.Nil(t, err)
})
alert, _ := getAlertById(t, insertedAlert.Id, sqlStore)
alert, _ := getAlertById(t, insertedAlert.Id, store)
stateDateBeforePause := alert.NewStateDate
t.Run("can pause all alerts", func(t *testing.T) {
err := sqlStore.pauseAllAlerts(t, true)
err := store.pauseAllAlerts(t, true)
require.Nil(t, err)
t.Run("cannot updated paused alert", func(t *testing.T) {
@ -92,26 +101,26 @@ func TestIntegrationAlertingDataAccess(t *testing.T) {
State: models.AlertStateOK,
}
err = sqlStore.SetAlertState(context.Background(), cmd)
err = store.SetAlertState(context.Background(), cmd)
require.Error(t, err)
})
t.Run("alert is paused", func(t *testing.T) {
alert, _ = getAlertById(t, insertedAlert.Id, sqlStore)
alert, _ = getAlertById(t, insertedAlert.Id, store)
currentState := alert.State
require.Equal(t, models.AlertStatePaused, currentState)
})
t.Run("pausing alerts should update their NewStateDate", func(t *testing.T) {
alert, _ = getAlertById(t, insertedAlert.Id, sqlStore)
alert, _ = getAlertById(t, insertedAlert.Id, store)
stateDateAfterPause := alert.NewStateDate
require.True(t, stateDateBeforePause.Before(stateDateAfterPause))
})
t.Run("unpausing alerts should update their NewStateDate again", func(t *testing.T) {
err := sqlStore.pauseAllAlerts(t, false)
err := store.pauseAllAlerts(t, false)
require.Nil(t, err)
alert, _ = getAlertById(t, insertedAlert.Id, sqlStore)
alert, _ = getAlertById(t, insertedAlert.Id, store)
stateDateAfterUnpause := alert.NewStateDate
require.True(t, stateDateBeforePause.Before(stateDateAfterUnpause))
})
@ -121,7 +130,7 @@ func TestIntegrationAlertingDataAccess(t *testing.T) {
t.Run("Can read properties", func(t *testing.T) {
setup(t)
alertQuery := models.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, PanelId: 1, OrgId: 1, User: &models.SignedInUser{OrgRole: models.ROLE_ADMIN}}
err2 := sqlStore.HandleAlertsQuery(context.Background(), &alertQuery)
err2 := store.HandleAlertsQuery(context.Background(), &alertQuery)
alert := alertQuery.Result[0]
require.Nil(t, err2)
@ -143,7 +152,7 @@ func TestIntegrationAlertingDataAccess(t *testing.T) {
setup(t)
viewerUser := &models.SignedInUser{OrgRole: models.ROLE_VIEWER, OrgId: 1}
alertQuery := models.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, PanelId: 1, OrgId: 1, User: viewerUser}
err2 := sqlStore.HandleAlertsQuery(context.Background(), &alertQuery)
err2 := store.HandleAlertsQuery(context.Background(), &alertQuery)
require.Nil(t, err2)
require.Equal(t, 1, len(alertQuery.Result))
@ -154,7 +163,7 @@ func TestIntegrationAlertingDataAccess(t *testing.T) {
modifiedItems := items
modifiedItems[0].Name = "Name"
err := sqlStore.SaveAlerts(context.Background(), testDash.Id, items)
err := store.SaveAlerts(context.Background(), testDash.Id, items)
t.Run("Can save alerts with same dashboard and panel id", func(t *testing.T) {
require.Nil(t, err)
@ -162,7 +171,7 @@ func TestIntegrationAlertingDataAccess(t *testing.T) {
t.Run("Alerts should be updated", func(t *testing.T) {
query := models.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, OrgId: 1, User: &models.SignedInUser{OrgRole: models.ROLE_ADMIN}}
err2 := sqlStore.HandleAlertsQuery(context.Background(), &query)
err2 := store.HandleAlertsQuery(context.Background(), &query)
require.Nil(t, err2)
require.Equal(t, 1, len(query.Result))
@ -174,7 +183,7 @@ func TestIntegrationAlertingDataAccess(t *testing.T) {
})
t.Run("Updates without changes should be ignored", func(t *testing.T) {
err3 := sqlStore.SaveAlerts(context.Background(), testDash.Id, items)
err3 := store.SaveAlerts(context.Background(), testDash.Id, items)
require.Nil(t, err3)
})
})
@ -205,13 +214,13 @@ func TestIntegrationAlertingDataAccess(t *testing.T) {
},
}
err := sqlStore.SaveAlerts(context.Background(), testDash.Id, multipleItems)
err := store.SaveAlerts(context.Background(), testDash.Id, multipleItems)
t.Run("Should save 3 dashboards", func(t *testing.T) {
require.Nil(t, err)
queryForDashboard := models.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, OrgId: 1, User: &models.SignedInUser{OrgRole: models.ROLE_ADMIN}}
err2 := sqlStore.HandleAlertsQuery(context.Background(), &queryForDashboard)
err2 := store.HandleAlertsQuery(context.Background(), &queryForDashboard)
require.Nil(t, err2)
require.Equal(t, 3, len(queryForDashboard.Result))
@ -220,11 +229,11 @@ func TestIntegrationAlertingDataAccess(t *testing.T) {
t.Run("should updated two dashboards and delete one", func(t *testing.T) {
missingOneAlert := multipleItems[:2]
err = sqlStore.SaveAlerts(context.Background(), testDash.Id, missingOneAlert)
err = store.SaveAlerts(context.Background(), testDash.Id, missingOneAlert)
t.Run("should delete the missing alert", func(t *testing.T) {
query := models.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, OrgId: 1, User: &models.SignedInUser{OrgRole: models.ROLE_ADMIN}}
err2 := sqlStore.HandleAlertsQuery(context.Background(), &query)
err2 := store.HandleAlertsQuery(context.Background(), &query)
require.Nil(t, err2)
require.Equal(t, 2, len(query.Result))
})
@ -242,10 +251,10 @@ func TestIntegrationAlertingDataAccess(t *testing.T) {
},
}
err := sqlStore.SaveAlerts(context.Background(), testDash.Id, items)
err := store.SaveAlerts(context.Background(), testDash.Id, items)
require.Nil(t, err)
err = sqlStore.WithDbSession(context.Background(), func(sess *DBSession) error {
err = store.db.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
dash := models.Dashboard{Id: testDash.Id, OrgId: 1}
_, err := sess.Delete(dash)
return err
@ -254,7 +263,7 @@ func TestIntegrationAlertingDataAccess(t *testing.T) {
t.Run("Alerts should be removed", func(t *testing.T) {
query := models.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, OrgId: 1, User: &models.SignedInUser{OrgRole: models.ROLE_ADMIN}}
err2 := sqlStore.HandleAlertsQuery(context.Background(), &query)
err2 := store.HandleAlertsQuery(context.Background(), &query)
require.Nil(t, err2)
require.Equal(t, 0, len(query.Result))
@ -270,9 +279,9 @@ func TestIntegrationPausingAlerts(t *testing.T) {
defer resetTimeNow()
t.Run("Given an alert", func(t *testing.T) {
sqlStore := InitTestDB(t)
sqlStore := sqlStore{db: sqlstore.InitTestDB(t), log: log.New()}
testDash := insertTestDashboard(t, sqlStore, "dashboard with alerts", 1, 0, false, "alert")
testDash := insertTestDashboard(t, sqlStore.db, "dashboard with alerts", 1, 0, false, "alert")
alert, err := insertTestAlert("Alerting title", "Alerting message", testDash.OrgId, testDash.Id, simplejson.New(), sqlStore)
require.Nil(t, err)
@ -291,7 +300,7 @@ func TestIntegrationPausingAlerts(t *testing.T) {
require.Nil(t, err)
t.Run("the NewStateDate should be updated", func(t *testing.T) {
alert, err := getAlertById(t, insertedAlert.Id, sqlStore)
alert, err := getAlertById(t, insertedAlert.Id, &sqlStore)
require.Nil(t, err)
stateDateAfterPause = alert.NewStateDate
@ -304,7 +313,7 @@ func TestIntegrationPausingAlerts(t *testing.T) {
require.Nil(t, err)
t.Run("the NewStateDate should be updated again", func(t *testing.T) {
alert, err := getAlertById(t, insertedAlert.Id, sqlStore)
alert, err := getAlertById(t, insertedAlert.Id, &sqlStore)
require.Nil(t, err)
stateDateAfterUnpause := alert.NewStateDate
@ -313,7 +322,8 @@ func TestIntegrationPausingAlerts(t *testing.T) {
})
})
}
func (ss *SQLStore) pauseAlert(t *testing.T, orgId int64, alertId int64, pauseState bool) (int64, error) {
func (ss *sqlStore) pauseAlert(t *testing.T, orgId int64, alertId int64, pauseState bool) (int64, error) {
cmd := &models.PauseAlertCommand{
OrgId: orgId,
AlertIds: []int64{alertId},
@ -323,7 +333,8 @@ func (ss *SQLStore) pauseAlert(t *testing.T, orgId int64, alertId int64, pauseSt
require.Nil(t, err)
return cmd.ResultCount, err
}
func insertTestAlert(title string, message string, orgId int64, dashId int64, settings *simplejson.Json, ss *SQLStore) (*models.Alert, error) {
func insertTestAlert(title string, message string, orgId int64, dashId int64, settings *simplejson.Json, ss sqlStore) (*models.Alert, error) {
items := []*models.Alert{
{
PanelId: 1,
@ -340,7 +351,7 @@ func insertTestAlert(title string, message string, orgId int64, dashId int64, se
return items[0], err
}
func getAlertById(t *testing.T, id int64, ss *SQLStore) (*models.Alert, error) {
func getAlertById(t *testing.T, id int64, ss *sqlStore) (*models.Alert, error) {
q := &models.GetAlertByIdQuery{
Id: id,
}
@ -349,7 +360,7 @@ func getAlertById(t *testing.T, id int64, ss *SQLStore) (*models.Alert, error) {
return q.Result, err
}
func (ss *SQLStore) pauseAllAlerts(t *testing.T, pauseState bool) error {
func (ss *sqlStore) pauseAllAlerts(t *testing.T, pauseState bool) error {
cmd := &models.PauseAllAlertCommand{
Paused: pauseState,
}
@ -357,3 +368,59 @@ func (ss *SQLStore) pauseAllAlerts(t *testing.T, pauseState bool) error {
require.Nil(t, err)
return err
}
func insertTestDashboard(t *testing.T, db db.DB, title string, orgId int64,
folderId int64, isFolder bool, tags ...interface{}) *models.Dashboard {
t.Helper()
cmd := models.SaveDashboardCommand{
OrgId: orgId,
FolderId: folderId,
IsFolder: isFolder,
Dashboard: simplejson.NewFromAny(map[string]interface{}{
"id": nil,
"title": title,
"tags": tags,
}),
}
var dash *models.Dashboard
err := db.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
dash = cmd.GetDashboardModel()
dash.SetVersion(1)
dash.Created = time.Now()
dash.Updated = time.Now()
dash.Uid = util.GenerateShortUID()
_, err := sess.Insert(dash)
return err
})
require.NoError(t, err)
require.NotNil(t, dash)
dash.Data.Set("id", dash.Id)
dash.Data.Set("uid", dash.Uid)
err = db.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
dashVersion := &dashver.DashboardVersion{
DashboardID: dash.Id,
ParentVersion: dash.Version,
RestoredFrom: cmd.RestoredFrom,
Version: dash.Version,
Created: time.Now(),
CreatedBy: dash.UpdatedBy,
Message: cmd.Message,
Data: dash.Data,
}
require.NoError(t, err)
if affectedRows, err := sess.Insert(dashVersion); err != nil {
return err
} else if affectedRows == 0 {
return dashboards.ErrDashboardNotFound
}
return nil
})
require.NoError(t, err)
return dash
}

@ -57,7 +57,7 @@ func createTestEvalContext(cmd *NotificationTestCommand) *EvalContext {
ID: rand.Int63(),
}
ctx := NewEvalContext(context.Background(), testRule, fakeRequestValidator{}, nil, nil)
ctx := NewEvalContext(context.Background(), testRule, fakeRequestValidator{}, nil, nil, nil)
if cmd.Settings.Get("uploadImage").MustBool(true) {
ctx.ImagePublicURL = "https://grafana.com/assets/img/blog/mixed_styles.png"
}

@ -25,14 +25,14 @@ func (e *AlertEngine) AlertTest(orgID int64, dashboard *simplejson.Json, panelID
if alert.PanelId != panelID {
continue
}
rule, err := NewRuleFromDBAlert(context.Background(), e.sqlStore, alert, true)
rule, err := NewRuleFromDBAlert(context.Background(), e.AlertStore, alert, true)
if err != nil {
return nil, err
}
handler := NewEvalHandler(e.DataService)
context := NewEvalContext(context.Background(), rule, fakeRequestValidator{}, e.sqlStore, nil)
context := NewEvalContext(context.Background(), rule, fakeRequestValidator{}, e.AlertStore, nil, e.datasourceService)
context.IsTestRun = true
context.IsDebug = true

@ -11,6 +11,7 @@ import (
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/services/alerting/notifiers"
encryptionservice "github.com/grafana/grafana/pkg/services/encryption/service"
"github.com/grafana/grafana/pkg/services/notifications"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/stretchr/testify/require"
@ -31,6 +32,7 @@ var (
func TestNotificationAsConfig(t *testing.T) {
var sqlStore *sqlstore.SQLStore
var ns *alerting.AlertNotificationService
logger := log.New("fake.log")
encryptionService := encryptionservice.SetupTestService(t)
@ -38,6 +40,8 @@ func TestNotificationAsConfig(t *testing.T) {
t.Run("Testing notification as configuration", func(t *testing.T) {
setup := func() {
sqlStore = sqlstore.InitTestDB(t)
nm := &notifications.NotificationService{}
ns = alerting.ProvideService(sqlStore, encryptionService, nm)
for i := 1; i < 5; i++ {
orgCommand := models.CreateOrgCommand{Name: fmt.Sprintf("Main Org. %v", i)}
@ -158,11 +162,11 @@ func TestNotificationAsConfig(t *testing.T) {
Uid: "notifier1",
Type: "slack",
}
err := sqlStore.CreateAlertNotificationCommand(context.Background(), &existingNotificationCmd)
err := ns.SQLStore.CreateAlertNotificationCommand(context.Background(), &existingNotificationCmd)
require.NoError(t, err)
require.NotNil(t, existingNotificationCmd.Result)
notificationsQuery := models.GetAllAlertNotificationsQuery{OrgId: 1}
err = sqlStore.GetAllAlertNotifications(context.Background(), &notificationsQuery)
err = ns.SQLStore.GetAllAlertNotifications(context.Background(), &notificationsQuery)
require.NoError(t, err)
require.NotNil(t, notificationsQuery.Result)
require.Equal(t, len(notificationsQuery.Result), 1)
@ -175,6 +179,7 @@ func TestNotificationAsConfig(t *testing.T) {
}
})
})
t.Run("Two notifications with is_default", func(t *testing.T) {
setup()
dc := newNotificationProvisioner(sqlStore, &fakeAlertNotification{}, encryptionService, nil, logger)
@ -194,7 +199,7 @@ func TestNotificationAsConfig(t *testing.T) {
Uid: "notifier0",
Type: "slack",
}
err := sqlStore.CreateAlertNotificationCommand(context.Background(), &existingNotificationCmd)
err := ns.SQLStore.CreateAlertNotificationCommand(context.Background(), &existingNotificationCmd)
require.NoError(t, err)
existingNotificationCmd = models.CreateAlertNotificationCommand{
Name: "channel3",
@ -202,11 +207,11 @@ func TestNotificationAsConfig(t *testing.T) {
Uid: "notifier3",
Type: "slack",
}
err = sqlStore.CreateAlertNotificationCommand(context.Background(), &existingNotificationCmd)
err = ns.SQLStore.CreateAlertNotificationCommand(context.Background(), &existingNotificationCmd)
require.NoError(t, err)
notificationsQuery := models.GetAllAlertNotificationsQuery{OrgId: 1}
err = sqlStore.GetAllAlertNotifications(context.Background(), &notificationsQuery)
err = ns.GetAllAlertNotifications(context.Background(), &notificationsQuery)
require.NoError(t, err)
require.NotNil(t, notificationsQuery.Result)
require.Equal(t, len(notificationsQuery.Result), 2)
@ -223,6 +228,7 @@ func TestNotificationAsConfig(t *testing.T) {
t.Run("Can read correct properties with orgName instead of orgId", func(t *testing.T) {
setup()
existingOrg1 := models.GetOrgByNameQuery{Name: "Main Org. 1"}
err := sqlStore.GetOrgByNameHandler(context.Background(), &existingOrg1)
require.NoError(t, err)
@ -238,7 +244,7 @@ func TestNotificationAsConfig(t *testing.T) {
Uid: "notifier2",
Type: "slack",
}
err = sqlStore.CreateAlertNotificationCommand(context.Background(), &existingNotificationCmd)
err = ns.SQLStore.CreateAlertNotificationCommand(context.Background(), &existingNotificationCmd)
require.NoError(t, err)
dc := newNotificationProvisioner(sqlStore, &fakeAlertNotification{}, encryptionService, nil, logger)
@ -270,7 +276,7 @@ func TestNotificationAsConfig(t *testing.T) {
t.Fatalf("applyChanges return an error %v", err)
}
notificationsQuery := models.GetAllAlertNotificationsQuery{OrgId: 1}
err = sqlStore.GetAllAlertNotifications(context.Background(), &notificationsQuery)
err = ns.GetAllAlertNotifications(context.Background(), &notificationsQuery)
require.NoError(t, err)
require.Empty(t, notificationsQuery.Result)
})

@ -10,5 +10,6 @@ import (
type DB interface {
WithTransactionalDbSession(ctx context.Context, callback sqlstore.DBTransactionFunc) error
WithDbSession(ctx context.Context, callback sqlstore.DBTransactionFunc) error
NewSession(ctx context.Context) *sqlstore.DBSession
GetDialect() migrator.Dialect
}

@ -2,6 +2,7 @@ package sqlstore
import (
"context"
"time"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/datasources"
@ -9,6 +10,8 @@ import (
"github.com/grafana/grafana/pkg/services/user"
)
var timeNow = time.Now
type Store interface {
GetAdminStats(ctx context.Context, query *models.GetAdminStatsQuery) error
GetAlertNotifiersUsageStats(ctx context.Context, query *models.GetAlertNotifierUsageStatsQuery) error
@ -84,13 +87,6 @@ type Store interface {
SearchPlaylists(ctx context.Context, query *models.GetPlaylistsQuery) error
// deprecated
GetPlaylistItem(ctx context.Context, query *models.GetPlaylistItemsByUidQuery) error
GetAlertById(ctx context.Context, query *models.GetAlertByIdQuery) error
GetAllAlertQueryHandler(ctx context.Context, query *models.GetAllAlertsQuery) error
HandleAlertsQuery(ctx context.Context, query *models.GetAlertsQuery) error
SetAlertState(ctx context.Context, cmd *models.SetAlertStateCommand) error
PauseAlert(ctx context.Context, cmd *models.PauseAlertCommand) error
PauseAllAlerts(ctx context.Context, cmd *models.PauseAllAlertCommand) error
GetAlertStatesForDashboard(ctx context.Context, query *models.GetAlertStatesForDashboardQuery) error
AddOrgUser(ctx context.Context, cmd *models.AddOrgUserCommand) error
UpdateOrgUser(ctx context.Context, cmd *models.UpdateOrgUserCommand) error
GetOrgUsers(ctx context.Context, query *models.GetOrgUsersQuery) error
@ -107,19 +103,6 @@ type Store interface {
Sync() error
Reset() error
Quote(value string) string
DeleteAlertNotification(ctx context.Context, cmd *models.DeleteAlertNotificationCommand) error
DeleteAlertNotificationWithUid(ctx context.Context, cmd *models.DeleteAlertNotificationWithUidCommand) error
GetAlertNotifications(ctx context.Context, query *models.GetAlertNotificationsQuery) error
GetAlertNotificationUidWithId(ctx context.Context, query *models.GetAlertNotificationUidQuery) error
GetAlertNotificationsWithUid(ctx context.Context, query *models.GetAlertNotificationsWithUidQuery) error
GetAllAlertNotifications(ctx context.Context, query *models.GetAllAlertNotificationsQuery) error
GetAlertNotificationsWithUidToSend(ctx context.Context, query *models.GetAlertNotificationsWithUidToSendQuery) error
CreateAlertNotificationCommand(ctx context.Context, cmd *models.CreateAlertNotificationCommand) error
UpdateAlertNotification(ctx context.Context, cmd *models.UpdateAlertNotificationCommand) error
UpdateAlertNotificationWithUid(ctx context.Context, cmd *models.UpdateAlertNotificationWithUidCommand) error
SetAlertNotificationStateToCompleteCommand(ctx context.Context, cmd *models.SetAlertNotificationStateToCompleteCommand) error
SetAlertNotificationStateToPendingCommand(ctx context.Context, cmd *models.SetAlertNotificationStateToPendingCommand) error
GetOrCreateAlertNotificationState(ctx context.Context, cmd *models.GetOrCreateNotificationStateQuery) error
UpdateTempUserStatus(ctx context.Context, cmd *models.UpdateTempUserStatusCommand) error
CreateTempUser(ctx context.Context, cmd *models.CreateTempUserCommand) error
UpdateTempUserWithEmailSent(ctx context.Context, cmd *models.UpdateTempUserWithEmailSentCommand) error

Loading…
Cancel
Save