mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Dashboard Alert Extractor: Create service for dashboard extractor and remove bus (#45518)
* Create DashAlertService service * Remove no used dashboard service from plugin's manager that generates dependency cycle in Enterprise * Remove bus for dashboard permissions * Remove bus from dashboard extractor service * Add missing argument * Fix wire * Fix lint * More goimports * Use datasource service instead sql calls * Fix integration test
This commit is contained in:
parent
1df040eb23
commit
2c90dcf3c0
@ -364,7 +364,7 @@ func setupHTTPServerWithCfg(t *testing.T, useFakeAccessControl, enableAccessCont
|
||||
RouteRegister: routeRegister,
|
||||
SQLStore: db,
|
||||
searchUsersService: searchusers.ProvideUsersService(bus, filters.ProvideOSSSearchUserFilter()),
|
||||
dashboardService: dashboardservice.ProvideDashboardService(dashboardsStore),
|
||||
dashboardService: dashboardservice.ProvideDashboardService(dashboardsStore, nil),
|
||||
}
|
||||
|
||||
// Defining the accesscontrol service has to be done before registering routes
|
||||
|
@ -29,7 +29,7 @@ func TestDashboardPermissionAPIEndpoint(t *testing.T) {
|
||||
|
||||
hs := &HTTPServer{
|
||||
Cfg: settings,
|
||||
dashboardService: dashboardservice.ProvideDashboardService(dashboardStore),
|
||||
dashboardService: dashboardservice.ProvideDashboardService(dashboardStore, nil),
|
||||
SQLStore: mockSQLStore,
|
||||
}
|
||||
|
||||
|
@ -219,7 +219,7 @@ func TestDashboardAPIEndpoint(t *testing.T) {
|
||||
Live: newTestLive(t),
|
||||
LibraryPanelService: &mockLibraryPanelService{},
|
||||
LibraryElementService: &mockLibraryElementService{},
|
||||
dashboardService: service.ProvideDashboardService(dashboardStore),
|
||||
dashboardService: service.ProvideDashboardService(dashboardStore, nil),
|
||||
SQLStore: mockSQLStore,
|
||||
}
|
||||
hs.SQLStore = mockSQLStore
|
||||
@ -939,7 +939,7 @@ func getDashboardShouldReturn200WithConfig(t *testing.T, sc *scenarioContext, pr
|
||||
LibraryPanelService: &libraryPanelsService,
|
||||
LibraryElementService: &libraryElementsService,
|
||||
ProvisioningService: provisioningService,
|
||||
dashboardProvisioningService: service.ProvideDashboardService(dashboardStore),
|
||||
dashboardProvisioningService: service.ProvideDashboardService(dashboardStore, nil),
|
||||
SQLStore: sc.sqlStore,
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/datasources/permissions"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -29,7 +30,7 @@ const (
|
||||
|
||||
func TestDataSourcesProxy_userLoggedIn(t *testing.T) {
|
||||
mockSQLStore := mockstore.NewSQLStoreMock()
|
||||
mockDatasourcePermissionService := newMockDatasourcePermissionService()
|
||||
mockDatasourcePermissionService := permissions.NewMockDatasourcePermissionService()
|
||||
loggedInUserScenario(t, "When calling GET on", "/api/datasources/", "/api/datasources/", func(sc *scenarioContext) {
|
||||
// Stubs the database query
|
||||
ds := []*models.DataSource{
|
||||
@ -38,7 +39,7 @@ func TestDataSourcesProxy_userLoggedIn(t *testing.T) {
|
||||
{Name: "BBB"},
|
||||
{Name: "aaa"},
|
||||
}
|
||||
mockDatasourcePermissionService.dsResult = ds
|
||||
mockDatasourcePermissionService.DsResult = ds
|
||||
|
||||
// handler func being tested
|
||||
hs := &HTTPServer{
|
||||
@ -209,8 +210,8 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
dsServiceMock := &dataSourcesServiceMock{
|
||||
expectedDatasource: &testDatasource,
|
||||
}
|
||||
dsPermissionService := newMockDatasourcePermissionService()
|
||||
dsPermissionService.dsResult = []*models.DataSource{
|
||||
dsPermissionService := permissions.NewMockDatasourcePermissionService()
|
||||
dsPermissionService.DsResult = []*models.DataSource{
|
||||
&testDatasource,
|
||||
}
|
||||
|
||||
@ -505,9 +506,9 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
// mock sqlStore and datasource permission service
|
||||
dsServiceMock.expectedError = test.expectedSQLError
|
||||
dsServiceMock.expectedDatasource = test.expectedDS
|
||||
dsPermissionService.dsResult = []*models.DataSource{test.expectedDS}
|
||||
dsPermissionService.DsResult = []*models.DataSource{test.expectedDS}
|
||||
if test.expectedDS == nil {
|
||||
dsPermissionService.dsResult = nil
|
||||
dsPermissionService.DsResult = nil
|
||||
}
|
||||
hs.DataSourcesService = dsServiceMock
|
||||
hs.DatasourcePermissionsService = dsPermissionService
|
||||
|
@ -30,7 +30,7 @@ func TestFolderPermissionAPIEndpoint(t *testing.T) {
|
||||
dashboardStore := &database.FakeDashboardStore{}
|
||||
defer dashboardStore.AssertExpectations(t)
|
||||
|
||||
hs := &HTTPServer{Cfg: settings, folderService: folderService, dashboardService: service.ProvideDashboardService(dashboardStore)}
|
||||
hs := &HTTPServer{Cfg: settings, folderService: folderService, dashboardService: service.ProvideDashboardService(dashboardStore, nil)}
|
||||
|
||||
t.Run("Given folder not exists", func(t *testing.T) {
|
||||
folderService.On("GetFolderByUID", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, models.ErrFolderNotFound).Twice()
|
||||
|
@ -36,6 +36,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/dashboardsnapshots"
|
||||
"github.com/grafana/grafana/pkg/services/datasourceproxy"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/datasources/permissions"
|
||||
"github.com/grafana/grafana/pkg/services/encryption"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/hooks"
|
||||
@ -135,7 +136,7 @@ type HTTPServer struct {
|
||||
dashboardService dashboards.DashboardService
|
||||
dashboardProvisioningService dashboards.DashboardProvisioningService
|
||||
folderService dashboards.FolderService
|
||||
DatasourcePermissionsService DatasourcePermissionsService
|
||||
DatasourcePermissionsService permissions.DatasourcePermissionsService
|
||||
commentsService *comments.Service
|
||||
AlertNotificationService *alerting.AlertNotificationService
|
||||
DashboardsnapshotsService *dashboardsnapshots.Service
|
||||
@ -169,7 +170,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
||||
authInfoService login.AuthInfoService, permissionsServices accesscontrol.PermissionsServices,
|
||||
notificationService *notifications.NotificationService, dashboardService dashboards.DashboardService,
|
||||
dashboardProvisioningService dashboards.DashboardProvisioningService, folderService dashboards.FolderService,
|
||||
datasourcePermissionsService DatasourcePermissionsService, alertNotificationService *alerting.AlertNotificationService,
|
||||
datasourcePermissionsService permissions.DatasourcePermissionsService, alertNotificationService *alerting.AlertNotificationService,
|
||||
dashboardsnapshotsService *dashboardsnapshots.Service, commentsService *comments.Service, pluginSettings *pluginsettings.ServiceImpl,
|
||||
) (*HTTPServer, error) {
|
||||
web.Env = cfg.Env
|
||||
|
@ -11,9 +11,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/provider"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/loader"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/signature"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards/database"
|
||||
service "github.com/grafana/grafana/pkg/services/dashboards/manager"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@ -27,9 +24,8 @@ func TestGetPluginDashboards(t *testing.T) {
|
||||
},
|
||||
}
|
||||
pmCfg := plugins.FromGrafanaCfg(cfg)
|
||||
dashboardService := service.ProvideDashboardService(database.ProvideDashboardStore(&sqlstore.SQLStore{}))
|
||||
pm, err := ProvideService(cfg, loader.New(pmCfg, nil,
|
||||
signature.NewUnsignedAuthorizer(pmCfg), &provider.Service{}), dashboardService)
|
||||
signature.NewUnsignedAuthorizer(pmCfg), &provider.Service{}))
|
||||
require.NoError(t, err)
|
||||
|
||||
bus.AddHandler("test", func(ctx context.Context, query *models.GetDashboardQuery) error {
|
||||
|
@ -14,7 +14,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/instrumentation"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/installer"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util/errutil"
|
||||
)
|
||||
@ -30,38 +29,35 @@ var _ plugins.StaticRouteResolver = (*PluginManager)(nil)
|
||||
var _ plugins.RendererManager = (*PluginManager)(nil)
|
||||
|
||||
type PluginManager struct {
|
||||
cfg *plugins.Cfg
|
||||
store map[string]*plugins.Plugin
|
||||
pluginInstaller plugins.Installer
|
||||
pluginLoader plugins.Loader
|
||||
pluginsMu sync.RWMutex
|
||||
pluginPaths map[plugins.Class][]string
|
||||
dashboardService dashboards.DashboardService
|
||||
log log.Logger
|
||||
cfg *plugins.Cfg
|
||||
store map[string]*plugins.Plugin
|
||||
pluginInstaller plugins.Installer
|
||||
pluginLoader plugins.Loader
|
||||
pluginsMu sync.RWMutex
|
||||
pluginPaths map[plugins.Class][]string
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func ProvideService(grafanaCfg *setting.Cfg, pluginLoader plugins.Loader, dashboardService dashboards.DashboardService) (*PluginManager, error) {
|
||||
func ProvideService(grafanaCfg *setting.Cfg, pluginLoader plugins.Loader) (*PluginManager, error) {
|
||||
pm := New(plugins.FromGrafanaCfg(grafanaCfg), map[plugins.Class][]string{
|
||||
plugins.Core: corePluginPaths(grafanaCfg),
|
||||
plugins.Bundled: {grafanaCfg.BundledPluginsPath},
|
||||
plugins.External: append([]string{grafanaCfg.PluginsPath}, pluginSettingPaths(grafanaCfg)...),
|
||||
}, pluginLoader, dashboardService)
|
||||
}, pluginLoader)
|
||||
if err := pm.Init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pm, nil
|
||||
}
|
||||
|
||||
func New(cfg *plugins.Cfg, pluginPaths map[plugins.Class][]string, pluginLoader plugins.Loader,
|
||||
dashboardService dashboards.DashboardService) *PluginManager {
|
||||
func New(cfg *plugins.Cfg, pluginPaths map[plugins.Class][]string, pluginLoader plugins.Loader) *PluginManager {
|
||||
return &PluginManager{
|
||||
cfg: cfg,
|
||||
pluginLoader: pluginLoader,
|
||||
pluginPaths: pluginPaths,
|
||||
store: make(map[string]*plugins.Plugin),
|
||||
log: log.New("plugin.manager"),
|
||||
pluginInstaller: installer.New(false, cfg.BuildVersion, newInstallerLogger("plugin.installer", true)),
|
||||
dashboardService: dashboardService,
|
||||
cfg: cfg,
|
||||
pluginLoader: pluginLoader,
|
||||
pluginPaths: pluginPaths,
|
||||
store: make(map[string]*plugins.Plugin),
|
||||
log: log.New("plugin.manager"),
|
||||
pluginInstaller: installer.New(false, cfg.BuildVersion, newInstallerLogger("plugin.installer", true)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/provider"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/loader"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/signature"
|
||||
service "github.com/grafana/grafana/pkg/services/dashboards/manager"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/licensing"
|
||||
"github.com/grafana/grafana/pkg/services/searchV2"
|
||||
@ -94,7 +93,7 @@ func TestPluginManager_int_init(t *testing.T) {
|
||||
|
||||
pmCfg := plugins.FromGrafanaCfg(cfg)
|
||||
pm, err := ProvideService(cfg, loader.New(pmCfg, license, signature.NewUnsignedAuthorizer(pmCfg),
|
||||
provider.ProvideService(coreRegistry)), &service.DashboardServiceImpl{})
|
||||
provider.ProvideService(coreRegistry)))
|
||||
require.NoError(t, err)
|
||||
|
||||
verifyCorePluginCatalogue(t, pm)
|
||||
|
@ -12,9 +12,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards/database"
|
||||
service "github.com/grafana/grafana/pkg/services/dashboards/manager"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@ -469,8 +466,7 @@ func TestPluginManager_lifecycle_unmanaged(t *testing.T) {
|
||||
func createManager(t *testing.T, cbs ...func(*PluginManager)) *PluginManager {
|
||||
t.Helper()
|
||||
|
||||
dashboardService := service.ProvideDashboardService(database.ProvideDashboardStore(&sqlstore.SQLStore{}))
|
||||
pm := New(&plugins.Cfg{}, nil, &fakeLoader{}, dashboardService)
|
||||
pm := New(&plugins.Cfg{}, nil, &fakeLoader{})
|
||||
|
||||
for _, cb := range cbs {
|
||||
cb(pm)
|
||||
@ -524,8 +520,7 @@ func newScenario(t *testing.T, managed bool, fn func(t *testing.T, ctx *managerS
|
||||
cfg.Azure.ManagedIdentityClientId = "client-id"
|
||||
|
||||
loader := &fakeLoader{}
|
||||
dashboardService := service.ProvideDashboardService(database.ProvideDashboardStore(&sqlstore.SQLStore{}))
|
||||
manager := New(cfg, nil, loader, dashboardService)
|
||||
manager := New(cfg, nil, loader)
|
||||
manager.pluginLoader = loader
|
||||
ctx := &managerScenarioCtx{
|
||||
manager: manager,
|
||||
|
@ -213,6 +213,8 @@ var wireBasicSet = wire.NewSet(
|
||||
dashboardimportservice.ProvideService,
|
||||
wire.Bind(new(dashboardimport.Service), new(*dashboardimportservice.ImportDashboardService)),
|
||||
plugindashboards.ProvideService,
|
||||
alerting.ProvideDashAlertExtractorService,
|
||||
wire.Bind(new(alerting.DashAlertExtractor), new(*alerting.DashAlertExtractorService)),
|
||||
comments.ProvideService,
|
||||
)
|
||||
|
||||
|
@ -5,7 +5,6 @@ package server
|
||||
|
||||
import (
|
||||
"github.com/google/wire"
|
||||
"github.com/grafana/grafana/pkg/api"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/provider"
|
||||
@ -18,6 +17,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions"
|
||||
"github.com/grafana/grafana/pkg/services/auth"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/datasources/permissions"
|
||||
datasourceservice "github.com/grafana/grafana/pkg/services/datasources/service"
|
||||
"github.com/grafana/grafana/pkg/services/encryption"
|
||||
"github.com/grafana/grafana/pkg/services/encryption/ossencryption"
|
||||
@ -75,8 +75,8 @@ var wireExtsBasicSet = wire.NewSet(
|
||||
wire.Bind(new(kmsproviders.Service), new(osskmsproviders.Service)),
|
||||
ldap.ProvideGroupsService,
|
||||
wire.Bind(new(ldap.Groups), new(*ldap.OSSGroups)),
|
||||
api.ProvideDatasourcePermissionsService,
|
||||
wire.Bind(new(api.DatasourcePermissionsService), new(*api.OSSDatasourcePermissionsService)),
|
||||
permissions.ProvideDatasourcePermissionsService,
|
||||
wire.Bind(new(permissions.DatasourcePermissionsService), new(*permissions.OSSDatasourcePermissionsService)),
|
||||
ossaccesscontrol.ProvidePermissionsServices,
|
||||
wire.Bind(new(accesscontrol.PermissionsServices), new(*ossaccesscontrol.PermissionsService)),
|
||||
)
|
||||
|
@ -45,16 +45,17 @@ type AlertEngine struct {
|
||||
DataService legacydata.RequestHandler
|
||||
Cfg *setting.Cfg
|
||||
|
||||
execQueue chan *Job
|
||||
ticker *Ticker
|
||||
scheduler scheduler
|
||||
evalHandler evalHandler
|
||||
ruleReader ruleReader
|
||||
log log.Logger
|
||||
resultHandler resultHandler
|
||||
usageStatsService usagestats.Service
|
||||
tracer tracing.Tracer
|
||||
sqlStore AlertStore
|
||||
execQueue chan *Job
|
||||
ticker *Ticker
|
||||
scheduler scheduler
|
||||
evalHandler evalHandler
|
||||
ruleReader ruleReader
|
||||
log log.Logger
|
||||
resultHandler resultHandler
|
||||
usageStatsService usagestats.Service
|
||||
tracer tracing.Tracer
|
||||
sqlStore AlertStore
|
||||
dashAlertExtractor DashAlertExtractor
|
||||
}
|
||||
|
||||
// IsDisabled returns true if the alerting service is disabled for this instance.
|
||||
@ -65,16 +66,18 @@ func (e *AlertEngine) IsDisabled() bool {
|
||||
// ProvideAlertEngine returns a new AlertEngine.
|
||||
func ProvideAlertEngine(renderer rendering.Service, bus bus.Bus, requestValidator models.PluginRequestValidator,
|
||||
dataService legacydata.RequestHandler, usageStatsService usagestats.Service, encryptionService encryption.Internal,
|
||||
notificationService *notifications.NotificationService, tracer tracing.Tracer, sqlStore AlertStore, cfg *setting.Cfg) *AlertEngine {
|
||||
notificationService *notifications.NotificationService, tracer tracing.Tracer, sqlStore AlertStore, cfg *setting.Cfg,
|
||||
dashAlertExtractor DashAlertExtractor) *AlertEngine {
|
||||
e := &AlertEngine{
|
||||
Cfg: cfg,
|
||||
RenderService: renderer,
|
||||
Bus: bus,
|
||||
RequestValidator: requestValidator,
|
||||
DataService: dataService,
|
||||
usageStatsService: usageStatsService,
|
||||
tracer: tracer,
|
||||
sqlStore: sqlStore,
|
||||
Cfg: cfg,
|
||||
RenderService: renderer,
|
||||
Bus: bus,
|
||||
RequestValidator: requestValidator,
|
||||
DataService: dataService,
|
||||
usageStatsService: usageStatsService,
|
||||
tracer: tracer,
|
||||
sqlStore: sqlStore,
|
||||
dashAlertExtractor: dashAlertExtractor,
|
||||
}
|
||||
e.ticker = NewTicker(time.Now(), time.Second*0, clock.New(), 1)
|
||||
e.execQueue = make(chan *Job, 1000)
|
||||
|
@ -24,7 +24,7 @@ func TestEngineTimeouts(t *testing.T) {
|
||||
usMock := &usagestats.UsageStatsMock{T: t}
|
||||
tracer, err := tracing.InitializeTracerForTest()
|
||||
require.NoError(t, err)
|
||||
engine := ProvideAlertEngine(nil, nil, nil, nil, usMock, ossencryption.ProvideService(), nil, tracer, nil, setting.NewCfg())
|
||||
engine := ProvideAlertEngine(nil, nil, nil, nil, usMock, ossencryption.ProvideService(), nil, tracer, nil, setting.NewCfg(), nil)
|
||||
setting.AlertingNotificationTimeout = 30 * time.Second
|
||||
setting.AlertingMaxAttempts = 3
|
||||
engine.resultHandler = &FakeResultHandler{}
|
||||
|
@ -102,7 +102,7 @@ func TestEngineProcessJob(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
store := &AlertStoreMock{}
|
||||
engine := ProvideAlertEngine(nil, bus, nil, nil, usMock, ossencryption.ProvideService(), nil, tracer, store, setting.NewCfg())
|
||||
engine := ProvideAlertEngine(nil, bus, nil, nil, usMock, ossencryption.ProvideService(), nil, tracer, store, setting.NewCfg(), nil)
|
||||
setting.AlertingEvaluationTimeout = 30 * time.Second
|
||||
setting.AlertingNotificationTimeout = 30 * time.Second
|
||||
setting.AlertingMaxAttempts = 3
|
||||
|
@ -6,31 +6,34 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"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/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/datasources/permissions"
|
||||
)
|
||||
|
||||
// DashAlertExtractor extracts alerts from the dashboard json.
|
||||
type DashAlertExtractor struct {
|
||||
User *models.SignedInUser
|
||||
Dash *models.Dashboard
|
||||
OrgID int64
|
||||
log log.Logger
|
||||
type DashAlertExtractor interface {
|
||||
GetAlerts(ctx context.Context, dashAlertInfo DashAlertInfo) ([]*models.Alert, error)
|
||||
ValidateAlerts(ctx context.Context, dashAlertInfo DashAlertInfo) error
|
||||
}
|
||||
|
||||
// NewDashAlertExtractor returns a new DashAlertExtractor.
|
||||
func NewDashAlertExtractor(dash *models.Dashboard, orgID int64, user *models.SignedInUser) *DashAlertExtractor {
|
||||
return &DashAlertExtractor{
|
||||
User: user,
|
||||
Dash: dash,
|
||||
OrgID: orgID,
|
||||
log: log.New("alerting.extractor"),
|
||||
// DashAlertExtractorService extracts alerts from the dashboard json.
|
||||
type DashAlertExtractorService struct {
|
||||
datasourcePermissionsService permissions.DatasourcePermissionsService
|
||||
datasourceService datasources.DataSourceService
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func ProvideDashAlertExtractorService(datasourcePermissionsService permissions.DatasourcePermissionsService, datasourceService datasources.DataSourceService) *DashAlertExtractorService {
|
||||
return &DashAlertExtractorService{
|
||||
datasourcePermissionsService: datasourcePermissionsService,
|
||||
datasourceService: datasourceService,
|
||||
log: log.New("alerting.extractor"),
|
||||
}
|
||||
}
|
||||
|
||||
func (e *DashAlertExtractor) lookupQueryDataSource(ctx context.Context, panel *simplejson.Json, panelQuery *simplejson.Json) (*models.DataSource, error) {
|
||||
func (e *DashAlertExtractorService) lookupQueryDataSource(ctx context.Context, panel *simplejson.Json, panelQuery *simplejson.Json, orgID int64) (*models.DataSource, error) {
|
||||
dsName := ""
|
||||
dsUid := ""
|
||||
|
||||
@ -47,15 +50,15 @@ func (e *DashAlertExtractor) lookupQueryDataSource(ctx context.Context, panel *s
|
||||
}
|
||||
|
||||
if dsName == "" && dsUid == "" {
|
||||
query := &models.GetDefaultDataSourceQuery{OrgId: e.OrgID}
|
||||
if err := bus.Dispatch(ctx, query); err != nil {
|
||||
query := &models.GetDefaultDataSourceQuery{OrgId: orgID}
|
||||
if err := e.datasourceService.GetDefaultDataSource(ctx, query); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return query.Result, nil
|
||||
}
|
||||
|
||||
query := &models.GetDataSourceQuery{Name: dsName, Uid: dsUid, OrgId: e.OrgID}
|
||||
if err := bus.Dispatch(ctx, query); err != nil {
|
||||
query := &models.GetDataSourceQuery{Name: dsName, Uid: dsUid, OrgId: orgID}
|
||||
if err := e.datasourceService.GetDataSource(ctx, query); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -101,7 +104,7 @@ func UAEnabled(ctx context.Context) bool {
|
||||
return enabled
|
||||
}
|
||||
|
||||
func (e *DashAlertExtractor) getAlertFromPanels(ctx context.Context, jsonWithPanels *simplejson.Json, validateAlertFunc func(*models.Alert) bool, logTranslationFailures bool) ([]*models.Alert, error) {
|
||||
func (e *DashAlertExtractorService) getAlertFromPanels(ctx context.Context, jsonWithPanels *simplejson.Json, validateAlertFunc func(*models.Alert) bool, logTranslationFailures bool, dashAlertInfo DashAlertInfo) ([]*models.Alert, error) {
|
||||
alerts := make([]*models.Alert, 0)
|
||||
|
||||
for _, panelObj := range jsonWithPanels.Get("panels").MustArray() {
|
||||
@ -111,7 +114,7 @@ func (e *DashAlertExtractor) getAlertFromPanels(ctx context.Context, jsonWithPan
|
||||
// check if the panel is collapsed
|
||||
if collapsed && collapsedJSON.MustBool() {
|
||||
// extract alerts from sub panels for collapsed panels
|
||||
alertSlice, err := e.getAlertFromPanels(ctx, panel, validateAlertFunc, logTranslationFailures)
|
||||
alertSlice, err := e.getAlertFromPanels(ctx, panel, validateAlertFunc, logTranslationFailures, dashAlertInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -143,8 +146,8 @@ func (e *DashAlertExtractor) getAlertFromPanels(ctx context.Context, jsonWithPan
|
||||
Err: validationErr.Err,
|
||||
PanelID: panelID,
|
||||
}
|
||||
if e.Dash != nil {
|
||||
ve.DashboardID = e.Dash.Id
|
||||
if dashAlertInfo.Dash != nil {
|
||||
ve.DashboardID = dashAlertInfo.Dash.Id
|
||||
}
|
||||
return ve
|
||||
}
|
||||
@ -170,8 +173,8 @@ func (e *DashAlertExtractor) getAlertFromPanels(ctx context.Context, jsonWithPan
|
||||
}
|
||||
|
||||
alert := &models.Alert{
|
||||
DashboardId: e.Dash.Id,
|
||||
OrgId: e.OrgID,
|
||||
DashboardId: dashAlertInfo.Dash.Id,
|
||||
OrgId: dashAlertInfo.OrgID,
|
||||
PanelId: panelID,
|
||||
Id: jsonAlert.Get("id").MustInt64(),
|
||||
Name: jsonAlert.Get("name").MustString(),
|
||||
@ -198,24 +201,21 @@ func (e *DashAlertExtractor) getAlertFromPanels(ctx context.Context, jsonWithPan
|
||||
return nil, ValidationError{Reason: reason}
|
||||
}
|
||||
|
||||
datasource, err := e.lookupQueryDataSource(ctx, panel, panelQuery)
|
||||
datasource, err := e.lookupQueryDataSource(ctx, panel, panelQuery, dashAlertInfo.OrgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dsFilterQuery := models.DatasourcesPermissionFilterQuery{
|
||||
User: e.User,
|
||||
User: dashAlertInfo.User,
|
||||
Datasources: []*models.DataSource{datasource},
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(ctx, &dsFilterQuery); err != nil {
|
||||
if !errors.Is(err, bus.ErrHandlerNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if len(dsFilterQuery.Result) == 0 {
|
||||
return nil, models.ErrDataSourceAccessDenied
|
||||
}
|
||||
if err := e.datasourcePermissionsService.FilterDatasourcesBasedOnQueryPermissions(ctx, &dsFilterQuery); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(dsFilterQuery.Result) == 0 {
|
||||
return nil, models.ErrDataSourceAccessDenied
|
||||
}
|
||||
|
||||
jsonQuery.SetPath([]string{"datasourceId"}, datasource.Id)
|
||||
@ -250,12 +250,12 @@ func validateAlertRule(alert *models.Alert) bool {
|
||||
}
|
||||
|
||||
// GetAlerts extracts alerts from the dashboard json and does full validation on the alert json data.
|
||||
func (e *DashAlertExtractor) GetAlerts(ctx context.Context) ([]*models.Alert, error) {
|
||||
return e.extractAlerts(ctx, validateAlertRule, true)
|
||||
func (e *DashAlertExtractorService) GetAlerts(ctx context.Context, dashAlertInfo DashAlertInfo) ([]*models.Alert, error) {
|
||||
return e.extractAlerts(ctx, validateAlertRule, true, dashAlertInfo)
|
||||
}
|
||||
|
||||
func (e *DashAlertExtractor) extractAlerts(ctx context.Context, validateFunc func(alert *models.Alert) bool, logTranslationFailures bool) ([]*models.Alert, error) {
|
||||
dashboardJSON, err := copyJSON(e.Dash.Data)
|
||||
func (e *DashAlertExtractorService) extractAlerts(ctx context.Context, validateFunc func(alert *models.Alert) bool, logTranslationFailures bool, dashAlertInfo DashAlertInfo) ([]*models.Alert, error) {
|
||||
dashboardJSON, err := copyJSON(dashAlertInfo.Dash.Data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -268,7 +268,7 @@ func (e *DashAlertExtractor) extractAlerts(ctx context.Context, validateFunc fun
|
||||
if len(rows) > 0 {
|
||||
for _, rowObj := range rows {
|
||||
row := simplejson.NewFromAny(rowObj)
|
||||
a, err := e.getAlertFromPanels(ctx, row, validateFunc, logTranslationFailures)
|
||||
a, err := e.getAlertFromPanels(ctx, row, validateFunc, logTranslationFailures, dashAlertInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -276,7 +276,7 @@ func (e *DashAlertExtractor) extractAlerts(ctx context.Context, validateFunc fun
|
||||
alerts = append(alerts, a...)
|
||||
}
|
||||
} else {
|
||||
a, err := e.getAlertFromPanels(ctx, dashboardJSON, validateFunc, logTranslationFailures)
|
||||
a, err := e.getAlertFromPanels(ctx, dashboardJSON, validateFunc, logTranslationFailures, dashAlertInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -290,9 +290,9 @@ func (e *DashAlertExtractor) extractAlerts(ctx context.Context, validateFunc fun
|
||||
|
||||
// ValidateAlerts validates alerts in the dashboard json but does not require a valid dashboard id
|
||||
// in the first validation pass.
|
||||
func (e *DashAlertExtractor) ValidateAlerts(ctx context.Context) error {
|
||||
func (e *DashAlertExtractorService) ValidateAlerts(ctx context.Context, dashAlertInfo DashAlertInfo) error {
|
||||
_, err := e.extractAlerts(ctx, func(alert *models.Alert) bool {
|
||||
return alert.OrgId != 0 && alert.PanelId != 0
|
||||
}, false)
|
||||
}, false, dashAlertInfo)
|
||||
return err
|
||||
}
|
||||
|
@ -6,9 +6,10 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/datasources/permissions"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@ -21,40 +22,24 @@ func TestAlertRuleExtraction(t *testing.T) {
|
||||
// mock data
|
||||
defaultDs := &models.DataSource{Id: 12, OrgId: 1, Name: "I am default", IsDefault: true, Uid: "def-uid"}
|
||||
graphite2Ds := &models.DataSource{Id: 15, OrgId: 1, Name: "graphite2", Uid: "graphite2-uid"}
|
||||
influxDBDs := &models.DataSource{Id: 16, OrgId: 1, Name: "InfluxDB", Uid: "InfluxDB-uid"}
|
||||
prom := &models.DataSource{Id: 17, OrgId: 1, Name: "Prometheus", Uid: "Prometheus-uid"}
|
||||
|
||||
bus.AddHandler("test", func(ctx context.Context, query *models.GetDefaultDataSourceQuery) error {
|
||||
query.Result = defaultDs
|
||||
return nil
|
||||
})
|
||||
|
||||
bus.AddHandler("test", func(ctx context.Context, query *models.GetDataSourceQuery) error {
|
||||
if query.Name == defaultDs.Name || query.Uid == defaultDs.Uid {
|
||||
query.Result = defaultDs
|
||||
}
|
||||
if query.Name == graphite2Ds.Name || query.Uid == graphite2Ds.Uid {
|
||||
query.Result = graphite2Ds
|
||||
}
|
||||
if query.Name == influxDBDs.Name || query.Uid == influxDBDs.Uid {
|
||||
query.Result = influxDBDs
|
||||
}
|
||||
if query.Name == prom.Name || query.Uid == prom.Uid {
|
||||
query.Result = prom
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
json, err := ioutil.ReadFile("./testdata/graphite-alert.json")
|
||||
require.Nil(t, err)
|
||||
|
||||
dsPermissions := permissions.NewMockDatasourcePermissionService()
|
||||
dsPermissions.DsResult = []*models.DataSource{
|
||||
{
|
||||
Id: 1,
|
||||
},
|
||||
}
|
||||
|
||||
dsService := &fakeDatasourceService{ExpectedDatasource: defaultDs}
|
||||
extractor := ProvideDashAlertExtractorService(dsPermissions, dsService)
|
||||
|
||||
t.Run("Parsing alert rules from dashboard json", func(t *testing.T) {
|
||||
dashJSON, err := simplejson.NewJson(json)
|
||||
require.Nil(t, err)
|
||||
|
||||
dash := models.NewDashboardFromJson(dashJSON)
|
||||
|
||||
getTarget := func(j *simplejson.Json) string {
|
||||
rowObj := j.Get("rows").MustArray()[0]
|
||||
row := simplejson.NewFromAny(rowObj)
|
||||
@ -67,8 +52,11 @@ func TestAlertRuleExtraction(t *testing.T) {
|
||||
|
||||
require.Equal(t, getTarget(dashJSON), "")
|
||||
|
||||
extractor := NewDashAlertExtractor(dash, 1, nil)
|
||||
_, _ = extractor.GetAlerts(context.Background())
|
||||
_, _ = extractor.GetAlerts(context.Background(), DashAlertInfo{
|
||||
User: nil,
|
||||
Dash: models.NewDashboardFromJson(dashJSON),
|
||||
OrgID: 1,
|
||||
})
|
||||
|
||||
require.Equal(t, getTarget(dashJSON), "")
|
||||
})
|
||||
@ -77,10 +65,12 @@ func TestAlertRuleExtraction(t *testing.T) {
|
||||
dashJSON, err := simplejson.NewJson(json)
|
||||
require.Nil(t, err)
|
||||
|
||||
dash := models.NewDashboardFromJson(dashJSON)
|
||||
extractor := NewDashAlertExtractor(dash, 1, nil)
|
||||
|
||||
alerts, err := extractor.GetAlerts(context.Background())
|
||||
dsService.ExpectedDatasource = &models.DataSource{Id: 12}
|
||||
alerts, err := extractor.GetAlerts(context.Background(), DashAlertInfo{
|
||||
User: nil,
|
||||
Dash: models.NewDashboardFromJson(dashJSON),
|
||||
OrgID: 1,
|
||||
})
|
||||
|
||||
require.Nil(t, err)
|
||||
|
||||
@ -127,10 +117,12 @@ func TestAlertRuleExtraction(t *testing.T) {
|
||||
|
||||
dashJSON, err := simplejson.NewJson(panelWithoutID)
|
||||
require.Nil(t, err)
|
||||
dash := models.NewDashboardFromJson(dashJSON)
|
||||
extractor := NewDashAlertExtractor(dash, 1, nil)
|
||||
|
||||
_, err = extractor.GetAlerts(context.Background())
|
||||
_, err = extractor.GetAlerts(context.Background(), DashAlertInfo{
|
||||
User: nil,
|
||||
Dash: models.NewDashboardFromJson(dashJSON),
|
||||
OrgID: 1,
|
||||
})
|
||||
|
||||
require.NotNil(t, err)
|
||||
})
|
||||
@ -141,10 +133,12 @@ func TestAlertRuleExtraction(t *testing.T) {
|
||||
|
||||
dashJSON, err := simplejson.NewJson(panelWithIDZero)
|
||||
require.Nil(t, err)
|
||||
dash := models.NewDashboardFromJson(dashJSON)
|
||||
extractor := NewDashAlertExtractor(dash, 1, nil)
|
||||
|
||||
_, err = extractor.GetAlerts(context.Background())
|
||||
_, err = extractor.GetAlerts(context.Background(), DashAlertInfo{
|
||||
User: nil,
|
||||
Dash: models.NewDashboardFromJson(dashJSON),
|
||||
OrgID: 1,
|
||||
})
|
||||
|
||||
require.NotNil(t, err)
|
||||
})
|
||||
@ -154,10 +148,12 @@ func TestAlertRuleExtraction(t *testing.T) {
|
||||
require.Nil(t, err)
|
||||
dashJSON, err := simplejson.NewJson(panelWithQuery)
|
||||
require.Nil(t, err)
|
||||
dash := models.NewDashboardFromJson(dashJSON)
|
||||
extractor := NewDashAlertExtractor(dash, 1, nil)
|
||||
|
||||
_, err = extractor.GetAlerts(WithUAEnabled(context.Background(), true))
|
||||
_, err = extractor.GetAlerts(WithUAEnabled(context.Background(), true), DashAlertInfo{
|
||||
User: nil,
|
||||
Dash: models.NewDashboardFromJson(dashJSON),
|
||||
OrgID: 1,
|
||||
})
|
||||
require.Equal(t, "alert validation error: Alert on PanelId: 2 refers to query(B) that cannot be found. Legacy alerting queries are not able to be removed at this time in order to preserve the ability to rollback to previous versions of Grafana", err.Error())
|
||||
})
|
||||
|
||||
@ -167,10 +163,13 @@ func TestAlertRuleExtraction(t *testing.T) {
|
||||
|
||||
dashJSON, err := simplejson.NewJson(panelWithoutSpecifiedDatasource)
|
||||
require.Nil(t, err)
|
||||
dash := models.NewDashboardFromJson(dashJSON)
|
||||
extractor := NewDashAlertExtractor(dash, 1, nil)
|
||||
|
||||
alerts, err := extractor.GetAlerts(context.Background())
|
||||
dsService.ExpectedDatasource = &models.DataSource{Id: 12}
|
||||
alerts, err := extractor.GetAlerts(context.Background(), DashAlertInfo{
|
||||
User: nil,
|
||||
Dash: models.NewDashboardFromJson(dashJSON),
|
||||
OrgID: 1,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
condition := simplejson.NewFromAny(alerts[0].Settings.Get("conditions").MustArray()[0])
|
||||
@ -184,10 +183,12 @@ func TestAlertRuleExtraction(t *testing.T) {
|
||||
|
||||
dashJSON, err := simplejson.NewJson(json)
|
||||
require.Nil(t, err)
|
||||
dash := models.NewDashboardFromJson(dashJSON)
|
||||
extractor := NewDashAlertExtractor(dash, 1, nil)
|
||||
|
||||
alerts, err := extractor.GetAlerts(context.Background())
|
||||
alerts, err := extractor.GetAlerts(context.Background(), DashAlertInfo{
|
||||
User: nil,
|
||||
Dash: models.NewDashboardFromJson(dashJSON),
|
||||
OrgID: 1,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Len(t, alerts, 2)
|
||||
@ -209,10 +210,12 @@ func TestAlertRuleExtraction(t *testing.T) {
|
||||
|
||||
dashJSON, err := simplejson.NewJson(json)
|
||||
require.Nil(t, err)
|
||||
dash := models.NewDashboardFromJson(dashJSON)
|
||||
extractor := NewDashAlertExtractor(dash, 1, nil)
|
||||
|
||||
alerts, err := extractor.GetAlerts(context.Background())
|
||||
alerts, err := extractor.GetAlerts(context.Background(), DashAlertInfo{
|
||||
User: nil,
|
||||
Dash: models.NewDashboardFromJson(dashJSON),
|
||||
OrgID: 1,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Len(t, alerts, 1)
|
||||
@ -235,9 +238,12 @@ func TestAlertRuleExtraction(t *testing.T) {
|
||||
require.Nil(t, err)
|
||||
|
||||
dash := models.NewDashboardFromJson(dashJSON)
|
||||
extractor := NewDashAlertExtractor(dash, 1, nil)
|
||||
|
||||
alerts, err := extractor.GetAlerts(context.Background())
|
||||
alerts, err := extractor.GetAlerts(context.Background(), DashAlertInfo{
|
||||
User: nil,
|
||||
Dash: dash,
|
||||
OrgID: 1,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Len(t, alerts, 4)
|
||||
@ -249,14 +255,17 @@ func TestAlertRuleExtraction(t *testing.T) {
|
||||
|
||||
dashJSON, err := simplejson.NewJson(json)
|
||||
require.Nil(t, err)
|
||||
dash := models.NewDashboardFromJson(dashJSON)
|
||||
extractor := NewDashAlertExtractor(dash, 1, nil)
|
||||
|
||||
err = extractor.ValidateAlerts(context.Background())
|
||||
dashAlertInfo := DashAlertInfo{
|
||||
User: nil,
|
||||
Dash: models.NewDashboardFromJson(dashJSON),
|
||||
OrgID: 1,
|
||||
}
|
||||
|
||||
err = extractor.ValidateAlerts(context.Background(), dashAlertInfo)
|
||||
require.Nil(t, err)
|
||||
|
||||
_, err = extractor.GetAlerts(context.Background())
|
||||
_, err = extractor.GetAlerts(context.Background(), dashAlertInfo)
|
||||
require.Equal(t, err.Error(), "alert validation error: Panel id is not correct, alertName=Influxdb, panelId=1")
|
||||
})
|
||||
|
||||
@ -266,14 +275,18 @@ func TestAlertRuleExtraction(t *testing.T) {
|
||||
|
||||
dashJSON, err := simplejson.NewJson(json)
|
||||
require.Nil(t, err)
|
||||
dash := models.NewDashboardFromJson(dashJSON)
|
||||
extractor := NewDashAlertExtractor(dash, 1, nil)
|
||||
|
||||
err = extractor.ValidateAlerts(context.Background())
|
||||
dsService.ExpectedDatasource = graphite2Ds
|
||||
dashAlertInfo := DashAlertInfo{
|
||||
User: nil,
|
||||
Dash: models.NewDashboardFromJson(dashJSON),
|
||||
OrgID: 1,
|
||||
}
|
||||
|
||||
err = extractor.ValidateAlerts(context.Background(), dashAlertInfo)
|
||||
require.Nil(t, err)
|
||||
|
||||
alerts, err := extractor.GetAlerts(context.Background())
|
||||
alerts, err := extractor.GetAlerts(context.Background(), dashAlertInfo)
|
||||
require.Nil(t, err)
|
||||
|
||||
condition := simplejson.NewFromAny(alerts[0].Settings.Get("conditions").MustArray()[0])
|
||||
@ -281,3 +294,18 @@ func TestAlertRuleExtraction(t *testing.T) {
|
||||
require.EqualValues(t, 15, query.Get("datasourceId").MustInt64())
|
||||
})
|
||||
}
|
||||
|
||||
type fakeDatasourceService struct {
|
||||
ExpectedDatasource *models.DataSource
|
||||
datasources.DataSourceService
|
||||
}
|
||||
|
||||
func (f *fakeDatasourceService) GetDefaultDataSource(ctx context.Context, query *models.GetDefaultDataSourceQuery) error {
|
||||
query.Result = f.ExpectedDatasource
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakeDatasourceService) GetDataSource(ctx context.Context, query *models.GetDataSourceQuery) error {
|
||||
query.Result = f.ExpectedDatasource
|
||||
return nil
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/null"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
// Job holds state about when the alert rule should be evaluated.
|
||||
@ -42,3 +43,9 @@ type EvalMatch struct {
|
||||
Metric string `json:"metric"`
|
||||
Tags map[string]string `json:"tags"`
|
||||
}
|
||||
|
||||
type DashAlertInfo struct {
|
||||
User *models.SignedInUser
|
||||
Dash *models.Dashboard
|
||||
OrgID int64
|
||||
}
|
||||
|
@ -11,9 +11,12 @@ import (
|
||||
// AlertTest makes a test alert.
|
||||
func (e *AlertEngine) AlertTest(orgID int64, dashboard *simplejson.Json, panelID int64, user *models.SignedInUser) (*EvalContext, error) {
|
||||
dash := models.NewDashboardFromJson(dashboard)
|
||||
|
||||
extractor := NewDashAlertExtractor(dash, orgID, user)
|
||||
alerts, err := extractor.GetAlerts(context.Background())
|
||||
dashInfo := DashAlertInfo{
|
||||
User: user,
|
||||
Dash: dash,
|
||||
OrgID: orgID,
|
||||
}
|
||||
alerts, err := e.dashAlertExtractor.GetAlerts(context.Background(), dashInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -19,14 +19,16 @@ import (
|
||||
)
|
||||
|
||||
type DashboardServiceImpl struct {
|
||||
dashboardStore m.Store
|
||||
log log.Logger
|
||||
dashboardStore m.Store
|
||||
dashAlertExtractor alerting.DashAlertExtractor
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func ProvideDashboardService(store m.Store) *DashboardServiceImpl {
|
||||
func ProvideDashboardService(store m.Store, dashAlertExtractor alerting.DashAlertExtractor) *DashboardServiceImpl {
|
||||
return &DashboardServiceImpl{
|
||||
dashboardStore: store,
|
||||
log: log.New("dashboard-service"),
|
||||
dashboardStore: store,
|
||||
dashAlertExtractor: dashAlertExtractor,
|
||||
log: log.New("dashboard-service"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,7 +76,8 @@ func (dr *DashboardServiceImpl) BuildSaveDashboardCommand(ctx context.Context, d
|
||||
}
|
||||
|
||||
if shouldValidateAlerts {
|
||||
if err := validateAlerts(ctx, dash, dto.User); err != nil {
|
||||
dashAlertInfo := alerting.DashAlertInfo{Dash: dash, User: dto.User, OrgID: dash.OrgId}
|
||||
if err := dr.dashAlertExtractor.ValidateAlerts(ctx, dashAlertInfo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@ -139,11 +142,6 @@ func (dr *DashboardServiceImpl) DeleteOrphanedProvisionedDashboards(ctx context.
|
||||
return dr.dashboardStore.DeleteOrphanedProvisionedDashboards(ctx, cmd)
|
||||
}
|
||||
|
||||
var validateAlerts = func(ctx context.Context, dash *models.Dashboard, user *models.SignedInUser) error {
|
||||
extractor := alerting.NewDashAlertExtractor(dash, dash.OrgId, user)
|
||||
return extractor.ValidateAlerts(ctx)
|
||||
}
|
||||
|
||||
func validateDashboardRefreshInterval(dash *models.Dashboard) error {
|
||||
if setting.MinRefreshInterval == "" {
|
||||
return nil
|
||||
@ -171,19 +169,6 @@ func validateDashboardRefreshInterval(dash *models.Dashboard) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateAlerting updates alerting.
|
||||
//
|
||||
// Stubbable by tests.
|
||||
var UpdateAlerting = func(ctx context.Context, store m.Store, orgID int64, dashboard *models.Dashboard, user *models.SignedInUser) error {
|
||||
extractor := alerting.NewDashAlertExtractor(dashboard, orgID, user)
|
||||
alerts, err := extractor.GetAlerts(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return store.SaveAlerts(ctx, dashboard.Id, alerts)
|
||||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) SaveProvisionedDashboard(ctx context.Context, dto *m.SaveDashboardDTO,
|
||||
provisioning *models.DashboardProvisioning) (*models.Dashboard, error) {
|
||||
if err := validateDashboardRefreshInterval(dto.Dashboard); err != nil {
|
||||
@ -210,7 +195,19 @@ func (dr *DashboardServiceImpl) SaveProvisionedDashboard(ctx context.Context, dt
|
||||
}
|
||||
|
||||
// alerts
|
||||
if err := UpdateAlerting(ctx, dr.dashboardStore, dto.OrgId, dash, dto.User); err != nil {
|
||||
dashAlertInfo := alerting.DashAlertInfo{
|
||||
User: dto.User,
|
||||
Dash: dash,
|
||||
OrgID: dto.OrgId,
|
||||
}
|
||||
|
||||
alerts, err := dr.dashAlertExtractor.GetAlerts(ctx, dashAlertInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = dr.dashboardStore.SaveAlerts(ctx, dash.Id, alerts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -232,7 +229,19 @@ func (dr *DashboardServiceImpl) SaveFolderForProvisionedDashboards(ctx context.C
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := UpdateAlerting(ctx, dr.dashboardStore, dto.OrgId, dash, dto.User); err != nil {
|
||||
dashAlertInfo := alerting.DashAlertInfo{
|
||||
User: dto.User,
|
||||
Dash: dash,
|
||||
OrgID: dto.OrgId,
|
||||
}
|
||||
|
||||
alerts, err := dr.dashAlertExtractor.GetAlerts(ctx, dashAlertInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = dr.dashboardStore.SaveAlerts(ctx, dash.Id, alerts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -258,7 +267,19 @@ func (dr *DashboardServiceImpl) SaveDashboard(ctx context.Context, dto *m.SaveDa
|
||||
return nil, fmt.Errorf("saving dashboard failed: %w", err)
|
||||
}
|
||||
|
||||
if err := UpdateAlerting(ctx, dr.dashboardStore, dto.OrgId, dash, dto.User); err != nil {
|
||||
dashAlertInfo := alerting.DashAlertInfo{
|
||||
User: dto.User,
|
||||
Dash: dash,
|
||||
OrgID: dto.OrgId,
|
||||
}
|
||||
|
||||
alerts, err := dr.dashAlertExtractor.GetAlerts(ctx, dashAlertInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = dr.dashboardStore.SaveAlerts(ctx, dash.Id, alerts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/grafana/grafana/pkg/services/alerting"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
@ -21,14 +22,6 @@ const testOrgID int64 = 1
|
||||
|
||||
func TestIntegratedDashboardService(t *testing.T) {
|
||||
t.Run("Given saved folders and dashboards in organization A", func(t *testing.T) {
|
||||
origUpdateAlerting := UpdateAlerting
|
||||
t.Cleanup(func() {
|
||||
UpdateAlerting = origUpdateAlerting
|
||||
})
|
||||
UpdateAlerting = func(ctx context.Context, store dashbboardservice.Store, orgID int64, dashboard *models.Dashboard, user *models.SignedInUser) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Basic validation tests
|
||||
|
||||
permissionScenario(t, "When saving a dashboard with non-existing id", true,
|
||||
@ -861,7 +854,7 @@ func callSaveWithResult(t *testing.T, cmd models.SaveDashboardCommand, sqlStore
|
||||
|
||||
dto := toSaveDashboardDto(cmd)
|
||||
dashboardStore := database.ProvideDashboardStore(sqlStore)
|
||||
res, err := ProvideDashboardService(dashboardStore).SaveDashboard(context.Background(), &dto, false)
|
||||
res, err := ProvideDashboardService(dashboardStore, &dummyDashAlertExtractor{}).SaveDashboard(context.Background(), &dto, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
return res
|
||||
@ -870,7 +863,7 @@ func callSaveWithResult(t *testing.T, cmd models.SaveDashboardCommand, sqlStore
|
||||
func callSaveWithError(cmd models.SaveDashboardCommand, sqlStore *sqlstore.SQLStore) error {
|
||||
dto := toSaveDashboardDto(cmd)
|
||||
dashboardStore := database.ProvideDashboardStore(sqlStore)
|
||||
_, err := ProvideDashboardService(dashboardStore).SaveDashboard(context.Background(), &dto, false)
|
||||
_, err := ProvideDashboardService(dashboardStore, &dummyDashAlertExtractor{}).SaveDashboard(context.Background(), &dto, false)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -897,7 +890,7 @@ func saveTestDashboard(t *testing.T, title string, orgID, folderID int64, sqlSto
|
||||
}
|
||||
|
||||
dashboardStore := database.ProvideDashboardStore(sqlStore)
|
||||
res, err := ProvideDashboardService(dashboardStore).SaveDashboard(context.Background(), &dto, false)
|
||||
res, err := ProvideDashboardService(dashboardStore, &dummyDashAlertExtractor{}).SaveDashboard(context.Background(), &dto, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
return res
|
||||
@ -925,7 +918,7 @@ func saveTestFolder(t *testing.T, title string, orgID int64, sqlStore *sqlstore.
|
||||
}
|
||||
|
||||
dashboardStore := database.ProvideDashboardStore(sqlStore)
|
||||
res, err := ProvideDashboardService(dashboardStore).SaveDashboard(context.Background(), &dto, false)
|
||||
res, err := ProvideDashboardService(dashboardStore, &dummyDashAlertExtractor{}).SaveDashboard(context.Background(), &dto, false)
|
||||
require.NoError(t, err)
|
||||
|
||||
return res
|
||||
@ -942,3 +935,14 @@ func toSaveDashboardDto(cmd models.SaveDashboardCommand) dashbboardservice.SaveD
|
||||
Overwrite: cmd.Overwrite,
|
||||
}
|
||||
}
|
||||
|
||||
type dummyDashAlertExtractor struct {
|
||||
}
|
||||
|
||||
func (d *dummyDashAlertExtractor) GetAlerts(ctx context.Context, dashAlertInfo alerting.DashAlertInfo) ([]*models.Alert, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (d *dummyDashAlertExtractor) ValidateAlerts(ctx context.Context, dashAlertInfo alerting.DashAlertInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
@ -27,8 +27,9 @@ func TestDashboardService(t *testing.T) {
|
||||
fakeStore := database.FakeDashboardStore{}
|
||||
defer fakeStore.AssertExpectations(t)
|
||||
service := &DashboardServiceImpl{
|
||||
log: log.New("test.logger"),
|
||||
dashboardStore: &fakeStore,
|
||||
log: log.New("test.logger"),
|
||||
dashboardStore: &fakeStore,
|
||||
dashAlertExtractor: &dummyDashAlertExtractor{},
|
||||
}
|
||||
|
||||
origNewDashboardGuardian := guardian.New
|
||||
|
@ -25,7 +25,7 @@ func TestFolderService(t *testing.T) {
|
||||
store := &database.FakeDashboardStore{}
|
||||
defer store.AssertExpectations(t)
|
||||
service := ProvideFolderService(
|
||||
&dashboards.FakeDashboardService{DashboardService: ProvideDashboardService(store)},
|
||||
&dashboards.FakeDashboardService{DashboardService: ProvideDashboardService(store, nil)},
|
||||
store,
|
||||
nil,
|
||||
)
|
||||
|
@ -1,4 +1,4 @@
|
||||
package api
|
||||
package permissions
|
||||
|
||||
import (
|
||||
"context"
|
@ -1,4 +1,4 @@
|
||||
package api
|
||||
package permissions
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -7,14 +7,14 @@ import (
|
||||
)
|
||||
|
||||
type mockDatasourcePermissionService struct {
|
||||
dsResult []*models.DataSource
|
||||
DsResult []*models.DataSource
|
||||
}
|
||||
|
||||
func (m *mockDatasourcePermissionService) FilterDatasourcesBasedOnQueryPermissions(ctx context.Context, cmd *models.DatasourcesPermissionFilterQuery) error {
|
||||
cmd.Result = m.dsResult
|
||||
cmd.Result = m.DsResult
|
||||
return nil
|
||||
}
|
||||
|
||||
func newMockDatasourcePermissionService() *mockDatasourcePermissionService {
|
||||
func NewMockDatasourcePermissionService() *mockDatasourcePermissionService {
|
||||
return &mockDatasourcePermissionService{}
|
||||
}
|
@ -13,6 +13,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/alerting"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards/database"
|
||||
dashboardservice "github.com/grafana/grafana/pkg/services/dashboards/manager"
|
||||
@ -195,7 +196,8 @@ func createDashboard(t *testing.T, sqlStore *sqlstore.SQLStore, user models.Sign
|
||||
}
|
||||
|
||||
dashboardStore := database.ProvideDashboardStore(sqlStore)
|
||||
dashboard, err := dashboardservice.ProvideDashboardService(dashboardStore).SaveDashboard(context.Background(), dashItem, true)
|
||||
dashAlertExtractor := alerting.ProvideDashAlertExtractorService(nil, nil)
|
||||
dashboard, err := dashboardservice.ProvideDashboardService(dashboardStore, dashAlertExtractor).SaveDashboard(context.Background(), dashItem, true)
|
||||
require.NoError(t, err)
|
||||
|
||||
return dashboard
|
||||
@ -206,7 +208,7 @@ func createFolderWithACL(t *testing.T, sqlStore *sqlstore.SQLStore, title string
|
||||
t.Helper()
|
||||
|
||||
dashboardStore := database.ProvideDashboardStore(sqlStore)
|
||||
d := dashboardservice.ProvideDashboardService(dashboardStore)
|
||||
d := dashboardservice.ProvideDashboardService(dashboardStore, nil)
|
||||
s := dashboardservice.ProvideFolderService(d, dashboardStore, nil)
|
||||
t.Logf("Creating folder with title and UID %q", title)
|
||||
folder, err := s.CreateFolder(context.Background(), &user, user.OrgId, title, title)
|
||||
@ -292,7 +294,7 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
|
||||
role := models.ROLE_ADMIN
|
||||
sqlStore := sqlstore.InitTestDB(t)
|
||||
dashboardStore := database.ProvideDashboardStore(sqlStore)
|
||||
dashboardService := dashboardservice.ProvideDashboardService(dashboardStore)
|
||||
dashboardService := dashboardservice.ProvideDashboardService(dashboardStore, &alerting.DashAlertExtractorService{})
|
||||
service := LibraryElementService{
|
||||
Cfg: setting.NewCfg(),
|
||||
SQLStore: sqlStore,
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/alerting"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards/database"
|
||||
dashboardservice "github.com/grafana/grafana/pkg/services/dashboards/manager"
|
||||
@ -1416,7 +1417,8 @@ func createDashboard(t *testing.T, sqlStore *sqlstore.SQLStore, user *models.Sig
|
||||
}
|
||||
|
||||
dashboadStore := database.ProvideDashboardStore(sqlStore)
|
||||
dashboard, err := dashboardservice.ProvideDashboardService(dashboadStore).SaveDashboard(context.Background(), dashItem, true)
|
||||
dashAlertService := alerting.ProvideDashAlertExtractorService(nil, nil)
|
||||
dashboard, err := dashboardservice.ProvideDashboardService(dashboadStore, dashAlertService).SaveDashboard(context.Background(), dashItem, true)
|
||||
require.NoError(t, err)
|
||||
|
||||
return dashboard
|
||||
@ -1427,7 +1429,7 @@ func createFolderWithACL(t *testing.T, sqlStore *sqlstore.SQLStore, title string
|
||||
t.Helper()
|
||||
|
||||
dashboardStore := database.ProvideDashboardStore(sqlStore)
|
||||
d := dashboardservice.ProvideDashboardService(dashboardStore)
|
||||
d := dashboardservice.ProvideDashboardService(dashboardStore, nil)
|
||||
s := dashboardservice.ProvideFolderService(d, dashboardStore, nil)
|
||||
t.Logf("Creating folder with title and UID %q", title)
|
||||
folder, err := s.CreateFolder(context.Background(), user, user.OrgId, title, title)
|
||||
@ -1516,7 +1518,7 @@ func testScenario(t *testing.T, desc string, fn func(t *testing.T, sc scenarioCo
|
||||
role := models.ROLE_ADMIN
|
||||
sqlStore := sqlstore.InitTestDB(t)
|
||||
dashboardStore := database.ProvideDashboardStore(sqlStore)
|
||||
folderService := dashboardservice.ProvideFolderService(dashboardservice.ProvideDashboardService(dashboardStore), dashboardStore, nil)
|
||||
folderService := dashboardservice.ProvideFolderService(dashboardservice.ProvideDashboardService(dashboardStore, &alerting.DashAlertExtractorService{}), dashboardStore, nil)
|
||||
|
||||
elementService := libraryelements.ProvideService(cfg, sqlStore, routing.NewRouteRegister(), folderService)
|
||||
service := LibraryPanelService{
|
||||
|
@ -41,7 +41,7 @@ func SetupTestEnv(t *testing.T, baseInterval time.Duration) (*ngalert.AlertNG, *
|
||||
sqlStore := sqlstore.InitTestDB(t)
|
||||
secretsService := secretsManager.SetupTestService(t, database.ProvideSecretsStore(sqlStore))
|
||||
dashboardStore := databasestore.ProvideDashboardStore(sqlStore)
|
||||
folderService := dashboardservice.ProvideFolderService(dashboardservice.ProvideDashboardService(dashboardStore), dashboardStore, nil)
|
||||
folderService := dashboardservice.ProvideFolderService(dashboardservice.ProvideDashboardService(dashboardStore, nil), dashboardStore, nil)
|
||||
ng, err := ngalert.ProvideService(
|
||||
cfg, nil, routing.NewRouteRegister(), sqlStore,
|
||||
nil, nil, nil, nil, secretsService, nil, m, folderService,
|
||||
|
@ -503,6 +503,7 @@ func (m *SQLStoreMock) GetDataSourcesByType(ctx context.Context, query *models.G
|
||||
}
|
||||
|
||||
func (m *SQLStoreMock) GetDefaultDataSource(ctx context.Context, query *models.GetDefaultDataSourceQuery) error {
|
||||
query.Result = m.ExpectedDatasource
|
||||
return m.ExpectedError
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user